diff --git a/TMessagesProj/jni/jni.c b/TMessagesProj/jni/jni.c index 9f03d9bea..452ac5b5f 100644 --- a/TMessagesProj/jni/jni.c +++ b/TMessagesProj/jni/jni.c @@ -7,7 +7,7 @@ #include "aes.h" #include "log.h" -JNIEXPORT jbyteArray Java_org_telegram_messenger_Utilities_aesIgeEncryption(JNIEnv *env, jclass class, jbyteArray _what, jbyteArray _key, jbyteArray _iv, jboolean encrypt, jboolean changeIv) { +JNIEXPORT jbyteArray Java_org_telegram_messenger_Utilities_aesIgeEncryption(JNIEnv *env, jclass class, jbyteArray _what, jbyteArray _key, jbyteArray _iv, jboolean encrypt, jboolean changeIv, jint l) { unsigned char *what = (unsigned char *)(*env)->GetByteArrayElements(env, _what, NULL); unsigned char *key = (unsigned char *)(*env)->GetByteArrayElements(env, _key, NULL); unsigned char *__iv = (unsigned char *)(*env)->GetByteArrayElements(env, _iv, NULL); @@ -20,7 +20,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_Utilities_aesIgeEncryption(JNIE iv = __iv; } - int len = (*env)->GetArrayLength(env, _what); + int len = l == 0 ? (*env)->GetArrayLength(env, _what) : l; AES_KEY akey; if (!encrypt) { AES_set_decrypt_key(key, (*env)->GetArrayLength(env, _key) * 8, &akey); @@ -40,6 +40,36 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_Utilities_aesIgeEncryption(JNIE return _what; } +JNIEXPORT void Java_org_telegram_messenger_Utilities_aesIgeEncryption2(JNIEnv *env, jclass class, jobject _what, jbyteArray _key, jbyteArray _iv, jboolean encrypt, jboolean changeIv, jint l) { + jbyte *what = (*env)->GetDirectBufferAddress(env, _what); + unsigned char *key = (unsigned char *)(*env)->GetByteArrayElements(env, _key, NULL); + unsigned char *__iv = (unsigned char *)(*env)->GetByteArrayElements(env, _iv, NULL); + unsigned char *iv = 0; + + if (!changeIv) { + iv = (unsigned char *)malloc((*env)->GetArrayLength(env, _iv)); + memcpy(iv, __iv, (*env)->GetArrayLength(env, _iv)); + } else { + iv = __iv; + } + + AES_KEY akey; + if (!encrypt) { + AES_set_decrypt_key(key, (*env)->GetArrayLength(env, _key) * 8, &akey); + AES_ige_encrypt(what, what, l, &akey, iv, AES_DECRYPT); + } else { + AES_set_encrypt_key(key, (*env)->GetArrayLength(env, _key) * 8, &akey); + AES_ige_encrypt(what, what, l, &akey, iv, AES_ENCRYPT); + } + (*env)->ReleaseByteArrayElements(env, _key, key, JNI_ABORT); + if (!changeIv) { + (*env)->ReleaseByteArrayElements(env, _iv, __iv, JNI_ABORT); + free(iv); + } else { + (*env)->ReleaseByteArrayElements(env, _iv, __iv, 0); + } +} + uint64_t gcd(uint64_t a, uint64_t b){ while(a != 0 && b != 0) { while((b & 1) == 0) b >>= 1; diff --git a/TMessagesProj/jni/sqlite3.c b/TMessagesProj/jni/sqlite3.c index 8da37bff5..d0a1ec230 100644 --- a/TMessagesProj/jni/sqlite3.c +++ b/TMessagesProj/jni/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.8.1. By combining all the individual C code files into this +** version 3.8.3.1. 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 @@ -21,7 +21,6 @@ #define SQLITE_AMALGAMATION 1 #define SQLITE_ENABLE_FTS3 1 #define SQLITE_ENABLE_FTS3_PARENTHESIS 1 -//#define SQLITE_TEMP_STORE 3 #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static @@ -139,9 +138,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.1" -#define SQLITE_VERSION_NUMBER 3008001 -#define SQLITE_SOURCE_ID "2013-10-17 12:57:35 c78be6d786c19073b3a6730dfe3fb1be54f5657a" +#define SQLITE_VERSION "3.8.3.1" +#define SQLITE_VERSION_NUMBER 3008003 +#define SQLITE_SOURCE_ID "2014-02-11 14:52:19 ea3317a4803d71d88183b29f1d3086f46d68a00e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -402,7 +401,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -963,6 +993,10 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_BUSYHANDLER 15 #define SQLITE_FCNTL_TEMPFILENAME 16 #define SQLITE_FCNTL_MMAP_SIZE 18 +#define SQLITE_FCNTL_TRACE 19 +#define SQLITE_FCNTL_HAS_MOVED 20 +#define SQLITE_FCNTL_SYNC 21 +#define SQLITE_FCNTL_COMMIT_PHASETWO 22 /* ** CAPI3REF: Mutex Handle @@ -1407,7 +1441,7 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); ** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, ** that causes the corresponding memory allocation to fail. ** -** The xInit method initializes the memory allocator. (For example, +** The xInit method initializes the memory allocator. For example, ** it might allocate any require mutexes or initialize internal data ** structures. The xShutdown method is invoked (indirectly) by ** [sqlite3_shutdown()] and should deallocate any resources acquired @@ -1709,6 +1743,13 @@ struct sqlite3_mem_methods { ** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^ ** ^If either argument to this option is negative, then that argument is ** changed to its compile-time default. +** +** [[SQLITE_CONFIG_WIN32_HEAPSIZE]] +**
SQLITE_CONFIG_WIN32_HEAPSIZE +**
^This option is only available if SQLite is compiled for Windows +** with the [SQLITE_WIN32_MALLOC] pre-processor macro defined. +** SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value +** that specifies the maximum size of the created heap. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1733,6 +1774,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ +#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ /* ** CAPI3REF: Database Connection Configuration Options @@ -1809,19 +1851,21 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid ** -** ^Each entry in an SQLite table has a unique 64-bit signed +** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) +** has a unique 64-bit signed ** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those ** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** -** ^This routine returns the [rowid] of the most recent -** successful [INSERT] into the database from the [database connection] -** in the first argument. ^As of SQLite version 3.7.7, this routines -** records the last insert rowid of both ordinary tables and [virtual tables]. -** ^If no successful [INSERT]s -** have ever occurred on that database connection, zero is returned. +** ^The sqlite3_last_insert_rowid(D) interface returns the [rowid] of the +** most recent successful [INSERT] into a rowid table or [virtual table] +** on database connection D. +** ^Inserts into [WITHOUT ROWID] tables are not recorded. +** ^If no successful [INSERT]s into rowid tables +** have ever occurred on the database connection D, +** then sqlite3_last_insert_rowid(D) returns zero. ** ** ^(If an [INSERT] occurs within a trigger or within a [virtual table] ** method, then this routine will return the [rowid] of the inserted @@ -2387,11 +2431,13 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** applications to access the same PRNG for other purposes. ** ** ^A call to this routine stores N bytes of randomness into buffer P. +** ^If N is less than one, then P can be a NULL pointer. ** -** ^The first time this routine is invoked (either internally or by -** the application) the PRNG is seeded using randomness obtained -** from the xRandomness method of the default [sqlite3_vfs] object. -** ^On all subsequent invocations, the pseudo-randomness is generated +** ^If this routine has not been previously called or if the previous +** call had N less than one, then the PRNG is seeded using randomness +** obtained from the xRandomness method of the default [sqlite3_vfs] object. +** ^If the previous call to this routine had an N of 1 or more then +** the pseudo-randomness is generated ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ @@ -2551,6 +2597,7 @@ SQLITE_API int sqlite3_set_authorizer( #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ +#define SQLITE_RECURSIVE 33 /* NULL NULL */ /* ** CAPI3REF: Tracing And Profiling Functions @@ -3131,7 +3178,6 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. -** the ** ** */ @@ -3793,19 +3839,19 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** ** NULL INTEGER Result is 0 ** NULL FLOAT Result is 0.0 -** NULL TEXT Result is NULL pointer -** NULL BLOB Result is NULL pointer +** NULL TEXT Result is a NULL pointer +** NULL BLOB Result is a NULL pointer ** INTEGER FLOAT Convert from integer to float ** INTEGER TEXT ASCII rendering of the integer ** INTEGER BLOB Same as INTEGER->TEXT -** FLOAT INTEGER Convert from float to integer +** FLOAT INTEGER [CAST] to INTEGER ** FLOAT TEXT ASCII rendering of the float -** FLOAT BLOB Same as FLOAT->TEXT -** TEXT INTEGER Use atoi() -** TEXT FLOAT Use atof() +** FLOAT BLOB [CAST] to BLOB +** TEXT INTEGER [CAST] to INTEGER +** TEXT FLOAT [CAST] to REAL ** TEXT BLOB No change -** BLOB INTEGER Convert to TEXT then use atoi() -** BLOB FLOAT Convert to TEXT then use atof() +** BLOB INTEGER [CAST] to INTEGER +** BLOB FLOAT [CAST] to REAL ** BLOB TEXT Add a zero terminator if needed ** ** )^ @@ -3861,7 +3907,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned -** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into +** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** ** ^(If a memory allocation error occurs during the evaluation of any @@ -3970,15 +4016,24 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** ** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for -** its parameters. Every SQL function implementation must be able to work -** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be -** more efficient with one encoding than another. ^An application may -** invoke sqlite3_create_function() or sqlite3_create_function16() multiple -** times with the same function but with different values of eTextRep. +** its parameters. The application should set this parameter to +** [SQLITE_UTF16LE] if the function implementation invokes +** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the +** implementation invokes [sqlite3_value_text16be()] on an input, or +** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8] +** otherwise. ^The same SQL function may be registered multiple times using +** different preferred text encodings, with different implementations for +** each encoding. ** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. -** If there is only a single implementation which does not care what text -** encoding is used, then the fourth argument should be [SQLITE_ANY]. +** +** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC] +** to signal that the function will always return the same result given +** the same inputs within a single SQL statement. Most SQL functions are +** deterministic. The built-in [random()] SQL function is an example of a +** function that is not deterministic. The SQLite query planner is able to +** perform additional optimizations on deterministic functions, so use +** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ @@ -4064,9 +4119,19 @@ SQLITE_API int sqlite3_create_function_v2( #define SQLITE_UTF16LE 2 #define SQLITE_UTF16BE 3 #define SQLITE_UTF16 4 /* Use native byte order */ -#define SQLITE_ANY 5 /* sqlite3_create_function only */ +#define SQLITE_ANY 5 /* Deprecated */ #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ +/* +** CAPI3REF: Function Flags +** +** These constants may be ORed together with the +** [SQLITE_UTF8 | preferred text encoding] as the fourth argument +** to [sqlite3_create_function()], [sqlite3_create_function16()], or +** [sqlite3_create_function_v2()]. +*/ +#define SQLITE_DETERMINISTIC 0x800 + /* ** CAPI3REF: Deprecated Functions ** DEPRECATED @@ -4838,12 +4903,13 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument -** to be invoked whenever a row is updated, inserted or deleted. +** to be invoked whenever a row is updated, inserted or deleted in +** a rowid table. ** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** ** ^The second argument is a pointer to the function to invoke when a -** row is updated, inserted or deleted. +** row is updated, inserted or deleted in a rowid table. ** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], @@ -4856,6 +4922,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ** ^(The update hook is not invoked when internal system tables are ** modified (i.e. sqlite_master and sqlite_sequence).)^ +** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. ** ** ^In the current implementation, the update hook ** is not invoked when duplication rows are deleted because of an @@ -4937,8 +5004,8 @@ SQLITE_API int sqlite3_release_memory(int); ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the -** [sqlite3_release_memory()] interface, this interface is effect even -** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** [sqlite3_release_memory()] interface, this interface is in effect even +** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is ** omitted. ** ** See also: [sqlite3_release_memory()] @@ -5313,10 +5380,22 @@ struct sqlite3_module { ** the correct order to satisfy the ORDER BY clause so that no separate ** sorting step is required. ** -** ^The estimatedCost value is an estimate of the cost of doing the -** particular lookup. A full scan of a table with N entries should have -** a cost of N. A binary search of a table of N entries should have a -** cost of approximately log(N). +** ^The estimatedCost value is an estimate of the cost of a particular +** strategy. A cost of N indicates that the cost of the strategy is similar +** to a linear scan of an SQLite table with N rows. A cost of log(N) +** indicates that the expense of the operation is similar to that of a +** binary search on a unique indexed field of an SQLite table with N rows. +** +** ^The estimatedRows value is an estimate of the number of rows that +** will be returned by the strategy. +** +** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info +** structure for SQLite version 3.8.2. If a virtual table extension is +** used with an SQLite version earlier than 3.8.2, the results of attempting +** to read or write the estimatedRows field are undefined (but are likely +** to included crashing the application). The estimatedRows field should +** therefore only be used if [sqlite3_libversion_number()] returns a +** value greater than or equal to 3008002. */ struct sqlite3_index_info { /* Inputs */ @@ -5341,7 +5420,9 @@ struct sqlite3_index_info { char *idxStr; /* String, possibly obtained from sqlite3_malloc */ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ int orderByConsumed; /* True if output is already ordered */ - double estimatedCost; /* Estimated cost of using this index */ + double estimatedCost; /* Estimated cost of using this index */ + /* Fields below are only available in SQLite 3.8.2 and later */ + sqlite3_int64 estimatedRows; /* Estimated number of rows returned */ }; /* @@ -5545,6 +5626,9 @@ typedef struct sqlite3_blob sqlite3_blob; ** interface. Use the [UPDATE] SQL command to change the size of a ** blob. ** +** ^The [sqlite3_blob_open()] interface will fail for a [WITHOUT ROWID] +** table. Incremental BLOB I/O is not possible on [WITHOUT ROWID] tables. +** ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function can be used, if desired, ** to create an empty, zero-filled blob in which to read or write using @@ -6068,7 +6152,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 -#define SQLITE_TESTCTRL_LAST 19 +#define SQLITE_TESTCTRL_NEVER_CORRUPT 20 +#define SQLITE_TESTCTRL_LAST 20 /* ** CAPI3REF: SQLite Runtime Status @@ -7762,6 +7847,13 @@ struct sqlite3_rtree_geometry { # undef NDEBUG #endif +/* +** Enable SQLITE_ENABLE_EXPLAIN_COMMENTS if SQLITE_DEBUG is turned on. +*/ +#if !defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) && defined(SQLITE_DEBUG) +# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1 +#endif + /* ** The testcase() macro is used to aid in coverage testing. When ** doing coverage testing, the condition inside the argument to @@ -7954,163 +8046,165 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include parse.h in the middle of sqliteInt.h *****************/ /************** Begin file parse.h *******************************************/ -#define TK_SEMI 1 -#define TK_EXPLAIN 2 -#define TK_QUERY 3 -#define TK_PLAN 4 -#define TK_BEGIN 5 -#define TK_TRANSACTION 6 -#define TK_DEFERRED 7 -#define TK_IMMEDIATE 8 -#define TK_EXCLUSIVE 9 -#define TK_COMMIT 10 -#define TK_END 11 -#define TK_ROLLBACK 12 -#define TK_SAVEPOINT 13 -#define TK_RELEASE 14 -#define TK_TO 15 -#define TK_TABLE 16 -#define TK_CREATE 17 -#define TK_IF 18 -#define TK_NOT 19 -#define TK_EXISTS 20 -#define TK_TEMP 21 -#define TK_LP 22 -#define TK_RP 23 -#define TK_AS 24 -#define TK_COMMA 25 -#define TK_ID 26 -#define TK_INDEXED 27 -#define TK_ABORT 28 -#define TK_ACTION 29 -#define TK_AFTER 30 -#define TK_ANALYZE 31 -#define TK_ASC 32 -#define TK_ATTACH 33 -#define TK_BEFORE 34 -#define TK_BY 35 -#define TK_CASCADE 36 -#define TK_CAST 37 -#define TK_COLUMNKW 38 -#define TK_CONFLICT 39 -#define TK_DATABASE 40 -#define TK_DESC 41 -#define TK_DETACH 42 -#define TK_EACH 43 -#define TK_FAIL 44 -#define TK_FOR 45 -#define TK_IGNORE 46 -#define TK_INITIALLY 47 -#define TK_INSTEAD 48 -#define TK_LIKE_KW 49 -#define TK_MATCH 50 -#define TK_NO 51 -#define TK_KEY 52 -#define TK_OF 53 -#define TK_OFFSET 54 -#define TK_PRAGMA 55 -#define TK_RAISE 56 -#define TK_REPLACE 57 -#define TK_RESTRICT 58 -#define TK_ROW 59 -#define TK_TRIGGER 60 -#define TK_VACUUM 61 -#define TK_VIEW 62 -#define TK_VIRTUAL 63 -#define TK_REINDEX 64 -#define TK_RENAME 65 -#define TK_CTIME_KW 66 -#define TK_ANY 67 -#define TK_OR 68 -#define TK_AND 69 -#define TK_IS 70 -#define TK_BETWEEN 71 -#define TK_IN 72 -#define TK_ISNULL 73 -#define TK_NOTNULL 74 -#define TK_NE 75 -#define TK_EQ 76 -#define TK_GT 77 -#define TK_LE 78 -#define TK_LT 79 -#define TK_GE 80 -#define TK_ESCAPE 81 -#define TK_BITAND 82 -#define TK_BITOR 83 -#define TK_LSHIFT 84 -#define TK_RSHIFT 85 -#define TK_PLUS 86 -#define TK_MINUS 87 -#define TK_STAR 88 -#define TK_SLASH 89 -#define TK_REM 90 -#define TK_CONCAT 91 -#define TK_COLLATE 92 -#define TK_BITNOT 93 -#define TK_STRING 94 -#define TK_JOIN_KW 95 -#define TK_CONSTRAINT 96 -#define TK_DEFAULT 97 -#define TK_NULL 98 -#define TK_PRIMARY 99 -#define TK_UNIQUE 100 -#define TK_CHECK 101 -#define TK_REFERENCES 102 -#define TK_AUTOINCR 103 -#define TK_ON 104 -#define TK_INSERT 105 -#define TK_DELETE 106 -#define TK_UPDATE 107 -#define TK_SET 108 -#define TK_DEFERRABLE 109 -#define TK_FOREIGN 110 -#define TK_DROP 111 -#define TK_UNION 112 -#define TK_ALL 113 -#define TK_EXCEPT 114 -#define TK_INTERSECT 115 -#define TK_SELECT 116 -#define TK_DISTINCT 117 -#define TK_DOT 118 -#define TK_FROM 119 -#define TK_JOIN 120 -#define TK_USING 121 -#define TK_ORDER 122 -#define TK_GROUP 123 -#define TK_HAVING 124 -#define TK_LIMIT 125 -#define TK_WHERE 126 -#define TK_INTO 127 -#define TK_VALUES 128 -#define TK_INTEGER 129 -#define TK_FLOAT 130 -#define TK_BLOB 131 -#define TK_REGISTER 132 -#define TK_VARIABLE 133 -#define TK_CASE 134 -#define TK_WHEN 135 -#define TK_THEN 136 -#define TK_ELSE 137 -#define TK_INDEX 138 -#define TK_ALTER 139 -#define TK_ADD 140 -#define TK_TO_TEXT 141 -#define TK_TO_BLOB 142 -#define TK_TO_NUMERIC 143 -#define TK_TO_INT 144 -#define TK_TO_REAL 145 -#define TK_ISNOT 146 -#define TK_END_OF_FILE 147 -#define TK_ILLEGAL 148 -#define TK_SPACE 149 -#define TK_UNCLOSED_STRING 150 -#define TK_FUNCTION 151 -#define TK_COLUMN 152 -#define TK_AGG_FUNCTION 153 -#define TK_AGG_COLUMN 154 -#define TK_CONST_FUNC 155 -#define TK_UMINUS 156 -#define TK_UPLUS 157 +#define TK_SEMI 1 +#define TK_EXPLAIN 2 +#define TK_QUERY 3 +#define TK_PLAN 4 +#define TK_BEGIN 5 +#define TK_TRANSACTION 6 +#define TK_DEFERRED 7 +#define TK_IMMEDIATE 8 +#define TK_EXCLUSIVE 9 +#define TK_COMMIT 10 +#define TK_END 11 +#define TK_ROLLBACK 12 +#define TK_SAVEPOINT 13 +#define TK_RELEASE 14 +#define TK_TO 15 +#define TK_TABLE 16 +#define TK_CREATE 17 +#define TK_IF 18 +#define TK_NOT 19 +#define TK_EXISTS 20 +#define TK_TEMP 21 +#define TK_LP 22 +#define TK_RP 23 +#define TK_AS 24 +#define TK_WITHOUT 25 +#define TK_COMMA 26 +#define TK_ID 27 +#define TK_INDEXED 28 +#define TK_ABORT 29 +#define TK_ACTION 30 +#define TK_AFTER 31 +#define TK_ANALYZE 32 +#define TK_ASC 33 +#define TK_ATTACH 34 +#define TK_BEFORE 35 +#define TK_BY 36 +#define TK_CASCADE 37 +#define TK_CAST 38 +#define TK_COLUMNKW 39 +#define TK_CONFLICT 40 +#define TK_DATABASE 41 +#define TK_DESC 42 +#define TK_DETACH 43 +#define TK_EACH 44 +#define TK_FAIL 45 +#define TK_FOR 46 +#define TK_IGNORE 47 +#define TK_INITIALLY 48 +#define TK_INSTEAD 49 +#define TK_LIKE_KW 50 +#define TK_MATCH 51 +#define TK_NO 52 +#define TK_KEY 53 +#define TK_OF 54 +#define TK_OFFSET 55 +#define TK_PRAGMA 56 +#define TK_RAISE 57 +#define TK_RECURSIVE 58 +#define TK_REPLACE 59 +#define TK_RESTRICT 60 +#define TK_ROW 61 +#define TK_TRIGGER 62 +#define TK_VACUUM 63 +#define TK_VIEW 64 +#define TK_VIRTUAL 65 +#define TK_WITH 66 +#define TK_REINDEX 67 +#define TK_RENAME 68 +#define TK_CTIME_KW 69 +#define TK_ANY 70 +#define TK_OR 71 +#define TK_AND 72 +#define TK_IS 73 +#define TK_BETWEEN 74 +#define TK_IN 75 +#define TK_ISNULL 76 +#define TK_NOTNULL 77 +#define TK_NE 78 +#define TK_EQ 79 +#define TK_GT 80 +#define TK_LE 81 +#define TK_LT 82 +#define TK_GE 83 +#define TK_ESCAPE 84 +#define TK_BITAND 85 +#define TK_BITOR 86 +#define TK_LSHIFT 87 +#define TK_RSHIFT 88 +#define TK_PLUS 89 +#define TK_MINUS 90 +#define TK_STAR 91 +#define TK_SLASH 92 +#define TK_REM 93 +#define TK_CONCAT 94 +#define TK_COLLATE 95 +#define TK_BITNOT 96 +#define TK_STRING 97 +#define TK_JOIN_KW 98 +#define TK_CONSTRAINT 99 +#define TK_DEFAULT 100 +#define TK_NULL 101 +#define TK_PRIMARY 102 +#define TK_UNIQUE 103 +#define TK_CHECK 104 +#define TK_REFERENCES 105 +#define TK_AUTOINCR 106 +#define TK_ON 107 +#define TK_INSERT 108 +#define TK_DELETE 109 +#define TK_UPDATE 110 +#define TK_SET 111 +#define TK_DEFERRABLE 112 +#define TK_FOREIGN 113 +#define TK_DROP 114 +#define TK_UNION 115 +#define TK_ALL 116 +#define TK_EXCEPT 117 +#define TK_INTERSECT 118 +#define TK_SELECT 119 +#define TK_VALUES 120 +#define TK_DISTINCT 121 +#define TK_DOT 122 +#define TK_FROM 123 +#define TK_JOIN 124 +#define TK_USING 125 +#define TK_ORDER 126 +#define TK_GROUP 127 +#define TK_HAVING 128 +#define TK_LIMIT 129 +#define TK_WHERE 130 +#define TK_INTO 131 +#define TK_INTEGER 132 +#define TK_FLOAT 133 +#define TK_BLOB 134 +#define TK_VARIABLE 135 +#define TK_CASE 136 +#define TK_WHEN 137 +#define TK_THEN 138 +#define TK_ELSE 139 +#define TK_INDEX 140 +#define TK_ALTER 141 +#define TK_ADD 142 +#define TK_TO_TEXT 143 +#define TK_TO_BLOB 144 +#define TK_TO_NUMERIC 145 +#define TK_TO_INT 146 +#define TK_TO_REAL 147 +#define TK_ISNOT 148 +#define TK_END_OF_FILE 149 +#define TK_ILLEGAL 150 +#define TK_SPACE 151 +#define TK_UNCLOSED_STRING 152 +#define TK_FUNCTION 153 +#define TK_COLUMN 154 +#define TK_AGG_FUNCTION 155 +#define TK_AGG_COLUMN 156 +#define TK_UMINUS 157 +#define TK_UPLUS 158 +#define TK_REGISTER 159 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -8541,6 +8635,7 @@ typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; typedef struct Parse Parse; +typedef struct PrintfArguments PrintfArguments; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; @@ -8558,6 +8653,7 @@ typedef struct VTable VTable; typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WhereInfo WhereInfo; +typedef struct With With; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and @@ -8746,8 +8842,8 @@ SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); -SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); -SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, u32 *pAmt); +SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); @@ -8887,7 +8983,7 @@ struct VdbeOp { SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ int (*xAdvance)(BtCursor *, int *); } p4; -#ifdef SQLITE_DEBUG +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE @@ -8943,15 +9039,11 @@ typedef struct VdbeOpList VdbeOpList; #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ #define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ -/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure -** is made. That copy is freed when the Vdbe is finalized. But if the -** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still -** gets freed when the Vdbe is finalized so it still should be obtained -** from a single sqliteMalloc(). But no copy is made and the calling -** function should *not* try to free the KeyInfo. -*/ -#define P4_KEYINFO_HANDOFF (-16) -#define P4_KEYINFO_STATIC (-17) +/* Error message codes for OP_Halt */ +#define P5_ConstraintNotNull 1 +#define P5_ConstraintUnique 2 +#define P5_ConstraintCheck 3 +#define P5_ConstraintFK 4 /* ** The Vdbe.aColName array contains 5n Mem structures, where n is the @@ -8988,156 +9080,158 @@ typedef struct VdbeOpList VdbeOpList; /************** Begin file opcodes.h *****************************************/ /* Automatically generated. Do not edit */ /* See the mkopcodeh.awk script for details */ -#define OP_Function 1 -#define OP_Savepoint 2 -#define OP_AutoCommit 3 -#define OP_Transaction 4 -#define OP_SorterNext 5 -#define OP_Prev 6 -#define OP_Next 7 -#define OP_AggStep 8 -#define OP_Checkpoint 9 -#define OP_JournalMode 10 -#define OP_Vacuum 11 -#define OP_VFilter 12 -#define OP_VUpdate 13 -#define OP_Goto 14 -#define OP_Gosub 15 -#define OP_Return 16 -#define OP_Yield 17 -#define OP_HaltIfNull 18 -#define OP_Not 19 /* same as TK_NOT */ -#define OP_Halt 20 -#define OP_Integer 21 -#define OP_Int64 22 -#define OP_String 23 -#define OP_Null 24 -#define OP_Blob 25 -#define OP_Variable 26 -#define OP_Move 27 -#define OP_Copy 28 -#define OP_SCopy 29 -#define OP_ResultRow 30 -#define OP_CollSeq 31 -#define OP_AddImm 32 -#define OP_MustBeInt 33 -#define OP_RealAffinity 34 -#define OP_Permutation 35 -#define OP_Compare 36 -#define OP_Jump 37 -#define OP_Once 38 -#define OP_If 39 -#define OP_IfNot 40 -#define OP_Column 41 -#define OP_Affinity 42 -#define OP_MakeRecord 43 -#define OP_Count 44 -#define OP_ReadCookie 45 -#define OP_SetCookie 46 -#define OP_VerifyCookie 47 -#define OP_OpenRead 48 -#define OP_OpenWrite 49 -#define OP_OpenAutoindex 50 -#define OP_OpenEphemeral 51 -#define OP_SorterOpen 52 -#define OP_OpenPseudo 53 -#define OP_Close 54 -#define OP_SeekLt 55 -#define OP_SeekLe 56 -#define OP_SeekGe 57 -#define OP_SeekGt 58 -#define OP_Seek 59 -#define OP_NotFound 60 -#define OP_Found 61 -#define OP_IsUnique 62 -#define OP_NotExists 63 -#define OP_Sequence 64 -#define OP_NewRowid 65 -#define OP_Insert 66 -#define OP_InsertInt 67 -#define OP_Or 68 /* same as TK_OR */ -#define OP_And 69 /* same as TK_AND */ -#define OP_Delete 70 -#define OP_ResetCount 71 -#define OP_SorterCompare 72 -#define OP_IsNull 73 /* same as TK_ISNULL */ -#define OP_NotNull 74 /* same as TK_NOTNULL */ -#define OP_Ne 75 /* same as TK_NE */ -#define OP_Eq 76 /* same as TK_EQ */ -#define OP_Gt 77 /* same as TK_GT */ -#define OP_Le 78 /* same as TK_LE */ -#define OP_Lt 79 /* same as TK_LT */ -#define OP_Ge 80 /* same as TK_GE */ -#define OP_SorterData 81 -#define OP_BitAnd 82 /* same as TK_BITAND */ -#define OP_BitOr 83 /* same as TK_BITOR */ -#define OP_ShiftLeft 84 /* same as TK_LSHIFT */ -#define OP_ShiftRight 85 /* same as TK_RSHIFT */ -#define OP_Add 86 /* same as TK_PLUS */ -#define OP_Subtract 87 /* same as TK_MINUS */ -#define OP_Multiply 88 /* same as TK_STAR */ -#define OP_Divide 89 /* same as TK_SLASH */ -#define OP_Remainder 90 /* same as TK_REM */ -#define OP_Concat 91 /* same as TK_CONCAT */ -#define OP_RowKey 92 -#define OP_BitNot 93 /* same as TK_BITNOT */ -#define OP_String8 94 /* same as TK_STRING */ -#define OP_RowData 95 -#define OP_Rowid 96 -#define OP_NullRow 97 -#define OP_Last 98 -#define OP_SorterSort 99 -#define OP_Sort 100 -#define OP_Rewind 101 -#define OP_SorterInsert 102 -#define OP_IdxInsert 103 -#define OP_IdxDelete 104 -#define OP_IdxRowid 105 -#define OP_IdxLT 106 -#define OP_IdxGE 107 -#define OP_Destroy 108 -#define OP_Clear 109 -#define OP_CreateIndex 110 -#define OP_CreateTable 111 -#define OP_ParseSchema 112 -#define OP_LoadAnalysis 113 -#define OP_DropTable 114 -#define OP_DropIndex 115 -#define OP_DropTrigger 116 -#define OP_IntegrityCk 117 -#define OP_RowSetAdd 118 -#define OP_RowSetRead 119 -#define OP_RowSetTest 120 -#define OP_Program 121 -#define OP_Param 122 -#define OP_FkCounter 123 -#define OP_FkIfZero 124 -#define OP_MemMax 125 -#define OP_IfPos 126 -#define OP_IfNeg 127 -#define OP_IfZero 128 -#define OP_AggFinal 129 -#define OP_Real 130 /* same as TK_FLOAT */ -#define OP_IncrVacuum 131 -#define OP_Expire 132 -#define OP_TableLock 133 -#define OP_VBegin 134 -#define OP_VCreate 135 -#define OP_VDestroy 136 -#define OP_VOpen 137 -#define OP_VColumn 138 -#define OP_VNext 139 -#define OP_VRename 140 -#define OP_ToText 141 /* same as TK_TO_TEXT */ -#define OP_ToBlob 142 /* same as TK_TO_BLOB */ -#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ -#define OP_ToInt 144 /* same as TK_TO_INT */ -#define OP_ToReal 145 /* same as TK_TO_REAL */ -#define OP_Pagecount 146 -#define OP_MaxPgcnt 147 -#define OP_Trace 148 -#define OP_Noop 149 -#define OP_Explain 150 +#define OP_Function 1 /* synopsis: r[P3]=func(r[P2@P5]) */ +#define OP_Savepoint 2 +#define OP_AutoCommit 3 +#define OP_Transaction 4 +#define OP_SorterNext 5 +#define OP_PrevIfOpen 6 +#define OP_NextIfOpen 7 +#define OP_Prev 8 +#define OP_Next 9 +#define OP_AggStep 10 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_Checkpoint 11 +#define OP_JournalMode 12 +#define OP_Vacuum 13 +#define OP_VFilter 14 /* synopsis: iPlan=r[P3] zPlan='P4' */ +#define OP_VUpdate 15 /* synopsis: data=r[P3@P2] */ +#define OP_Goto 16 +#define OP_Gosub 17 +#define OP_Return 18 +#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ +#define OP_Yield 20 +#define OP_HaltIfNull 21 /* synopsis: if r[P3] null then halt */ +#define OP_Halt 22 +#define OP_Integer 23 /* synopsis: r[P2]=P1 */ +#define OP_Int64 24 /* synopsis: r[P2]=P4 */ +#define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ +#define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ +#define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ +#define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ +#define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ +#define OP_Copy 30 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ +#define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ +#define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ +#define OP_CollSeq 33 +#define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ +#define OP_MustBeInt 35 +#define OP_RealAffinity 36 +#define OP_Permutation 37 +#define OP_Compare 38 +#define OP_Jump 39 +#define OP_Once 40 +#define OP_If 41 +#define OP_IfNot 42 +#define OP_Column 43 /* synopsis: r[P3]=PX */ +#define OP_Affinity 44 /* synopsis: affinity(r[P1@P2]) */ +#define OP_MakeRecord 45 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 46 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 47 +#define OP_SetCookie 48 +#define OP_VerifyCookie 49 +#define OP_OpenRead 50 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenWrite 51 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenAutoindex 52 /* synopsis: nColumn=P2 */ +#define OP_OpenEphemeral 53 /* synopsis: nColumn=P2 */ +#define OP_SorterOpen 54 +#define OP_OpenPseudo 55 /* synopsis: content in r[P2@P3] */ +#define OP_Close 56 +#define OP_SeekLt 57 /* synopsis: key=r[P3@P4] */ +#define OP_SeekLe 58 /* synopsis: key=r[P3@P4] */ +#define OP_SeekGe 59 /* synopsis: key=r[P3@P4] */ +#define OP_SeekGt 60 /* synopsis: key=r[P3@P4] */ +#define OP_Seek 61 /* synopsis: intkey=r[P2] */ +#define OP_NoConflict 62 /* synopsis: key=r[P3@P4] */ +#define OP_NotFound 63 /* synopsis: key=r[P3@P4] */ +#define OP_Found 64 /* synopsis: key=r[P3@P4] */ +#define OP_NotExists 65 /* synopsis: intkey=r[P3] */ +#define OP_Sequence 66 /* synopsis: r[P2]=rowid */ +#define OP_NewRowid 67 /* synopsis: r[P2]=rowid */ +#define OP_Insert 68 /* synopsis: intkey=r[P3] data=r[P2] */ +#define OP_InsertInt 69 /* synopsis: intkey=P3 data=r[P2] */ +#define OP_Delete 70 +#define OP_Or 71 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */ +#define OP_And 72 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */ +#define OP_ResetCount 73 +#define OP_SorterCompare 74 /* synopsis: if key(P1)!=rtrim(r[P3],P4) goto P2 */ +#define OP_SorterData 75 /* synopsis: r[P2]=data */ +#define OP_IsNull 76 /* same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ +#define OP_NotNull 77 /* same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ +#define OP_Ne 78 /* same as TK_NE, synopsis: if r[P1]!=r[P3] goto P2 */ +#define OP_Eq 79 /* same as TK_EQ, synopsis: if r[P1]==r[P3] goto P2 */ +#define OP_Gt 80 /* same as TK_GT, synopsis: if r[P1]>r[P3] goto P2 */ +#define OP_Le 81 /* same as TK_LE, synopsis: if r[P1]<=r[P3] goto P2 */ +#define OP_Lt 82 /* same as TK_LT, synopsis: if r[P1]=r[P3] goto P2 */ +#define OP_RowKey 84 /* synopsis: r[P2]=key */ +#define OP_BitAnd 85 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ +#define OP_BitOr 86 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ +#define OP_ShiftLeft 87 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<>r[P1] */ +#define OP_Add 89 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */ +#define OP_Subtract 90 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */ +#define OP_Multiply 91 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */ +#define OP_Divide 92 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ +#define OP_Remainder 93 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ +#define OP_Concat 94 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ +#define OP_RowData 95 /* synopsis: r[P2]=data */ +#define OP_BitNot 96 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */ +#define OP_String8 97 /* same as TK_STRING, synopsis: r[P2]='P4' */ +#define OP_Rowid 98 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 99 +#define OP_Last 100 +#define OP_SorterSort 101 +#define OP_Sort 102 +#define OP_Rewind 103 +#define OP_SorterInsert 104 +#define OP_IdxInsert 105 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 106 /* synopsis: key=r[P2@P3] */ +#define OP_IdxRowid 107 /* synopsis: r[P2]=rowid */ +#define OP_IdxLT 108 /* synopsis: key=r[P3@P4] */ +#define OP_IdxGE 109 /* synopsis: key=r[P3@P4] */ +#define OP_Destroy 110 +#define OP_Clear 111 +#define OP_CreateIndex 112 /* synopsis: r[P2]=root iDb=P1 */ +#define OP_CreateTable 113 /* synopsis: r[P2]=root iDb=P1 */ +#define OP_ParseSchema 114 +#define OP_LoadAnalysis 115 +#define OP_DropTable 116 +#define OP_DropIndex 117 +#define OP_DropTrigger 118 +#define OP_IntegrityCk 119 +#define OP_RowSetAdd 120 /* synopsis: rowset(P1)=r[P2] */ +#define OP_RowSetRead 121 /* synopsis: r[P3]=rowset(P1) */ +#define OP_RowSetTest 122 /* synopsis: if r[P3] in rowset(P1) goto P2 */ +#define OP_Program 123 +#define OP_Param 124 +#define OP_FkCounter 125 /* synopsis: fkctr[P1]+=P2 */ +#define OP_FkIfZero 126 /* synopsis: if fkctr[P1]==0 goto P2 */ +#define OP_MemMax 127 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_IfPos 128 /* synopsis: if r[P1]>0 goto P2 */ +#define OP_IfNeg 129 /* synopsis: if r[P1]<0 goto P2 */ +#define OP_IfZero 130 /* synopsis: r[P1]+=P3, if r[P1]==0 goto P2 */ +#define OP_AggFinal 131 /* synopsis: accum=r[P1] N=P2 */ +#define OP_IncrVacuum 132 +#define OP_Real 133 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ +#define OP_Expire 134 +#define OP_TableLock 135 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 136 +#define OP_VCreate 137 +#define OP_VDestroy 138 +#define OP_VOpen 139 +#define OP_VColumn 140 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VNext 141 +#define OP_VRename 142 +#define OP_ToText 143 /* same as TK_TO_TEXT */ +#define OP_ToBlob 144 /* same as TK_TO_BLOB */ +#define OP_ToNumeric 145 /* same as TK_TO_NUMERIC */ +#define OP_ToInt 146 /* same as TK_TO_INT */ +#define OP_ToReal 147 /* same as TK_TO_REAL */ +#define OP_Pagecount 148 +#define OP_MaxPgcnt 149 +#define OP_Trace 150 +#define OP_Noop 151 +#define OP_Explain 152 /* Properties such as "out2" or "jump" that are specified in @@ -9153,24 +9247,25 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,\ -/* 8 */ 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01,\ -/* 16 */ 0x04, 0x04, 0x10, 0x24, 0x00, 0x02, 0x02, 0x02,\ -/* 24 */ 0x02, 0x02, 0x02, 0x00, 0x00, 0x24, 0x00, 0x00,\ -/* 32 */ 0x04, 0x05, 0x04, 0x00, 0x00, 0x01, 0x01, 0x05,\ -/* 40 */ 0x05, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,\ -/* 48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,\ -/* 56 */ 0x11, 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11,\ -/* 64 */ 0x02, 0x02, 0x00, 0x00, 0x4c, 0x4c, 0x00, 0x00,\ -/* 72 */ 0x00, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ -/* 80 */ 0x15, 0x00, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\ -/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x00, 0x24, 0x02, 0x00,\ -/* 96 */ 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x08, 0x08,\ -/* 104 */ 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02,\ -/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x45,\ -/* 120 */ 0x15, 0x01, 0x02, 0x00, 0x01, 0x08, 0x05, 0x05,\ -/* 128 */ 0x05, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,\ -/* 136 */ 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x04, 0x04,\ -/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,} +/* 8 */ 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,\ +/* 16 */ 0x01, 0x01, 0x04, 0x24, 0x04, 0x10, 0x00, 0x02,\ +/* 24 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x20,\ +/* 32 */ 0x00, 0x00, 0x04, 0x05, 0x04, 0x00, 0x00, 0x01,\ +/* 40 */ 0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x02,\ +/* 48 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 56 */ 0x00, 0x11, 0x11, 0x11, 0x11, 0x08, 0x11, 0x11,\ +/* 64 */ 0x11, 0x11, 0x02, 0x02, 0x00, 0x00, 0x00, 0x4c,\ +/* 72 */ 0x4c, 0x00, 0x00, 0x00, 0x05, 0x05, 0x15, 0x15,\ +/* 80 */ 0x15, 0x15, 0x15, 0x15, 0x00, 0x4c, 0x4c, 0x4c,\ +/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x00,\ +/* 96 */ 0x24, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,\ +/* 104 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\ +/* 112 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 120 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ +/* 128 */ 0x05, 0x05, 0x05, 0x00, 0x01, 0x02, 0x00, 0x00,\ +/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04,\ +/* 144 */ 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00,\ +/* 152 */ 0x00,} /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -9179,7 +9274,7 @@ typedef struct VdbeOpList VdbeOpList; ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*); SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); @@ -9194,7 +9289,9 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr); +SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op); SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); +SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); @@ -9207,7 +9304,6 @@ SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); -SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); #endif SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*); @@ -9233,15 +9329,27 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); #endif - -#ifndef NDEBUG +/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on +** each VDBE opcode. +** +** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op +** comments in VDBE programs that show key decision points in the code +** generator. +*/ +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...); # define VdbeComment(X) sqlite3VdbeComment X SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); # define VdbeNoopComment(X) sqlite3VdbeNoopComment X +# ifdef SQLITE_ENABLE_MODULE_COMMENTS +# define VdbeModuleComment(X) sqlite3VdbeNoopComment X +# else +# define VdbeModuleComment(X) +# endif #else # define VdbeComment(X) # define VdbeNoopComment(X) +# define VdbeModuleComment(X) #endif #endif @@ -9388,6 +9496,7 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); +SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*); /* Operations on page references. */ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); @@ -9402,7 +9511,7 @@ SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); -SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster); SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); @@ -10262,6 +10371,7 @@ struct sqlite3 { #define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */ #define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */ #define SQLITE_QueryOnly 0x02000000 /* Disable database changes */ +#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */ /* @@ -10273,7 +10383,7 @@ struct sqlite3 { #define SQLITE_ColumnCache 0x0002 /* Column cache */ #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ -#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ +/* not used 0x0010 // Was: SQLITE_IdxRealAsInt */ #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ @@ -10295,6 +10405,13 @@ struct sqlite3 { #define OptimizationEnabled(db, mask) 1 #endif +/* +** Return true if it OK to factor constant expressions into the initialization +** code. The argument is a Parse object for the code generator. +*/ +#define ConstFactorOk(P) \ + ((P)->cookieGoto>0 && OptimizationEnabled((P)->db,SQLITE_FactorOutConst)) + /* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other @@ -10361,6 +10478,7 @@ struct FuncDestructor { #define SQLITE_FUNC_COUNT 0x100 /* Built-in count(*) aggregate */ #define SQLITE_FUNC_COALESCE 0x200 /* Built-in coalesce() or ifnull() */ #define SQLITE_FUNC_UNLIKELY 0x400 /* Built-in unlikely() function */ +#define SQLITE_FUNC_CONSTANT 0x800 /* Constant inputs give a constant output */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are @@ -10373,6 +10491,9 @@ struct FuncDestructor { ** as the user-data (sqlite3_user_data()) for the function. If ** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. ** +** VFUNCTION(zName, nArg, iArg, bNC, xFunc) +** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag. +** ** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) ** Used to create an aggregate function definition implemented by ** the C functions xStep and xFinal. The first four parameters @@ -10388,16 +10509,20 @@ struct FuncDestructor { ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ + {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ - {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ + {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ + {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ pArg, 0, xFunc, 0, 0, #zName, 0, 0} #define LIKEFUNC(zName, nArg, arg, flags) \ - {nArg, SQLITE_UTF8|flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} + {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ + (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} @@ -10630,13 +10755,14 @@ struct Table { }; /* -** Allowed values for Tabe.tabFlags. +** Allowed values for Table.tabFlags. */ #define TF_Readonly 0x01 /* Read-only system table */ #define TF_Ephemeral 0x02 /* An ephemeral table */ #define TF_HasPrimaryKey 0x04 /* Table has a primary key */ #define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ #define TF_Virtual 0x10 /* Is a virtual table */ +#define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */ /* @@ -10652,6 +10778,9 @@ struct Table { # define IsHiddenColumn(X) 0 #endif +/* Does the table have a rowid */ +#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0) + /* ** Each foreign key constraint is an instance of the following structure. ** @@ -10666,26 +10795,35 @@ struct Table { ** ); ** ** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". +** Equivalent names: +** +** from-table == child-table +** to-table == parent-table ** ** Each REFERENCES clause generates an instance of the following structure ** which is attached to the from-table. The to-table need not exist when ** the from-table is created. The existence of the to-table is not checked. +** +** The list of all parents for child Table X is held at X.pFKey. +** +** A list of all children for a table named Z (which might not even exist) +** is held in Schema.fkeyHash with a hash key of Z. */ struct FKey { Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */ - FKey *pNextFrom; /* Next foreign key in pFrom */ + FKey *pNextFrom; /* Next FKey with the same in pFrom. Next parent of pFrom */ char *zTo; /* Name of table that the key points to (aka: Parent) */ - FKey *pNextTo; /* Next foreign key on table named zTo */ - FKey *pPrevTo; /* Previous foreign key on table named zTo */ + FKey *pNextTo; /* Next with the same zTo. Next child of zTo. */ + FKey *pPrevTo; /* Previous with the same zTo */ int nCol; /* Number of columns in this key */ /* EV: R-30323-21917 */ - u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ - u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ - Trigger *apTrigger[2]; /* Triggers for aAction[] actions */ - struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ - int iFrom; /* Index of column in pFrom */ - char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ - } aCol[1]; /* One entry for each of nCol column s */ + u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ + u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ + Trigger *apTrigger[2];/* Triggers for aAction[] actions */ + struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ + int iFrom; /* Index of column in pFrom */ + char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ + } aCol[1]; /* One entry for each of nCol columns */ }; /* @@ -10738,9 +10876,11 @@ struct FKey { ** for the rowid at the end. */ struct KeyInfo { - sqlite3 *db; /* The database connection */ + u32 nRef; /* Number of references to this KeyInfo object */ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ - u16 nField; /* Maximum index for aColl[] and aSortOrder[] */ + u16 nField; /* Number of key columns in the index */ + u16 nXField; /* Number of columns beyond the key columns */ + sqlite3 *db; /* The database connection */ u8 *aSortOrder; /* Sort order for each column. */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; @@ -10763,7 +10903,6 @@ struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ u16 nField; /* Number of entries in apMem[] */ u8 flags; /* Boolean settings. UNPACKED_... below */ - i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */ Mem *aMem; /* Values */ }; @@ -10772,7 +10911,6 @@ struct UnpackedRecord { */ #define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ #define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ -#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */ /* ** Each SQL index is represented in memory by an @@ -10802,7 +10940,7 @@ struct UnpackedRecord { */ struct Index { char *zName; /* Name of this index */ - int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + i16 *aiColumn; /* Which columns are used by this index. 1st is 0 */ tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ Table *pTable; /* The SQL table being indexed */ char *zColAff; /* String defining the affinity of each column */ @@ -10811,13 +10949,17 @@ struct Index { u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ Expr *pPartIdxWhere; /* WHERE clause for partial indices */ + KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */ int tnum; /* DB Page containing root of this index */ LogEst szIdxRow; /* Estimated average row size in bytes */ - u16 nColumn; /* Number of columns in table used by this index */ + u16 nKeyCol; /* Number of columns forming the key */ + u16 nColumn; /* Number of columns stored in the index */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ + unsigned isResized:1; /* True if resizeIndexObject() has been called */ + unsigned isCovering:1; /* True if this is a covering index */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ @@ -10873,6 +11015,7 @@ struct AggInfo { int sortingIdx; /* Cursor number of the sorting index */ int sortingIdxPTab; /* Cursor number of pseudo-table */ int nSortingColumn; /* Number of columns in the sorting index */ + int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ ExprList *pGroupBy; /* The group by clause */ struct AggInfo_col { /* For each column used in source tables */ Table *pTab; /* Source table */ @@ -11030,7 +11173,7 @@ struct Expr { #define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ #define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ #define EP_Collate 0x000100 /* Tree contains a TK_COLLATE opeartor */ -#define EP_FixedDest 0x000200 /* Result needed in a specific register */ + /* unused 0x000200 */ #define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ #define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */ #define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */ @@ -11040,6 +11183,7 @@ struct Expr { #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ +#define EP_Constant 0x080000 /* Node is a constant */ /* ** These macros can be used to test, set, or clear bits in the @@ -11101,8 +11245,14 @@ struct ExprList { u8 sortOrder; /* 1 for DESC or 0 for ASC */ unsigned done :1; /* A flag to indicate when processing is finished */ unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ - u16 iOrderByCol; /* For ORDER BY, column number in result set */ - u16 iAlias; /* Index into Parse.aAlias[] for zName */ + unsigned reusable :1; /* Constant expression is reusable */ + union { + struct { + u16 iOrderByCol; /* For ORDER BY, column number in result set */ + u16 iAlias; /* Index into Parse.aAlias[] for zName */ + } x; + int iConstExprReg; /* Register in which Expr value is cached */ + } u; } *a; /* Alloc a power of two greater or equal to nExpr */ }; @@ -11158,6 +11308,7 @@ typedef u64 Bitmask; ** A bit in a Bitmask */ #define MASKBIT(n) (((Bitmask)1)<<(n)) +#define MASKBIT32(n) (((unsigned int)1)<<(n)) /* ** The following structure describes the FROM clause of a SELECT statement. @@ -11194,6 +11345,7 @@ struct SrcList { unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ + unsigned isRecursive :1; /* True for recursive reference in WITH */ #ifndef SQLITE_OMIT_EXPLAIN u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ #endif @@ -11320,6 +11472,7 @@ struct Select { Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ + With *pWith; /* WITH clause attached to this select. Or NULL. */ }; /* @@ -11337,11 +11490,70 @@ struct Select { #define SF_Materialize 0x0100 /* Force materialization of views */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ +#define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ /* -** The results of a select can be distributed in several ways. The -** "SRT" prefix means "SELECT Result Type". +** The results of a SELECT can be distributed in several ways, as defined +** by one of the following macros. The "SRT" prefix means "SELECT Result +** Type". +** +** SRT_Union Store results as a key in a temporary index +** identified by pDest->iSDParm. +** +** SRT_Except Remove results from the temporary index pDest->iSDParm. +** +** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result +** set is not empty. +** +** SRT_Discard Throw the results away. This is used by SELECT +** statements within triggers whose only purpose is +** the side-effects of functions. +** +** All of the above are free to ignore their ORDER BY clause. Those that +** follow must honor the ORDER BY clause. +** +** SRT_Output Generate a row of output (using the OP_ResultRow +** opcode) for each row in the result set. +** +** SRT_Mem Only valid if the result is a single column. +** Store the first column of the first result row +** in register pDest->iSDParm then abandon the rest +** of the query. This destination implies "LIMIT 1". +** +** SRT_Set The result must be a single column. Store each +** row of result as the key in table pDest->iSDParm. +** Apply the affinity pDest->affSdst before storing +** results. Used to implement "IN (SELECT ...)". +** +** SRT_EphemTab Create an temporary table pDest->iSDParm and store +** the result there. The cursor is left open after +** returning. This is like SRT_Table except that +** this destination uses OP_OpenEphemeral to create +** the table first. +** +** SRT_Coroutine Generate a co-routine that returns a new row of +** results each time it is invoked. The entry point +** of the co-routine is stored in register pDest->iSDParm +** and the result row is stored in pDest->nDest registers +** starting with pDest->iSdst. +** +** SRT_Table Store results in temporary table pDest->iSDParm. +** This is like SRT_EphemTab except that the table +** is assumed to already be open. +** +** SRT_DistTable Store results in a temporary table pDest->iSDParm. +** But also use temporary table pDest->iSDParm+1 as +** a record of all prior results and ignore any duplicate +** rows. Name means: "Distinct Table". +** +** SRT_Queue Store results in priority queue pDest->iSDParm (really +** an index). Append a sequence number so that all entries +** are distinct. +** +** SRT_DistQueue Store results in priority queue pDest->iSDParm only if +** the same record has never been stored before. The +** index at pDest->iSDParm+1 hold all prior stores. */ #define SRT_Union 1 /* Store result as keys in an index */ #define SRT_Except 2 /* Remove result from a UNION index */ @@ -11354,20 +11566,24 @@ struct Select { #define SRT_Output 5 /* Output each row of result */ #define SRT_Mem 6 /* Store result in a memory cell */ #define SRT_Set 7 /* Store results as keys in an index */ -#define SRT_Table 8 /* Store result as data with an automatic rowid */ -#define SRT_EphemTab 9 /* Create transient tab and store like SRT_Table */ -#define SRT_Coroutine 10 /* Generate a single row of result */ +#define SRT_EphemTab 8 /* Create transient tab and store like SRT_Table */ +#define SRT_Coroutine 9 /* Generate a single row of result */ +#define SRT_Table 10 /* Store result as data with an automatic rowid */ +#define SRT_DistTable 11 /* Like SRT_Table, but unique results only */ +#define SRT_Queue 12 /* Store result in an queue */ +#define SRT_DistQueue 13 /* Like SRT_Queue, but unique results only */ /* ** An instance of this object describes where to put of the results of ** a SELECT statement. */ struct SelectDest { - u8 eDest; /* How to dispose of the results. On of SRT_* above. */ - char affSdst; /* Affinity used when eDest==SRT_Set */ - int iSDParm; /* A parameter used by the eDest disposal method */ - int iSdst; /* Base register where results are written */ - int nSdst; /* Number of registers allocated */ + u8 eDest; /* How to dispose of the results. On of SRT_* above. */ + char affSdst; /* Affinity used when eDest==SRT_Set */ + int iSDParm; /* A parameter used by the eDest disposal method */ + int iSdst; /* Base register where results are written */ + int nSdst; /* Number of registers allocated */ + ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */ }; /* @@ -11467,6 +11683,10 @@ struct Parse { int nMem; /* Number of memory cells used so far */ int nSet; /* Number of sets used so far */ int nOnce; /* Number of OP_Once instructions so far */ + int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */ + int nLabel; /* Number of labels used */ + int *aLabel; /* Space to hold the labels */ + int iFixedOp; /* Never back out opcodes iFixedOp-1 or earlier */ int ckBase; /* Base register of data during check constraints */ int iPartIdxTab; /* Table corresponding to a partial index */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ @@ -11479,6 +11699,7 @@ struct Parse { int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ + ExprList *pConstExpr;/* Constant expressions */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ @@ -11496,6 +11717,8 @@ struct Parse { /* Information used while coding trigger programs. */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ + int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */ + int addrSkipPK; /* Address of instruction to skip PRIMARY KEY index */ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ @@ -11508,6 +11731,7 @@ struct Parse { int nVar; /* Number of '?' variables seen in the SQL so far */ int nzVar; /* Number of available slots in azVar[] */ + u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ #ifndef SQLITE_OMIT_VIRTUALTABLE u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ @@ -11521,7 +11745,6 @@ struct Parse { #endif char **azVar; /* Pointers to names of parameters */ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ - int *aAlias; /* Register used to hold aliased result */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ @@ -11534,6 +11757,8 @@ struct Parse { #endif Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ + With *pWith; /* Current WITH clause, or NULL */ + u8 bFreeWith; /* True if pWith should be freed with parser */ }; /* @@ -11653,7 +11878,7 @@ struct TriggerStep { Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ Token target; /* Target table for DELETE, UPDATE, INSERT */ Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ - ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */ + ExprList *pExprList; /* SET clause for UPDATE. */ IdList *pIdList; /* Column names for INSERT */ TriggerStep *pNext; /* Next in the link-list */ TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */ @@ -11714,6 +11939,7 @@ struct Sqlite3Config { int bOpenUri; /* True to interpret filenames as URIs */ int bUseCis; /* Use covering indices for full-scans */ int mxStrlen; /* Maximum string length */ + int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ @@ -11750,15 +11976,33 @@ struct Sqlite3Config { #endif }; +/* +** This macro is used inside of assert() statements to indicate that +** the assert is only valid on a well-formed database. Instead of: +** +** assert( X ); +** +** One writes: +** +** assert( X || CORRUPT_DB ); +** +** CORRUPT_DB is true during normal operation. CORRUPT_DB does not indicate +** that the database is definitely corrupt, only that it might be corrupt. +** For most test cases, CORRUPT_DB is set to false using a special +** sqlite3_test_control(). This enables assert() statements to prove +** things that are always true for well-formed databases. +*/ +#define CORRUPT_DB (sqlite3Config.neverCorrupt==0) + /* ** Context pointer passed down through the tree-walk. */ struct Walker { int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ + void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ Parse *pParse; /* Parser context. */ int walkerDepth; /* Number of subqueries */ - u8 bSelectDepthFirst; /* Do subqueries first */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ @@ -11782,6 +12026,21 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*); #define WRC_Prune 1 /* Omit children but continue walking siblings */ #define WRC_Abort 2 /* Abandon the tree walk */ +/* +** An instance of this structure represents a set of one or more CTEs +** (common table expressions) created by a single WITH clause. +*/ +struct With { + int nCte; /* Number of CTEs in the WITH clause */ + With *pOuter; /* Containing WITH clause, or NULL */ + struct Cte { /* For each CTE in the WITH clause.... */ + char *zName; /* Name of this CTE */ + ExprList *pCols; /* List of explicit column names, or NULL */ + Select *pSelect; /* The definition of this CTE */ + const char *zErr; /* Error message for circular references */ + } a[1]; +}; + /* ** Assuming zIn points to the first byte of a UTF-8 character, ** advance zIn to point to the first byte of the next UTF-8 character. @@ -11921,10 +12180,20 @@ SQLITE_PRIVATE int sqlite3IsNaN(double); # define sqlite3IsNaN(X) 0 #endif -SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, int, const char*, va_list); -#ifndef SQLITE_OMIT_TRACE -SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, const char*, ...); -#endif +/* +** An instance of the following structure holds information about SQL +** functions arguments that are the parameters to the printf() function. +*/ +struct PrintfArguments { + int nArg; /* Total number of arguments */ + int nUsed; /* Number of arguments used so far */ + sqlite3_value **apArg; /* The argument values */ +}; + +#define SQLITE_PRINTF_INTERNAL 0x01 +#define SQLITE_PRINTF_SQLFUNC 0x02 +SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, u32, const char*, va_list); +SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, u32, const char*, ...); SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3*,char*,const char*,...); @@ -11990,6 +12259,8 @@ SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int); SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*); SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*); SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int); +SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*); +SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index*, i16); SQLITE_PRIVATE void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int); SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*); SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int); @@ -11998,7 +12269,7 @@ SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*); SQLITE_PRIVATE void sqlite3AddColumnType(Parse*,Token*); SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*); SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); -SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,Select*); +SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*); @@ -12037,7 +12308,7 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse); # define sqlite3AutoincrementEnd(X) #endif SQLITE_PRIVATE int sqlite3CodeCoroutine(Parse*, Select*, SelectDest*); -SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); +SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int); SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); @@ -12051,6 +12322,7 @@ SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*); SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); +SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, Expr*, int, int); SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); @@ -12073,7 +12345,7 @@ SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*); SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*); SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*); SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*); -SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*); +SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*, int*); SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int); @@ -12084,11 +12356,13 @@ SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int); SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*); SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int); SQLITE_PRIVATE int sqlite3ExprCode(Parse*, Expr*, int); +SQLITE_PRIVATE void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8); SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse*, Expr*, int); -SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse*, Expr*); -SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int); +SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8); +#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ +#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse*, Expr*, int, int); SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse*, Expr*, int, int); SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*); @@ -12109,7 +12383,6 @@ SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*); SQLITE_PRIVATE void sqlite3PrngSaveState(void); SQLITE_PRIVATE void sqlite3PrngRestoreState(void); -SQLITE_PRIVATE void sqlite3PrngResetState(void); SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int); SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int); SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); @@ -12127,17 +12400,19 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); -SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int); -SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*); -SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*); -SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int, - int*,int,int,int,int,int*); -SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); -SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); +SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8); +SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*); +SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); +SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, + u8,u8,int,int*); +SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int); +SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, u8*, int*, int*); SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int); SQLITE_PRIVATE void sqlite3MultiWrite(Parse*); SQLITE_PRIVATE void sqlite3MayAbort(Parse*); -SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, int); +SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); +SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*); +SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*); SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int); SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); @@ -12171,7 +12446,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, i SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*, - ExprList*,Select*,u8); + Select*,u8); SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8); SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*); SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*); @@ -12276,8 +12551,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ - defined(SQLITE_DEBUG_OS_TRACE) +#if defined(SQLITE_TEST) SQLITE_PRIVATE const char *sqlite3ErrName(int); #endif @@ -12307,6 +12581,7 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8); SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8); SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); +SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value*); SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*); SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); @@ -12355,8 +12630,13 @@ SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int); SQLITE_PRIVATE void sqlite3SchemaClear(void *); SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *); SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *); -SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int); -SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); +SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int); +SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*); +SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*); +SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*); +#endif SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*), @@ -12367,6 +12647,7 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int); SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int); +SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum*,const char*); SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum*,int); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*); @@ -12448,6 +12729,7 @@ SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**) SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); +SQLITE_PRIVATE void sqlite3ParserReset(Parse*); SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*); SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); @@ -12457,6 +12739,14 @@ SQLITE_PRIVATE const char *sqlite3JournalModename(int); SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); #endif +#ifndef SQLITE_OMIT_CTE +SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*); +SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); +SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8); +#else +#define sqlite3WithPush(x,y,z) +#define sqlite3WithDelete(x,y) +#endif /* Declarations for functions in fkey.c. All of these are replaced by ** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign @@ -12764,6 +13054,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_USE_URI, /* bOpenUri */ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0x7ffffffe, /* mxStrlen */ + 0, /* neverCorrupt */ 128, /* szLookaside */ 500, /* nLookaside */ {0,0,0,0,0,0,0,0}, /* m */ @@ -12799,7 +13090,6 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { #endif }; - /* ** Hash table for global functions - functions common to all ** database connections. After initialization, this table is @@ -13196,6 +13486,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_SOUNDEX "SOUNDEX", #endif +#ifdef SQLITE_SYSTEM_MALLOC + "SYSTEM_MALLOC", +#endif #ifdef SQLITE_TCL "TCL", #endif @@ -13211,6 +13504,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_USE_ALLOCA "USE_ALLOCA", #endif +#ifdef SQLITE_WIN32_MALLOC + "WIN32_MALLOC", +#endif #ifdef SQLITE_ZERO_MALLOC "ZERO_MALLOC" #endif @@ -13310,7 +13606,7 @@ typedef struct VdbeOp Op; /* ** Boolean values */ -typedef unsigned char Bool; +typedef unsigned Bool; /* Opaque type used by code in vdbesort.c */ typedef struct VdbeSorter VdbeSorter; @@ -13327,6 +13623,9 @@ typedef struct AuxData AuxData; ** loop over all entries of the Btree. You can also insert new BTree ** entries or retrieve the key or data from the entry that the cursor ** is currently pointing to. +** +** Cursors can also point to virtual tables, sorters, or "pseudo-tables". +** A pseudo-table is a single-row table implemented by registers. ** ** Every cursor that the virtual machine has open is represented by an ** instance of the following structure. @@ -13335,31 +13634,24 @@ struct VdbeCursor { BtCursor *pCursor; /* The cursor structure of the backend */ Btree *pBt; /* Separate file holding temporary table */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ - int iDb; /* Index of cursor database in db->aDb[] (or -1) */ + int seekResult; /* Result of previous sqlite3BtreeMoveto() */ int pseudoTableReg; /* Register holding pseudotable content. */ - int nField; /* Number of fields in the header */ - Bool zeroed; /* True if zeroed out and ready for reuse */ - Bool rowidIsValid; /* True if lastRowid is valid */ - Bool atFirst; /* True if pointing to first entry */ - Bool useRandomRowid; /* Generate new record numbers semi-randomly */ - Bool nullRow; /* True if pointing to a row with no data */ - Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ - Bool isTable; /* True if a table requiring integer keys */ - Bool isIndex; /* True if an index containing keys only - no data */ - Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ - Bool isSorter; /* True if a new-style sorter */ - Bool multiPseudo; /* Multi-register pseudo-cursor */ + i16 nField; /* Number of fields in the header */ + u16 nHdrParsed; /* Number of header fields parsed so far */ + i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ + u8 nullRow; /* True if pointing to a row with no data */ + u8 rowidIsValid; /* True if lastRowid is valid */ + u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ + Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ + Bool isTable:1; /* True if a table requiring integer keys */ + Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ + Bool multiPseudo:1; /* Multi-register pseudo-cursor */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ - const sqlite3_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ - i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ + i64 lastRowid; /* Rowid being deleted by OP_Delete */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ - /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or - ** OP_IsUnique opcode on this cursor. */ - int seekResult; - /* Cached information about the header for the data record that the ** cursor is currently pointing to. Only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of @@ -13370,10 +13662,14 @@ struct VdbeCursor { ** be NULL. */ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ - int payloadSize; /* Total number of bytes in the record */ - u32 *aType; /* Type values for all entries in the record */ - u32 *aOffset; /* Cached offsets to the start of each columns data */ - u8 *aRow; /* Data for the current row, if all on one page */ + u32 payloadSize; /* Total number of bytes in the record */ + u32 szRow; /* Byte available in aRow */ + u32 iHdrOffset; /* Offset to next unparsed byte of the header */ + const u8 *aRow; /* Data for the current row, if all on one page */ + u32 aType[1]; /* Type values for all entries in the record */ + /* 2*nField extra array elements allocated for aType[], beyond the one + ** static element declared in the structure. nField total array slots for + ** aType[] and nField+1 array slots for aOffset[] */ }; typedef struct VdbeCursor VdbeCursor; @@ -13586,12 +13882,9 @@ struct Vdbe { Mem **apArg; /* Arguments to currently executing user function */ Mem *aColName; /* Column names to return */ Mem *pResultSet; /* Pointer to an array of results */ + Parse *pParse; /* Parsing context used to create this Vdbe */ int nMem; /* Number of memory locations currently allocated */ int nOp; /* Number of instructions in the program */ - int nOpAlloc; /* Number of slots allocated for aOp[] */ - int nLabel; /* Number of labels used */ - int *aLabel; /* Space to hold the labels */ - u16 nResColumn; /* Number of columns in one row of the result set */ int nCursor; /* Number of slots in apCsr[] */ u32 magic; /* Magic number for sanity checking */ char *zErrMsg; /* Error message written here */ @@ -13604,6 +13897,7 @@ struct Vdbe { u32 cacheCtr; /* VdbeCursor row cache generation counter */ int pc; /* The program counter */ int rc; /* Value to return */ + u16 nResColumn; /* Number of columns in one row of the result set */ u8 errorAction; /* Recovery action to do in case of an error */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ bft explain:2; /* True if EXPLAIN present on SQL command */ @@ -13630,9 +13924,6 @@ struct Vdbe { i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ -#ifdef SQLITE_DEBUG - FILE *trace; /* Write an execution trace here, if not NULL */ -#endif #ifdef SQLITE_ENABLE_TREE_EXPLAIN Explain *pExplain; /* The explainer */ char *zExplain; /* Explanation of data structures */ @@ -13666,7 +13957,7 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, Op*); #endif SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int); -SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int); +SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe*, int, int); @@ -13701,7 +13992,7 @@ SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*); SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*); -SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); +SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*); SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p); #define VdbeMemRelease(X) \ @@ -13722,7 +14013,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *); SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *); SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *); SQLITE_PRIVATE int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *); -SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int *); +SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*); @@ -15256,7 +15547,21 @@ SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){ ** routine has no return value since the return value would be meaningless. */ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ - DO_OS_MALLOC_TEST(id); +#ifdef SQLITE_TEST + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ + /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite + ** is using a regular VFS, it is called after the corresponding + ** transaction has been committed. Injecting a fault at this point + ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM + ** but the transaction is committed anyway. + ** + ** The core must call OsFileControl() though, not OsFileControlHint(), + ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably + ** means the commit really has failed and an error should be returned + ** to the user. */ + DO_OS_MALLOC_TEST(id); + } +#endif return id->pMethods->xFileControl(id, op, pArg); } SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){ @@ -15726,16 +16031,6 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){ ** macros. */ #ifdef SQLITE_SYSTEM_MALLOC - -/* -** The MSVCRT has malloc_usable_size() but it is called _msize(). -** The use of _msize() is automatic, but can be disabled by compiling -** with -DSQLITE_WITHOUT_MSIZE -*/ -#if defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE) -# define SQLITE_MALLOCSIZE _msize -#endif - #if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC) /* @@ -15758,22 +16053,48 @@ static malloc_zone_t* _sqliteZone_; ** Use standard C library malloc and free on non-Apple systems. ** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined. */ -#define SQLITE_MALLOC(x) malloc(x) -#define SQLITE_FREE(x) free(x) -#define SQLITE_REALLOC(x,y) realloc((x),(y)) +#define SQLITE_MALLOC(x) malloc(x) +#define SQLITE_FREE(x) free(x) +#define SQLITE_REALLOC(x,y) realloc((x),(y)) -#if (defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)) \ - || (defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE)) -# include /* Needed for malloc_usable_size on linux */ -#endif -#ifdef HAVE_MALLOC_USABLE_SIZE -# ifndef SQLITE_MALLOCSIZE -# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x) -# endif -#else -# undef SQLITE_MALLOCSIZE +/* +** The malloc.h header file is needed for malloc_usable_size() function +** on some systems (e.g. Linux). +*/ +#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE) +# define SQLITE_USE_MALLOC_H +# define SQLITE_USE_MALLOC_USABLE_SIZE +/* +** The MSVCRT has malloc_usable_size(), but it is called _msize(). The +** use of _msize() is automatic, but can be disabled by compiling with +** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires +** the malloc.h header file. +*/ +#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE) +# define SQLITE_USE_MALLOC_H +# define SQLITE_USE_MSIZE #endif +/* +** Include the malloc.h header file, if necessary. Also set define macro +** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize() +** for MSVC and malloc_usable_size() for most other systems (e.g. Linux). +** The memory size function can always be overridden manually by defining +** the macro SQLITE_MALLOCSIZE to the desired function name. +*/ +#if defined(SQLITE_USE_MALLOC_H) +# include +# if defined(SQLITE_USE_MALLOC_USABLE_SIZE) +# if !defined(SQLITE_MALLOCSIZE) +# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x) +# endif +# elif defined(SQLITE_USE_MSIZE) +# if !defined(SQLITE_MALLOCSIZE) +# define SQLITE_MALLOCSIZE _msize +# endif +# endif +#endif /* defined(SQLITE_USE_MALLOC_H) */ + #endif /* __APPLE__ or not __APPLE__ */ /* @@ -17379,32 +17700,13 @@ static void memsys5Leave(void){ static int memsys5Size(void *p){ int iSize = 0; if( p ){ - int i = ((u8 *)p-mem5.zPool)/mem5.szAtom; + int i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom); assert( i>=0 && i=0 && iLogsize<=LOGMAX ); - i = iFirst = mem5.aiFreelist[iLogsize]; - assert( iFirst>=0 ); - while( i>0 ){ - if( inext; - } - memsys5Unlink(iFirst, iLogsize); - return iFirst; -} - /* ** Return a block of memory of at least nBytes in size. ** Return NULL if unable. Return NULL if nBytes==0. @@ -17450,7 +17752,8 @@ static void *memsys5MallocUnsafe(int nByte){ sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte); return 0; } - i = memsys5UnlinkFirst(iBin); + i = mem5.aiFreelist[iBin]; + memsys5Unlink(i, iBin); while( iBin>iLogsize ){ int newSize; @@ -17484,7 +17787,7 @@ static void memsys5FreeUnsafe(void *pOld){ /* Set iBlock to the index of the block pointed to by pOld in ** the array of mem5.szAtom byte blocks pointed to by mem5.zPool. */ - iBlock = ((u8 *)pOld-mem5.zPool)/mem5.szAtom; + iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom); /* Check that the pointer pOld points to a valid, non-free block. */ assert( iBlock>=0 && iBlock=db->lookaside.pStart && plookaside.pEnd; + return p>=db->lookaside.pStart && plookaside.pEnd; } #else #define isLookaside(A,B) 0 @@ -19269,8 +19572,9 @@ SQLITE_PRIVATE int sqlite3MallocSize(void *p){ return sqlite3GlobalConfig.m.xSize(p); } SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ - assert( db==0 || sqlite3_mutex_held(db->mutex) ); - if( db && isLookaside(db, p) ){ + assert( db!=0 ); + assert( sqlite3_mutex_held(db->mutex) ); + if( isLookaside(db, p) ){ return db->lookaside.sz; }else{ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); @@ -19753,6 +20057,31 @@ SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum *pAccum, int N){ } } +/* +** Set the StrAccum object to an error mode. +*/ +static void setStrAccumError(StrAccum *p, u8 eError){ + p->accError = eError; + p->nAlloc = 0; +} + +/* +** Extra argument values from a PrintfArguments object +*/ +static sqlite3_int64 getIntArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0; + return sqlite3_value_int64(p->apArg[p->nUsed++]); +} +static double getDoubleArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0.0; + return sqlite3_value_double(p->apArg[p->nUsed++]); +} +static char *getTextArg(PrintfArguments *p){ + if( p->nArg<=p->nUsed ) return 0; + return (char*)sqlite3_value_text(p->apArg[p->nUsed++]); +} + + /* ** On machines with a small stack size, you can redefine the ** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired. @@ -19766,10 +20095,10 @@ SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum *pAccum, int N){ ** Render a string given by "fmt" into the StrAccum object. */ SQLITE_PRIVATE void sqlite3VXPrintf( - StrAccum *pAccum, /* Accumulate results here */ - int useExtended, /* Allow extended %-conversions */ - const char *fmt, /* Format string */ - va_list ap /* arguments */ + StrAccum *pAccum, /* Accumulate results here */ + u32 bFlags, /* SQLITE_PRINTF_* flags */ + const char *fmt, /* Format string */ + va_list ap /* arguments */ ){ int c; /* Next character in the format string */ char *bufpt; /* Pointer to the conversion buffer */ @@ -19787,6 +20116,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf( etByte flag_longlong; /* True if the "ll" flag is present */ etByte done; /* Loop termination flag */ etByte xtype = 0; /* Conversion paradigm */ + u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ + u8 useIntern; /* Ok to use internal conversions (ex: %T) */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ LONGDOUBLE_TYPE realvalue; /* Value for real types */ @@ -19801,9 +20132,18 @@ SQLITE_PRIVATE void sqlite3VXPrintf( etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ #endif + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ bufpt = 0; + if( bFlags ){ + if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){ + pArgList = va_arg(ap, PrintfArguments*); + } + useIntern = bFlags & SQLITE_PRINTF_INTERNAL; + }else{ + bArgList = useIntern = 0; + } for(; (c=(*fmt))!=0; ++fmt){ if( c!='%' ){ int amt; @@ -19835,7 +20175,11 @@ SQLITE_PRIVATE void sqlite3VXPrintf( /* Get the field width */ width = 0; if( c=='*' ){ - width = va_arg(ap,int); + if( bArgList ){ + width = (int)getIntArg(pArgList); + }else{ + width = va_arg(ap,int); + } if( width<0 ){ flag_leftjustify = 1; width = -width; @@ -19852,7 +20196,11 @@ SQLITE_PRIVATE void sqlite3VXPrintf( precision = 0; c = *++fmt; if( c=='*' ){ - precision = va_arg(ap,int); + if( bArgList ){ + precision = (int)getIntArg(pArgList); + }else{ + precision = va_arg(ap,int); + } if( precision<0 ) precision = -precision; c = *++fmt; }else{ @@ -19883,7 +20231,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf( for(idx=0; idxflags & FLAG_INTERN)==0 ){ + if( useIntern || (infop->flags & FLAG_INTERN)==0 ){ xtype = infop->type; }else{ return; @@ -19923,7 +20271,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf( case etRADIX: if( infop->flags & FLAG_SIGNED ){ i64 v; - if( flag_longlong ){ + if( bArgList ){ + v = getIntArg(pArgList); + }else if( flag_longlong ){ v = va_arg(ap,i64); }else if( flag_long ){ v = va_arg(ap,long int); @@ -19944,7 +20294,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf( else prefix = 0; } }else{ - if( flag_longlong ){ + if( bArgList ){ + longvalue = (u64)getIntArg(pArgList); + }else if( flag_longlong ){ longvalue = va_arg(ap,u64); }else if( flag_long ){ longvalue = va_arg(ap,unsigned long int); @@ -19964,7 +20316,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf( nOut = precision + 10; zOut = zExtra = sqlite3Malloc( nOut ); if( zOut==0 ){ - pAccum->accError = STRACCUM_NOMEM; + setStrAccumError(pAccum, STRACCUM_NOMEM); return; } } @@ -20004,7 +20356,11 @@ SQLITE_PRIVATE void sqlite3VXPrintf( case etFLOAT: case etEXP: case etGENERIC: - realvalue = va_arg(ap,double); + if( bArgList ){ + realvalue = getDoubleArg(pArgList); + }else{ + realvalue = va_arg(ap,double); + } #ifdef SQLITE_OMIT_FLOATING_POINT length = 0; #else @@ -20076,7 +20432,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf( if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){ bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 ); if( bufpt==0 ){ - pAccum->accError = STRACCUM_NOMEM; + setStrAccumError(pAccum, STRACCUM_NOMEM); return; } } @@ -20159,7 +20515,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf( #endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ break; case etSIZE: - *(va_arg(ap,int*)) = pAccum->nChar; + if( !bArgList ){ + *(va_arg(ap,int*)) = pAccum->nChar; + } length = width = 0; break; case etPERCENT: @@ -20168,7 +20526,12 @@ SQLITE_PRIVATE void sqlite3VXPrintf( length = 1; break; case etCHARX: - c = va_arg(ap,int); + if( bArgList ){ + bufpt = getTextArg(pArgList); + c = bufpt ? bufpt[0] : 0; + }else{ + c = va_arg(ap,int); + } buf[0] = (char)c; if( precision>=0 ){ for(idx=1; idx=0 ){ @@ -20199,7 +20566,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf( int needQuote; char ch; char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ - char *escarg = va_arg(ap,char*); + char *escarg; + + if( bArgList ){ + escarg = getTextArg(pArgList); + }else{ + escarg = va_arg(ap,char*); + } isnull = escarg==0; if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); k = precision; @@ -20211,7 +20584,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf( if( n>etBUFSIZE ){ bufpt = zExtra = sqlite3Malloc( n ); if( bufpt==0 ){ - pAccum->accError = STRACCUM_NOMEM; + setStrAccumError(pAccum, STRACCUM_NOMEM); return; } }else{ @@ -20234,7 +20607,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf( } case etTOKEN: { Token *pToken = va_arg(ap, Token*); - if( pToken ){ + assert( bArgList==0 ); + if( pToken && pToken->n ){ sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n); } length = width = 0; @@ -20244,12 +20618,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf( SrcList *pSrc = va_arg(ap, SrcList*); int k = va_arg(ap, int); struct SrcList_item *pItem = &pSrc->a[k]; + assert( bArgList==0 ); assert( k>=0 && knSrc ); if( pItem->zDatabase ){ - sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1); + sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase); sqlite3StrAccumAppend(pAccum, ".", 1); } - sqlite3StrAccumAppend(pAccum, pItem->zName, -1); + sqlite3StrAccumAppendAll(pAccum, pItem->zName); length = width = 0; break; } @@ -20280,7 +20655,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf( sqlite3AppendSpace(pAccum, nspace); } } - sqlite3_free(zExtra); + if( zExtra ) sqlite3_free(zExtra); }/* End for loop over the format string */ } /* End of function */ @@ -20288,22 +20663,20 @@ SQLITE_PRIVATE void sqlite3VXPrintf( ** Append N bytes of text from z to the StrAccum object. */ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ - assert( z!=0 || N==0 ); - if( p->accError ){ - testcase(p->accError==STRACCUM_TOOBIG); - testcase(p->accError==STRACCUM_NOMEM); - return; - } - assert( p->zText!=0 || p->nChar==0 ); - if( N<=0 ){ - if( N==0 || z[0]==0 ) return; - N = sqlite3Strlen30(z); - } + assert( z!=0 ); + assert( p->zText!=0 || p->nChar==0 || p->accError ); + assert( N>=0 ); + assert( p->accError==0 || p->nAlloc==0 ); if( p->nChar+N >= p->nAlloc ){ char *zNew; + if( p->accError ){ + testcase(p->accError==STRACCUM_TOOBIG); + testcase(p->accError==STRACCUM_NOMEM); + return; + } if( !p->useMalloc ){ - p->accError = STRACCUM_TOOBIG; N = p->nAlloc - p->nChar - 1; + setStrAccumError(p, STRACCUM_TOOBIG); if( N<=0 ){ return; } @@ -20313,7 +20686,7 @@ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ szNew += N + 1; if( szNew > p->mxAlloc ){ sqlite3StrAccumReset(p); - p->accError = STRACCUM_TOOBIG; + setStrAccumError(p, STRACCUM_TOOBIG); return; }else{ p->nAlloc = (int)szNew; @@ -20327,8 +20700,8 @@ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); p->zText = zNew; }else{ - p->accError = STRACCUM_NOMEM; sqlite3StrAccumReset(p); + setStrAccumError(p, STRACCUM_NOMEM); return; } } @@ -20338,6 +20711,14 @@ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ p->nChar += N; } +/* +** Append the complete text of zero-terminated string z[] to the p string. +*/ +SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){ + sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z)); +} + + /* ** Finish off a string by making sure it is zero-terminated. ** Return a pointer to the resulting string. Return a NULL @@ -20355,7 +20736,7 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ if( p->zText ){ memcpy(p->zText, p->zBase, p->nChar+1); }else{ - p->accError = STRACCUM_NOMEM; + setStrAccumError(p, STRACCUM_NOMEM); } } } @@ -20401,7 +20782,7 @@ SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list a sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); acc.db = db; - sqlite3VXPrintf(&acc, 1, zFormat, ap); + sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap); z = sqlite3StrAccumFinish(&acc); if( acc.accError==STRACCUM_NOMEM ){ db->mallocFailed = 1; @@ -20557,17 +20938,15 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ } #endif -#ifndef SQLITE_OMIT_TRACE /* ** variable-argument wrapper around sqlite3VXPrintf(). */ -SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){ +SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){ va_list ap; va_start(ap,zFormat); - sqlite3VXPrintf(p, 1, zFormat, ap); + sqlite3VXPrintf(p, bFlags, zFormat, ap); va_end(ap); } -#endif /************** End of printf.c **********************************************/ /************** Begin file random.c ******************************************/ @@ -20624,6 +21003,12 @@ SQLITE_API void sqlite3_randomness(int N, void *pBuf){ sqlite3_mutex_enter(mutex); #endif + if( N<=0 ){ + wsdPrng.isInit = 0; + sqlite3_mutex_leave(mutex); + return; + } + /* Initialize the state of the random number generator once, ** the first time this routine is called. The seed value does ** not need to contain a lot of randomness since we are not @@ -20651,7 +21036,8 @@ SQLITE_API void sqlite3_randomness(int N, void *pBuf){ wsdPrng.isInit = 1; } - while( N-- ){ + assert( N>0 ); + do{ wsdPrng.i++; t = wsdPrng.s[wsdPrng.i]; wsdPrng.j += t; @@ -20659,7 +21045,7 @@ SQLITE_API void sqlite3_randomness(int N, void *pBuf){ wsdPrng.s[wsdPrng.j] = t; t += wsdPrng.s[wsdPrng.i]; *(zBuf++) = wsdPrng.s[t]; - } + }while( --N ); sqlite3_mutex_leave(mutex); } @@ -20688,9 +21074,6 @@ SQLITE_PRIVATE void sqlite3PrngRestoreState(void){ sizeof(sqlite3Prng) ); } -SQLITE_PRIVATE void sqlite3PrngResetState(void){ - GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0; -} #endif /* SQLITE_OMIT_BUILTIN_TEST */ /************** End of random.c **********************************************/ @@ -21342,18 +21725,17 @@ SQLITE_PRIVATE int sqlite3Strlen30(const char *z){ ** to NULL. */ SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){ - if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){ - db->errCode = err_code; - if( zFormat ){ - char *z; - va_list ap; - va_start(ap, zFormat); - z = sqlite3VMPrintf(db, zFormat, ap); - va_end(ap); - sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC); - }else{ - sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC); - } + assert( db!=0 ); + db->errCode = err_code; + if( zFormat && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){ + char *z; + va_list ap; + va_start(ap, zFormat); + z = sqlite3VMPrintf(db, zFormat, ap); + va_end(ap); + sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC); + }else if( db->pErr ){ + sqlite3ValueSetNull(db->pErr); } } @@ -21692,12 +22074,12 @@ static int compare2pow63(const char *zNum, int incr){ ** If the zNum value is representable as a 64-bit twos-complement ** integer, then write that value into *pNum and return 0. ** -** If zNum is exactly 9223372036854665808, return 2. This special -** case is broken out because while 9223372036854665808 cannot be a -** signed 64-bit integer, its negative -9223372036854665808 can be. +** If zNum is exactly 9223372036854775808, return 2. This special +** case is broken out because while 9223372036854775808 cannot be a +** signed 64-bit integer, its negative -9223372036854775808 can be. ** ** If zNum is too big for a 64-bit integer and is not -** 9223372036854665808 or if zNum contains any non-numeric text, +** 9223372036854775808 or if zNum contains any non-numeric text, ** then return 1. ** ** length is the number of bytes in the string (bytes, not characters). @@ -21739,7 +22121,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc u = u*10 + c - '0'; } if( u>LARGEST_INT64 ){ - *pNum = SMALLEST_INT64; + *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; }else if( neg ){ *pNum = -(i64)u; }else{ @@ -21770,7 +22152,6 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc /* zNum is exactly 9223372036854775808. Fits if negative. The ** special case 2 overflow if positive */ assert( u-1==LARGEST_INT64 ); - assert( (*pNum)==SMALLEST_INT64 ); return neg ? 0 : 2; } } @@ -22230,7 +22611,8 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v){ ** Read or write a four-byte big-endian integer value. */ SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){ - return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; + testcase( p[0]&0x80 ); + return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; } SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){ p[0] = (u8)(v>>24); @@ -22509,7 +22891,9 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){ x /= 10; if( n>=5 ) n -= 2; else if( n>=1 ) n -= 1; - if( x>=3 ) return (n+8)<<(x-3); + if( x>=3 ){ + return x>60 ? (u64)LARGEST_INT64 : (n+8)<<(x-3); + } return (n+8)>>(3-x); } @@ -22569,7 +22953,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){ ** The hashing function. */ static unsigned int strHash(const char *z, int nKey){ - int h = 0; + unsigned int h = 0; assert( nKey>=0 ); while( nKey > 0 ){ h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++]; @@ -22800,159 +23184,166 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, voi /************** Begin file opcodes.c *****************************************/ /* Automatically generated. Do not edit */ /* See the mkopcodec.awk script for details. */ -#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) +#if !defined(SQLITE_OMIT_EXPLAIN) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG) +# define OpHelp(X) "\0" X +#else +# define OpHelp(X) +#endif SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ static const char *const azName[] = { "?", - /* 1 */ "Function", - /* 2 */ "Savepoint", - /* 3 */ "AutoCommit", - /* 4 */ "Transaction", - /* 5 */ "SorterNext", - /* 6 */ "Prev", - /* 7 */ "Next", - /* 8 */ "AggStep", - /* 9 */ "Checkpoint", - /* 10 */ "JournalMode", - /* 11 */ "Vacuum", - /* 12 */ "VFilter", - /* 13 */ "VUpdate", - /* 14 */ "Goto", - /* 15 */ "Gosub", - /* 16 */ "Return", - /* 17 */ "Yield", - /* 18 */ "HaltIfNull", - /* 19 */ "Not", - /* 20 */ "Halt", - /* 21 */ "Integer", - /* 22 */ "Int64", - /* 23 */ "String", - /* 24 */ "Null", - /* 25 */ "Blob", - /* 26 */ "Variable", - /* 27 */ "Move", - /* 28 */ "Copy", - /* 29 */ "SCopy", - /* 30 */ "ResultRow", - /* 31 */ "CollSeq", - /* 32 */ "AddImm", - /* 33 */ "MustBeInt", - /* 34 */ "RealAffinity", - /* 35 */ "Permutation", - /* 36 */ "Compare", - /* 37 */ "Jump", - /* 38 */ "Once", - /* 39 */ "If", - /* 40 */ "IfNot", - /* 41 */ "Column", - /* 42 */ "Affinity", - /* 43 */ "MakeRecord", - /* 44 */ "Count", - /* 45 */ "ReadCookie", - /* 46 */ "SetCookie", - /* 47 */ "VerifyCookie", - /* 48 */ "OpenRead", - /* 49 */ "OpenWrite", - /* 50 */ "OpenAutoindex", - /* 51 */ "OpenEphemeral", - /* 52 */ "SorterOpen", - /* 53 */ "OpenPseudo", - /* 54 */ "Close", - /* 55 */ "SeekLt", - /* 56 */ "SeekLe", - /* 57 */ "SeekGe", - /* 58 */ "SeekGt", - /* 59 */ "Seek", - /* 60 */ "NotFound", - /* 61 */ "Found", - /* 62 */ "IsUnique", - /* 63 */ "NotExists", - /* 64 */ "Sequence", - /* 65 */ "NewRowid", - /* 66 */ "Insert", - /* 67 */ "InsertInt", - /* 68 */ "Or", - /* 69 */ "And", - /* 70 */ "Delete", - /* 71 */ "ResetCount", - /* 72 */ "SorterCompare", - /* 73 */ "IsNull", - /* 74 */ "NotNull", - /* 75 */ "Ne", - /* 76 */ "Eq", - /* 77 */ "Gt", - /* 78 */ "Le", - /* 79 */ "Lt", - /* 80 */ "Ge", - /* 81 */ "SorterData", - /* 82 */ "BitAnd", - /* 83 */ "BitOr", - /* 84 */ "ShiftLeft", - /* 85 */ "ShiftRight", - /* 86 */ "Add", - /* 87 */ "Subtract", - /* 88 */ "Multiply", - /* 89 */ "Divide", - /* 90 */ "Remainder", - /* 91 */ "Concat", - /* 92 */ "RowKey", - /* 93 */ "BitNot", - /* 94 */ "String8", - /* 95 */ "RowData", - /* 96 */ "Rowid", - /* 97 */ "NullRow", - /* 98 */ "Last", - /* 99 */ "SorterSort", - /* 100 */ "Sort", - /* 101 */ "Rewind", - /* 102 */ "SorterInsert", - /* 103 */ "IdxInsert", - /* 104 */ "IdxDelete", - /* 105 */ "IdxRowid", - /* 106 */ "IdxLT", - /* 107 */ "IdxGE", - /* 108 */ "Destroy", - /* 109 */ "Clear", - /* 110 */ "CreateIndex", - /* 111 */ "CreateTable", - /* 112 */ "ParseSchema", - /* 113 */ "LoadAnalysis", - /* 114 */ "DropTable", - /* 115 */ "DropIndex", - /* 116 */ "DropTrigger", - /* 117 */ "IntegrityCk", - /* 118 */ "RowSetAdd", - /* 119 */ "RowSetRead", - /* 120 */ "RowSetTest", - /* 121 */ "Program", - /* 122 */ "Param", - /* 123 */ "FkCounter", - /* 124 */ "FkIfZero", - /* 125 */ "MemMax", - /* 126 */ "IfPos", - /* 127 */ "IfNeg", - /* 128 */ "IfZero", - /* 129 */ "AggFinal", - /* 130 */ "Real", - /* 131 */ "IncrVacuum", - /* 132 */ "Expire", - /* 133 */ "TableLock", - /* 134 */ "VBegin", - /* 135 */ "VCreate", - /* 136 */ "VDestroy", - /* 137 */ "VOpen", - /* 138 */ "VColumn", - /* 139 */ "VNext", - /* 140 */ "VRename", - /* 141 */ "ToText", - /* 142 */ "ToBlob", - /* 143 */ "ToNumeric", - /* 144 */ "ToInt", - /* 145 */ "ToReal", - /* 146 */ "Pagecount", - /* 147 */ "MaxPgcnt", - /* 148 */ "Trace", - /* 149 */ "Noop", - /* 150 */ "Explain", + /* 1 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"), + /* 2 */ "Savepoint" OpHelp(""), + /* 3 */ "AutoCommit" OpHelp(""), + /* 4 */ "Transaction" OpHelp(""), + /* 5 */ "SorterNext" OpHelp(""), + /* 6 */ "PrevIfOpen" OpHelp(""), + /* 7 */ "NextIfOpen" OpHelp(""), + /* 8 */ "Prev" OpHelp(""), + /* 9 */ "Next" OpHelp(""), + /* 10 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 11 */ "Checkpoint" OpHelp(""), + /* 12 */ "JournalMode" OpHelp(""), + /* 13 */ "Vacuum" OpHelp(""), + /* 14 */ "VFilter" OpHelp("iPlan=r[P3] zPlan='P4'"), + /* 15 */ "VUpdate" OpHelp("data=r[P3@P2]"), + /* 16 */ "Goto" OpHelp(""), + /* 17 */ "Gosub" OpHelp(""), + /* 18 */ "Return" OpHelp(""), + /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), + /* 20 */ "Yield" OpHelp(""), + /* 21 */ "HaltIfNull" OpHelp("if r[P3] null then halt"), + /* 22 */ "Halt" OpHelp(""), + /* 23 */ "Integer" OpHelp("r[P2]=P1"), + /* 24 */ "Int64" OpHelp("r[P2]=P4"), + /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), + /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), + /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), + /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), + /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), + /* 30 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), + /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), + /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), + /* 33 */ "CollSeq" OpHelp(""), + /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), + /* 35 */ "MustBeInt" OpHelp(""), + /* 36 */ "RealAffinity" OpHelp(""), + /* 37 */ "Permutation" OpHelp(""), + /* 38 */ "Compare" OpHelp(""), + /* 39 */ "Jump" OpHelp(""), + /* 40 */ "Once" OpHelp(""), + /* 41 */ "If" OpHelp(""), + /* 42 */ "IfNot" OpHelp(""), + /* 43 */ "Column" OpHelp("r[P3]=PX"), + /* 44 */ "Affinity" OpHelp("affinity(r[P1@P2])"), + /* 45 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 46 */ "Count" OpHelp("r[P2]=count()"), + /* 47 */ "ReadCookie" OpHelp(""), + /* 48 */ "SetCookie" OpHelp(""), + /* 49 */ "VerifyCookie" OpHelp(""), + /* 50 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 51 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 52 */ "OpenAutoindex" OpHelp("nColumn=P2"), + /* 53 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 54 */ "SorterOpen" OpHelp(""), + /* 55 */ "OpenPseudo" OpHelp("content in r[P2@P3]"), + /* 56 */ "Close" OpHelp(""), + /* 57 */ "SeekLt" OpHelp("key=r[P3@P4]"), + /* 58 */ "SeekLe" OpHelp("key=r[P3@P4]"), + /* 59 */ "SeekGe" OpHelp("key=r[P3@P4]"), + /* 60 */ "SeekGt" OpHelp("key=r[P3@P4]"), + /* 61 */ "Seek" OpHelp("intkey=r[P2]"), + /* 62 */ "NoConflict" OpHelp("key=r[P3@P4]"), + /* 63 */ "NotFound" OpHelp("key=r[P3@P4]"), + /* 64 */ "Found" OpHelp("key=r[P3@P4]"), + /* 65 */ "NotExists" OpHelp("intkey=r[P3]"), + /* 66 */ "Sequence" OpHelp("r[P2]=rowid"), + /* 67 */ "NewRowid" OpHelp("r[P2]=rowid"), + /* 68 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), + /* 69 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), + /* 70 */ "Delete" OpHelp(""), + /* 71 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), + /* 72 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), + /* 73 */ "ResetCount" OpHelp(""), + /* 74 */ "SorterCompare" OpHelp("if key(P1)!=rtrim(r[P3],P4) goto P2"), + /* 75 */ "SorterData" OpHelp("r[P2]=data"), + /* 76 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), + /* 77 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), + /* 78 */ "Ne" OpHelp("if r[P1]!=r[P3] goto P2"), + /* 79 */ "Eq" OpHelp("if r[P1]==r[P3] goto P2"), + /* 80 */ "Gt" OpHelp("if r[P1]>r[P3] goto P2"), + /* 81 */ "Le" OpHelp("if r[P1]<=r[P3] goto P2"), + /* 82 */ "Lt" OpHelp("if r[P1]=r[P3] goto P2"), + /* 84 */ "RowKey" OpHelp("r[P2]=key"), + /* 85 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), + /* 86 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), + /* 87 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<>r[P1]"), + /* 89 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), + /* 90 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), + /* 91 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), + /* 92 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), + /* 93 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), + /* 94 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), + /* 95 */ "RowData" OpHelp("r[P2]=data"), + /* 96 */ "BitNot" OpHelp("r[P1]= ~r[P1]"), + /* 97 */ "String8" OpHelp("r[P2]='P4'"), + /* 98 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 99 */ "NullRow" OpHelp(""), + /* 100 */ "Last" OpHelp(""), + /* 101 */ "SorterSort" OpHelp(""), + /* 102 */ "Sort" OpHelp(""), + /* 103 */ "Rewind" OpHelp(""), + /* 104 */ "SorterInsert" OpHelp(""), + /* 105 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 106 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 107 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 108 */ "IdxLT" OpHelp("key=r[P3@P4]"), + /* 109 */ "IdxGE" OpHelp("key=r[P3@P4]"), + /* 110 */ "Destroy" OpHelp(""), + /* 111 */ "Clear" OpHelp(""), + /* 112 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"), + /* 113 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"), + /* 114 */ "ParseSchema" OpHelp(""), + /* 115 */ "LoadAnalysis" OpHelp(""), + /* 116 */ "DropTable" OpHelp(""), + /* 117 */ "DropIndex" OpHelp(""), + /* 118 */ "DropTrigger" OpHelp(""), + /* 119 */ "IntegrityCk" OpHelp(""), + /* 120 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 121 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), + /* 122 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), + /* 123 */ "Program" OpHelp(""), + /* 124 */ "Param" OpHelp(""), + /* 125 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 126 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), + /* 127 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 128 */ "IfPos" OpHelp("if r[P1]>0 goto P2"), + /* 129 */ "IfNeg" OpHelp("if r[P1]<0 goto P2"), + /* 130 */ "IfZero" OpHelp("r[P1]+=P3, if r[P1]==0 goto P2"), + /* 131 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 132 */ "IncrVacuum" OpHelp(""), + /* 133 */ "Real" OpHelp("r[P2]=P4"), + /* 134 */ "Expire" OpHelp(""), + /* 135 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 136 */ "VBegin" OpHelp(""), + /* 137 */ "VCreate" OpHelp(""), + /* 138 */ "VDestroy" OpHelp(""), + /* 139 */ "VOpen" OpHelp(""), + /* 140 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 141 */ "VNext" OpHelp(""), + /* 142 */ "VRename" OpHelp(""), + /* 143 */ "ToText" OpHelp(""), + /* 144 */ "ToBlob" OpHelp(""), + /* 145 */ "ToNumeric" OpHelp(""), + /* 146 */ "ToInt" OpHelp(""), + /* 147 */ "ToReal" OpHelp(""), + /* 148 */ "Pagecount" OpHelp(""), + /* 149 */ "MaxPgcnt" OpHelp(""), + /* 150 */ "Trace" OpHelp(""), + /* 151 */ "Noop" OpHelp(""), + /* 152 */ "Explain" OpHelp(""), }; return azName[i]; } @@ -23221,6 +23612,12 @@ struct unixFile { #endif }; +/* This variable holds the process id (pid) from when the xRandomness() +** method was called. If xOpen() is called from a different process id, +** indicating that a fork() has occurred, the PRNG will be reset. +*/ +static int randomnessPid = 0; + /* ** Allowed values for the unixFile.ctrlFlags bitmask: */ @@ -24484,6 +24881,15 @@ static int findInodeInfo( return SQLITE_OK; } +/* +** Return TRUE if pFile has been renamed or unlinked since it was first opened. +*/ +static int fileHasMoved(unixFile *pFile){ + struct stat buf; + return pFile->pInode!=0 && + (osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino); +} + /* ** Check a unixFile that is a database. Verify the following: @@ -24518,10 +24924,7 @@ static void verifyDbFile(unixFile *pFile){ pFile->ctrlFlags |= UNIXFILE_WARNED; return; } - if( pFile->pInode!=0 - && ((rc = osStat(pFile->zPath, &buf))!=0 - || buf.st_ino!=pFile->pInode->fileId.ino) - ){ + if( fileHasMoved(pFile) ){ sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); pFile->ctrlFlags |= UNIXFILE_WARNED; return; @@ -25059,7 +25462,9 @@ end_unlock: ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ +#if SQLITE_MAX_MMAP_SIZE>0 assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 ); +#endif return posixUnlock(id, eFileLock, 0); } @@ -26968,6 +27373,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } + case SQLITE_FCNTL_HAS_MOVED: { + *(int*)pArg = fileHasMoved(pFile); + return SQLITE_OK; + } #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; @@ -27248,7 +27657,7 @@ static int unixShmSystemLock( #ifdef SQLITE_DEBUG { u16 mask; OSTRACE(("SHM-LOCK ")); - mask = (1<<(ofst+n)) - (1<31 ? 0xffff : (1<<(ofst+n)) - (1<0 unixFile *pFd = (unixFile *)fd; /* The underlying database file */ UNUSED_PARAMETER(iOff); -#if SQLITE_MAX_MMAP_SIZE>0 /* If p==0 (unmap the entire file) then there must be no outstanding ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), ** then there must be at least one outstanding. */ @@ -28018,6 +28427,10 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ } assert( pFd->nFetchOut>=0 ); +#else + UNUSED_PARAMETER(fd); + UNUSED_PARAMETER(p); + UNUSED_PARAMETER(iOff); #endif return SQLITE_OK; } @@ -28808,6 +29221,16 @@ static int unixOpen( || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL ); + /* Detect a pid change and reset the PRNG. There is a race condition + ** here such that two or more threads all trying to open databases at + ** the same instant might all reset the PRNG. But multiple resets + ** are harmless. + */ + if( randomnessPid!=getpid() ){ + randomnessPid = getpid(); + sqlite3_randomness(0,0); + } + memset(p, 0, sizeof(unixFile)); if( eType==SQLITE_OPEN_MAIN_DB ){ @@ -29195,18 +29618,18 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ ** tests repeatable. */ memset(zBuf, 0, nBuf); + randomnessPid = getpid(); #if !defined(SQLITE_TEST) { - int pid, fd, got; + int fd, got; fd = robust_open("/dev/urandom", O_RDONLY, 0); if( fd<0 ){ time_t t; time(&t); memcpy(zBuf, &t, sizeof(t)); - pid = getpid(); - memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); - assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); - nBuf = sizeof(t) + sizeof(pid); + memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid)); + assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf ); + nBuf = sizeof(t) + sizeof(randomnessPid); }else{ do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR ); robust_close(0, fd, __LINE__); @@ -30884,6 +31307,41 @@ SQLITE_API int sqlite3_open_file_count = 0; must be defined." #endif +/* +** Define the required Windows SDK version constants if they are not +** already available. +*/ +#ifndef NTDDI_WIN8 +# define NTDDI_WIN8 0x06020000 +#endif + +#ifndef NTDDI_WINBLUE +# define NTDDI_WINBLUE 0x06030000 +#endif + +/* +** Check if the GetVersionEx[AW] functions should be considered deprecated +** and avoid using them in that case. It should be noted here that if the +** value of the SQLITE_WIN32_GETVERSIONEX pre-processor macro is zero +** (whether via this block or via being manually specified), that implies +** the underlying operating system will always be based on the Windows NT +** Kernel. +*/ +#ifndef SQLITE_WIN32_GETVERSIONEX +# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE +# define SQLITE_WIN32_GETVERSIONEX 0 +# else +# define SQLITE_WIN32_GETVERSIONEX 1 +# endif +#endif + +/* +** This constant should already be defined (in the "WinDef.h" SDK file). +*/ +#ifndef MAX_PATH +# define MAX_PATH (260) +#endif + /* ** Maximum pathname length (in chars) for Win32. This should normally be ** MAX_PATH. @@ -30892,17 +31350,24 @@ SQLITE_API int sqlite3_open_file_count = 0; # define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH) #endif +/* +** This constant should already be defined (in the "WinNT.h" SDK file). +*/ +#ifndef UNICODE_STRING_MAX_CHARS +# define UNICODE_STRING_MAX_CHARS (32767) +#endif + /* ** Maximum pathname length (in chars) for WinNT. This should normally be -** 32767. +** UNICODE_STRING_MAX_CHARS. */ #ifndef SQLITE_WINNT_MAX_PATH_CHARS -# define SQLITE_WINNT_MAX_PATH_CHARS (32767) +# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS) #endif /* ** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in -** characters, so we allocate 3 bytes per character assuming worst-case of +** characters, so we allocate 4 bytes per character assuming worst-case of ** 4-bytes-per-character for UTF8. */ #ifndef SQLITE_WIN32_MAX_PATH_BYTES @@ -30911,7 +31376,7 @@ SQLITE_API int sqlite3_open_file_count = 0; /* ** Maximum pathname length (in bytes) for WinNT. This should normally be -** 32767 * sizeof(WCHAR). +** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR). */ #ifndef SQLITE_WINNT_MAX_PATH_BYTES # define SQLITE_WINNT_MAX_PATH_BYTES \ @@ -30942,14 +31407,10 @@ SQLITE_API int sqlite3_open_file_count = 0; #endif /* -** Returns the string that should be used as the directory separator. +** Returns the character that should be used as the directory separator. */ -#ifndef winGetDirDep -# ifdef __CYGWIN__ -# define winGetDirDep() "/" -# else -# define winGetDirDep() "\\" -# endif +#ifndef winGetDirSep +# define winGetDirSep() '\\' #endif /* @@ -31141,30 +31602,41 @@ struct winFile { typedef struct winMemData winMemData; struct winMemData { #ifndef NDEBUG - u32 magic; /* Magic number to detect structure corruption. */ + u32 magic1; /* Magic number to detect structure corruption. */ #endif HANDLE hHeap; /* The handle to our heap. */ BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */ +#ifndef NDEBUG + u32 magic2; /* Magic number to detect structure corruption. */ +#endif }; #ifndef NDEBUG -#define WINMEM_MAGIC 0x42b2830b +#define WINMEM_MAGIC1 0x42b2830b +#define WINMEM_MAGIC2 0xbd4d7cf4 #endif static struct winMemData win_mem_data = { #ifndef NDEBUG - WINMEM_MAGIC, + WINMEM_MAGIC1, #endif NULL, FALSE +#ifndef NDEBUG + ,WINMEM_MAGIC2 +#endif }; #ifndef NDEBUG -#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC ) +#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 ) +#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 ) +#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2(); #else #define winMemAssertMagic() #endif -#define winMemGetHeap() win_mem_data.hHeap +#define winMemGetDataPtr() &win_mem_data +#define winMemGetHeap() win_mem_data.hHeap +#define winMemGetOwned() win_mem_data.bOwned static void *winMemMalloc(int nBytes); static void winMemFree(void *pPrior); @@ -31498,7 +31970,8 @@ static struct win_syscall { #define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent) -#if defined(SQLITE_WIN32_HAS_ANSI) +#if defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_GETVERSIONEX) && \ + SQLITE_WIN32_GETVERSIONEX { "GetVersionExA", (SYSCALL)GetVersionExA, 0 }, #else { "GetVersionExA", (SYSCALL)0, 0 }, @@ -31507,7 +31980,8 @@ static struct win_syscall { #define osGetVersionExA ((BOOL(WINAPI*)( \ LPOSVERSIONINFOA))aSyscall[34].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ + defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX { "GetVersionExW", (SYSCALL)GetVersionExW, 0 }, #else { "GetVersionExW", (SYSCALL)0, 0 }, @@ -31561,13 +32035,21 @@ static struct win_syscall { #define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ LPCVOID))aSyscall[42].pCurrent) +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT + { "HeapCompact", (SYSCALL)HeapCompact, 0 }, +#else + { "HeapCompact", (SYSCALL)0, 0 }, +#endif + +#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent) + #if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION) { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, #else { "LoadLibraryA", (SYSCALL)0, 0 }, #endif -#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[43].pCurrent) +#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent) #if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -31576,7 +32058,7 @@ static struct win_syscall { { "LoadLibraryW", (SYSCALL)0, 0 }, #endif -#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[44].pCurrent) +#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent) #if !SQLITE_OS_WINRT { "LocalFree", (SYSCALL)LocalFree, 0 }, @@ -31584,7 +32066,7 @@ static struct win_syscall { { "LocalFree", (SYSCALL)0, 0 }, #endif -#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[45].pCurrent) +#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent) #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT { "LockFile", (SYSCALL)LockFile, 0 }, @@ -31594,7 +32076,7 @@ static struct win_syscall { #ifndef osLockFile #define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[46].pCurrent) + DWORD))aSyscall[47].pCurrent) #endif #if !SQLITE_OS_WINCE @@ -31605,7 +32087,7 @@ static struct win_syscall { #ifndef osLockFileEx #define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[47].pCurrent) + LPOVERLAPPED))aSyscall[48].pCurrent) #endif #if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)) @@ -31615,26 +32097,26 @@ static struct win_syscall { #endif #define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - SIZE_T))aSyscall[48].pCurrent) + SIZE_T))aSyscall[49].pCurrent) { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 }, #define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \ - int))aSyscall[49].pCurrent) + int))aSyscall[50].pCurrent) { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 }, #define osQueryPerformanceCounter ((BOOL(WINAPI*)( \ - LARGE_INTEGER*))aSyscall[50].pCurrent) + LARGE_INTEGER*))aSyscall[51].pCurrent) { "ReadFile", (SYSCALL)ReadFile, 0 }, #define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[51].pCurrent) + LPOVERLAPPED))aSyscall[52].pCurrent) { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 }, -#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[52].pCurrent) +#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent) #if !SQLITE_OS_WINRT { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, @@ -31643,7 +32125,7 @@ static struct win_syscall { #endif #define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ - DWORD))aSyscall[53].pCurrent) + DWORD))aSyscall[54].pCurrent) #if !SQLITE_OS_WINRT { "Sleep", (SYSCALL)Sleep, 0 }, @@ -31651,12 +32133,12 @@ static struct win_syscall { { "Sleep", (SYSCALL)0, 0 }, #endif -#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[54].pCurrent) +#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent) { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, #define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ - LPFILETIME))aSyscall[55].pCurrent) + LPFILETIME))aSyscall[56].pCurrent) #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT { "UnlockFile", (SYSCALL)UnlockFile, 0 }, @@ -31666,7 +32148,7 @@ static struct win_syscall { #ifndef osUnlockFile #define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - DWORD))aSyscall[56].pCurrent) + DWORD))aSyscall[57].pCurrent) #endif #if !SQLITE_OS_WINCE @@ -31676,7 +32158,7 @@ static struct win_syscall { #endif #define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[57].pCurrent) + LPOVERLAPPED))aSyscall[58].pCurrent) #if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 }, @@ -31684,17 +32166,17 @@ static struct win_syscall { { "UnmapViewOfFile", (SYSCALL)0, 0 }, #endif -#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[58].pCurrent) +#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent) { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 }, #define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \ - LPCSTR,LPBOOL))aSyscall[59].pCurrent) + LPCSTR,LPBOOL))aSyscall[60].pCurrent) { "WriteFile", (SYSCALL)WriteFile, 0 }, #define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[60].pCurrent) + LPOVERLAPPED))aSyscall[61].pCurrent) #if SQLITE_OS_WINRT { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, @@ -31703,7 +32185,7 @@ static struct win_syscall { #endif #define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ - DWORD,DWORD))aSyscall[61].pCurrent) + DWORD,DWORD))aSyscall[62].pCurrent) #if !SQLITE_OS_WINRT { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, @@ -31712,7 +32194,7 @@ static struct win_syscall { #endif #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ - DWORD))aSyscall[62].pCurrent) + DWORD))aSyscall[63].pCurrent) #if SQLITE_OS_WINRT { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, @@ -31721,7 +32203,7 @@ static struct win_syscall { #endif #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ - BOOL))aSyscall[63].pCurrent) + BOOL))aSyscall[64].pCurrent) #if SQLITE_OS_WINRT { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, @@ -31730,7 +32212,7 @@ static struct win_syscall { #endif #define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ - PLARGE_INTEGER,DWORD))aSyscall[64].pCurrent) + PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent) #if SQLITE_OS_WINRT { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, @@ -31739,7 +32221,7 @@ static struct win_syscall { #endif #define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ - FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[65].pCurrent) + FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent) #if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL) { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 }, @@ -31748,7 +32230,7 @@ static struct win_syscall { #endif #define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \ - SIZE_T))aSyscall[66].pCurrent) + SIZE_T))aSyscall[67].pCurrent) #if SQLITE_OS_WINRT { "CreateFile2", (SYSCALL)CreateFile2, 0 }, @@ -31757,7 +32239,7 @@ static struct win_syscall { #endif #define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ - LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[67].pCurrent) + LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent) #if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION) { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 }, @@ -31766,7 +32248,7 @@ static struct win_syscall { #endif #define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \ - DWORD))aSyscall[68].pCurrent) + DWORD))aSyscall[69].pCurrent) #if SQLITE_OS_WINRT { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, @@ -31774,7 +32256,7 @@ static struct win_syscall { { "GetTickCount64", (SYSCALL)0, 0 }, #endif -#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[69].pCurrent) +#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent) #if SQLITE_OS_WINRT { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, @@ -31783,7 +32265,7 @@ static struct win_syscall { #endif #define osGetNativeSystemInfo ((VOID(WINAPI*)( \ - LPSYSTEM_INFO))aSyscall[70].pCurrent) + LPSYSTEM_INFO))aSyscall[71].pCurrent) #if defined(SQLITE_WIN32_HAS_ANSI) { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, @@ -31791,7 +32273,7 @@ static struct win_syscall { { "OutputDebugStringA", (SYSCALL)0, 0 }, #endif -#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[71].pCurrent) +#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent) #if defined(SQLITE_WIN32_HAS_WIDE) { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, @@ -31799,11 +32281,11 @@ static struct win_syscall { { "OutputDebugStringW", (SYSCALL)0, 0 }, #endif -#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[72].pCurrent) +#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent) { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, -#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[73].pCurrent) +#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent) #if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL) { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 }, @@ -31812,7 +32294,7 @@ static struct win_syscall { #endif #define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \ - LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[74].pCurrent) + LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent) }; /* End of the overrideable system calls */ @@ -31899,6 +32381,94 @@ static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){ return 0; } +#ifdef SQLITE_WIN32_MALLOC +/* +** If a Win32 native heap has been configured, this function will attempt to +** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one +** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The +** "pnLargest" argument, if non-zero, will be used to return the size of the +** largest committed free block in the heap, in bytes. +*/ +SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){ + int rc = SQLITE_OK; + UINT nLargest = 0; + HANDLE hHeap; + + winMemAssertMagic(); + hHeap = winMemGetHeap(); + assert( hHeap!=0 ); + assert( hHeap!=INVALID_HANDLE_VALUE ); +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); +#endif +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT + if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){ + DWORD lastErrno = osGetLastError(); + if( lastErrno==NO_ERROR ){ + sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p", + (void*)hHeap); + rc = SQLITE_NOMEM; + }else{ + sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p", + osGetLastError(), (void*)hHeap); + rc = SQLITE_ERROR; + } + } +#else + sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p", + (void*)hHeap); + rc = SQLITE_NOTFOUND; +#endif + if( pnLargest ) *pnLargest = nLargest; + return rc; +} + +/* +** If a Win32 native heap has been configured, this function will attempt to +** destroy and recreate it. If the Win32 native heap is not isolated and/or +** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will +** be returned and no changes will be made to the Win32 native heap. +*/ +SQLITE_API int sqlite3_win32_reset_heap(){ + int rc; + MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ + MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ + MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) + MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); ) + sqlite3_mutex_enter(pMaster); + sqlite3_mutex_enter(pMem); + winMemAssertMagic(); + if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){ + /* + ** At this point, there should be no outstanding memory allocations on + ** the heap. Also, since both the master and memsys locks are currently + ** being held by us, no other function (i.e. from another thread) should + ** be able to even access the heap. Attempt to destroy and recreate our + ** isolated Win32 native heap now. + */ + assert( winMemGetHeap()!=NULL ); + assert( winMemGetOwned() ); + assert( sqlite3_memory_used()==0 ); + winMemShutdown(winMemGetDataPtr()); + assert( winMemGetHeap()==NULL ); + assert( !winMemGetOwned() ); + assert( sqlite3_memory_used()==0 ); + rc = winMemInit(winMemGetDataPtr()); + assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL ); + assert( rc!=SQLITE_OK || winMemGetOwned() ); + assert( rc!=SQLITE_OK || sqlite3_memory_used()==0 ); + }else{ + /* + ** The Win32 native heap cannot be modified because it may be in use. + */ + rc = SQLITE_BUSY; + } + sqlite3_mutex_leave(pMem); + sqlite3_mutex_leave(pMaster); + return rc; +} +#endif /* SQLITE_WIN32_MALLOC */ + /* ** This function outputs the specified (ANSI) string to the Win32 debugger ** (if available). @@ -31968,11 +32538,10 @@ SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){ ** WinNT/2K/XP so that we will know whether or not we can safely call ** the LockFileEx() API. */ -#ifndef NTDDI_WIN8 -# define NTDDI_WIN8 0x06020000 -#endif -#if SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI) +#if !defined(SQLITE_WIN32_GETVERSIONEX) || !SQLITE_WIN32_GETVERSIONEX +# define osIsNT() (1) +#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI) # define osIsNT() (1) #elif !defined(SQLITE_WIN32_HAS_WIDE) # define osIsNT() (0) @@ -32007,7 +32576,7 @@ static void *winMemMalloc(int nBytes){ assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); #if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif assert( nBytes>=0 ); p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); @@ -32029,7 +32598,7 @@ static void winMemFree(void *pPrior){ assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); #if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ @@ -32050,7 +32619,7 @@ static void *winMemRealloc(void *pPrior, int nBytes){ assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); #if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif assert( nBytes>=0 ); if( !pPrior ){ @@ -32078,7 +32647,7 @@ static int winMemSize(void *p){ assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); #if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) - assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) ); #endif if( !p ) return 0; n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); @@ -32104,18 +32673,25 @@ static int winMemInit(void *pAppData){ winMemData *pWinMemData = (winMemData *)pAppData; if( !pWinMemData ) return SQLITE_ERROR; - assert( pWinMemData->magic==WINMEM_MAGIC ); + assert( pWinMemData->magic1==WINMEM_MAGIC1 ); + assert( pWinMemData->magic2==WINMEM_MAGIC2 ); #if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE if( !pWinMemData->hHeap ){ + DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE; + DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap; + if( dwMaximumSize==0 ){ + dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE; + }else if( dwInitialSize>dwMaximumSize ){ + dwInitialSize = dwMaximumSize; + } pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS, - SQLITE_WIN32_HEAP_INIT_SIZE, - SQLITE_WIN32_HEAP_MAX_SIZE); + dwInitialSize, dwMaximumSize); if( !pWinMemData->hHeap ){ sqlite3_log(SQLITE_NOMEM, - "failed to HeapCreate (%lu), flags=%u, initSize=%u, maxSize=%u", - osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, - SQLITE_WIN32_HEAP_INIT_SIZE, SQLITE_WIN32_HEAP_MAX_SIZE); + "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu", + osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize, + dwMaximumSize); return SQLITE_NOMEM; } pWinMemData->bOwned = TRUE; @@ -32146,6 +32722,9 @@ static void winMemShutdown(void *pAppData){ winMemData *pWinMemData = (winMemData *)pAppData; if( !pWinMemData ) return; + assert( pWinMemData->magic1==WINMEM_MAGIC1 ); + assert( pWinMemData->magic2==WINMEM_MAGIC2 ); + if( pWinMemData->hHeap ){ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); #if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) @@ -33788,7 +34367,7 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ return SQLITE_OK; } case SQLITE_FCNTL_VFSNAME: { - *(char**)pArg = sqlite3_mprintf("win32"); + *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName); OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } @@ -33893,7 +34472,7 @@ static void winShmEnterMutex(void){ static void winShmLeaveMutex(void){ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } -#ifdef SQLITE_DEBUG +#ifndef NDEBUG static int winShmMutexHeld(void) { return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } @@ -34772,7 +35351,7 @@ static const sqlite3_io_methods winIoMethod = { ** sqlite3_vfs object. */ -#if 0 +#if defined(__CYGWIN__) /* ** Convert a filename from whatever the underlying operating system ** supports for filenames into UTF-8. Space to hold the result is @@ -34815,12 +35394,21 @@ static void *winConvertFromUtf8Filename(const char *zFilename){ /* ** This function returns non-zero if the specified UTF-8 string buffer -** ends with a directory separator character. +** ends with a directory separator character or one was successfully +** added to it. */ -static int winEndsInDirSep(char *zBuf){ +static int winMakeEndInDirSep(int nBuf, char *zBuf){ if( zBuf ){ int nLen = sqlite3Strlen30(zBuf); - return nLen>0 && winIsDirSep(zBuf[nLen-1]); + if( nLen>0 ){ + if( winIsDirSep(zBuf[nLen-1]) ){ + return 1; + }else if( nLen+1mxPathname; - zBuf = sqlite3MallocZero( nBuf+2 ); + nMax = pVfs->mxPathname; nBuf = nMax + 2; + zBuf = sqlite3MallocZero( nBuf ); if( !zBuf ){ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); return SQLITE_IOERR_NOMEM; @@ -34858,11 +35447,21 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ ** has been explicitly set by the application; otherwise, use the one ** configured by the operating system. */ - assert( nBuf>30 ); + nDir = nMax - (nPre + 15); + assert( nDir>0 ); if( sqlite3_temp_directory ){ - sqlite3_snprintf(nBuf-30, zBuf, "%s%s", sqlite3_temp_directory, - winEndsInDirSep(sqlite3_temp_directory) ? "" : - winGetDirDep()); + int nDirLen = sqlite3Strlen30(sqlite3_temp_directory); + if( nDirLen>0 ){ + if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){ + nDirLen++; + } + if( nDirLen>nDir ){ + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); + return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0); + } + sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory); + } } #if defined(__CYGWIN__) else{ @@ -34891,8 +35490,8 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ if( zDir==0 ) continue; /* If the path starts with a drive letter followed by the colon ** character, assume it is already a native Win32 path; otherwise, - ** it must be converted to a native Win32 path prior via the Cygwin - ** API prior to using it. + ** it must be converted to a native Win32 path via the Cygwin API + ** prior to using it. */ if( winIsDriveLetterAndColon(zDir) ){ zConverted = winConvertFromUtf8Filename(zDir); @@ -34902,13 +35501,13 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ return SQLITE_IOERR_NOMEM; } if( winIsDir(zConverted) ){ - sqlite3_snprintf(nBuf-30, zBuf, "%s", zDir); + sqlite3_snprintf(nMax, zBuf, "%s", zDir); sqlite3_free(zConverted); break; } sqlite3_free(zConverted); }else{ - zConverted = sqlite3MallocZero( nBuf+1 ); + zConverted = sqlite3MallocZero( nMax+1 ); if( !zConverted ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); @@ -34916,35 +35515,29 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ } if( cygwin_conv_path( osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir, - zConverted, nBuf+1)<0 ){ + zConverted, nMax+1)<0 ){ sqlite3_free(zConverted); sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n")); return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno, - "winGetTempname1", zDir); + "winGetTempname2", zDir); } if( winIsDir(zConverted) ){ /* At this point, we know the candidate directory exists and should ** be used. However, we may need to convert the string containing ** its name into UTF-8 (i.e. if it is UTF-16 right now). */ - if( osIsNT() ){ - char *zUtf8 = winUnicodeToUtf8(zConverted); - if( !zUtf8 ){ - sqlite3_free(zConverted); - sqlite3_free(zBuf); - OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; - } - sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8); - sqlite3_free(zUtf8); + char *zUtf8 = winConvertToUtf8Filename(zConverted); + if( !zUtf8 ){ sqlite3_free(zConverted); - break; - }else{ - sqlite3_snprintf(nBuf-30, zBuf, "%s", zConverted); - sqlite3_free(zConverted); - break; + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM; } + sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); + sqlite3_free(zUtf8); + sqlite3_free(zConverted); + break; } sqlite3_free(zConverted); } @@ -34953,22 +35546,22 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ #elif !SQLITE_OS_WINRT && !defined(__CYGWIN__) else if( osIsNT() ){ char *zMulti; - LPWSTR zWidePath = sqlite3MallocZero( nBuf*sizeof(WCHAR) ); + LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) ); if( !zWidePath ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); return SQLITE_IOERR_NOMEM; } - if( osGetTempPathW(nBuf, zWidePath)==0 ){ + if( osGetTempPathW(nMax, zWidePath)==0 ){ sqlite3_free(zWidePath); sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), - "winGetTempname1", 0); + "winGetTempname2", 0); } zMulti = winUnicodeToUtf8(zWidePath); if( zMulti ){ - sqlite3_snprintf(nBuf-30, zBuf, "%s", zMulti); + sqlite3_snprintf(nMax, zBuf, "%s", zMulti); sqlite3_free(zMulti); sqlite3_free(zWidePath); }else{ @@ -34981,21 +35574,21 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ #ifdef SQLITE_WIN32_HAS_ANSI else{ char *zUtf8; - char *zMbcsPath = sqlite3MallocZero( nBuf ); + char *zMbcsPath = sqlite3MallocZero( nMax ); if( !zMbcsPath ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); return SQLITE_IOERR_NOMEM; } - if( osGetTempPathA(nBuf, zMbcsPath)==0 ){ + if( osGetTempPathA(nMax, zMbcsPath)==0 ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), - "winGetTempname2", 0); + "winGetTempname3", 0); } zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); if( zUtf8 ){ - sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8); + sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); sqlite3_free(zUtf8); }else{ sqlite3_free(zBuf); @@ -35006,18 +35599,36 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ #endif /* SQLITE_WIN32_HAS_ANSI */ #endif /* !SQLITE_OS_WINRT */ - /* Check that the output buffer is large enough for the temporary file - ** name. If it is not, return SQLITE_ERROR. + /* + ** Check to make sure the temporary directory ends with an appropriate + ** separator. If it does not and there is not enough space left to add + ** one, fail. */ - nLen = sqlite3Strlen30(zBuf); - - if( (nLen + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){ + if( !winMakeEndInDirSep(nDir+1, zBuf) ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); - return winLogError(SQLITE_ERROR, 0, "winGetTempname3", 0); + return winLogError(SQLITE_ERROR, 0, "winGetTempname4", 0); } - sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX); + /* + ** Check that the output buffer is large enough for the temporary file + ** name in the following format: + ** + ** "/etilqs_XXXXXXXXXXXXXXX\0\0" + ** + ** If not, return SQLITE_ERROR. The number 17 is used here in order to + ** account for the space used by the 15 character random suffix and the + ** two trailing NUL characters. The final directory separator character + ** has already added if it was not already present. + */ + nLen = sqlite3Strlen30(zBuf); + if( (nLen + nPre + 17) > nBuf ){ + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); + return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0); + } + + sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX); j = sqlite3Strlen30(zBuf); sqlite3_randomness(15, &zBuf[j]); @@ -35072,7 +35683,7 @@ static int winOpen( int *pOutFlags /* Status return flags */ ){ HANDLE h; - DWORD lastErrno; + DWORD lastErrno = 0; DWORD dwDesiredAccess; DWORD dwShareMode; DWORD dwCreationDisposition; @@ -35363,7 +35974,7 @@ static int winDelete( int cnt = 0; int rc; DWORD attr; - DWORD lastErrno; + DWORD lastErrno = 0; void *zConverted; UNUSED_PARAMETER(pVfs); UNUSED_PARAMETER(syncDir); @@ -35471,7 +36082,7 @@ static int winAccess( ){ DWORD attr; int rc = 0; - DWORD lastErrno; + DWORD lastErrno = 0; void *zConverted; UNUSED_PARAMETER(pVfs); @@ -35611,19 +36222,43 @@ static int winFullPathname( if( !zOut ){ return SQLITE_IOERR_NOMEM; } - if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut, - pVfs->mxPathname+1)<0 ){ + if( cygwin_conv_path( + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | + CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ sqlite3_free(zOut); return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, "winFullPathname1", zRelative); + }else{ + char *zUtf8 = winConvertToUtf8Filename(zOut); + if( !zUtf8 ){ + sqlite3_free(zOut); + return SQLITE_IOERR_NOMEM; + } + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlite3_data_directory, winGetDirSep(), zUtf8); + sqlite3_free(zUtf8); + sqlite3_free(zOut); } - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s", - sqlite3_data_directory, winGetDirDep(), zOut); - sqlite3_free(zOut); }else{ - if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){ + char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); + if( !zOut ){ + return SQLITE_IOERR_NOMEM; + } + if( cygwin_conv_path( + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), + zRelative, zOut, pVfs->mxPathname+1)<0 ){ + sqlite3_free(zOut); return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, "winFullPathname2", zRelative); + }else{ + char *zUtf8 = winConvertToUtf8Filename(zOut); + if( !zUtf8 ){ + sqlite3_free(zOut); + return SQLITE_IOERR_NOMEM; + } + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); + sqlite3_free(zUtf8); + sqlite3_free(zOut); } } return SQLITE_OK; @@ -35640,8 +36275,8 @@ static int winFullPathname( ** for converting the relative path name to an absolute ** one by prepending the data directory and a backslash. */ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s", - sqlite3_data_directory, winGetDirDep(), zRelative); + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlite3_data_directory, winGetDirSep(), zRelative); }else{ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); } @@ -35673,8 +36308,8 @@ static int winFullPathname( ** for converting the relative path name to an absolute ** one by prepending the data directory and a backslash. */ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s", - sqlite3_data_directory, winGetDirDep(), zRelative); + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlite3_data_directory, winGetDirSep(), zRelative); return SQLITE_OK; } zConverted = winConvertFromUtf8Filename(zRelative); @@ -36012,7 +36647,7 @@ SQLITE_API int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==75 ); + assert( ArraySize(aSyscall)==76 ); /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); @@ -37174,6 +37809,7 @@ struct PCache1 { struct PgHdr1 { sqlite3_pcache_page page; unsigned int iKey; /* Key value (page number) */ + u8 isPinned; /* Page in use, not on the LRU list */ PgHdr1 *pNext; /* Next in hash table chain */ PCache1 *pCache; /* Cache that currently owns this page */ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ @@ -37502,34 +38138,32 @@ static int pcache1ResizeHash(PCache1 *p){ ** LRU list, then this function is a no-op. ** ** The PGroup mutex must be held when this function is called. -** -** If pPage is NULL then this routine is a no-op. */ static void pcache1PinPage(PgHdr1 *pPage){ PCache1 *pCache; PGroup *pGroup; - if( pPage==0 ) return; + assert( pPage!=0 ); + assert( pPage->isPinned==0 ); pCache = pPage->pCache; pGroup = pCache->pGroup; + assert( pPage->pLruNext || pPage==pGroup->pLruTail ); + assert( pPage->pLruPrev || pPage==pGroup->pLruHead ); assert( sqlite3_mutex_held(pGroup->mutex) ); - if( pPage->pLruNext || pPage==pGroup->pLruTail ){ - if( pPage->pLruPrev ){ - pPage->pLruPrev->pLruNext = pPage->pLruNext; - } - if( pPage->pLruNext ){ - pPage->pLruNext->pLruPrev = pPage->pLruPrev; - } - if( pGroup->pLruHead==pPage ){ - pGroup->pLruHead = pPage->pLruNext; - } - if( pGroup->pLruTail==pPage ){ - pGroup->pLruTail = pPage->pLruPrev; - } - pPage->pLruNext = 0; - pPage->pLruPrev = 0; - pPage->pCache->nRecyclable--; + if( pPage->pLruPrev ){ + pPage->pLruPrev->pLruNext = pPage->pLruNext; + }else{ + pGroup->pLruHead = pPage->pLruNext; } + if( pPage->pLruNext ){ + pPage->pLruNext->pLruPrev = pPage->pLruPrev; + }else{ + pGroup->pLruTail = pPage->pLruPrev; + } + pPage->pLruNext = 0; + pPage->pLruPrev = 0; + pPage->isPinned = 1; + pCache->nRecyclable--; } @@ -37561,6 +38195,7 @@ static void pcache1EnforceMaxPage(PGroup *pGroup){ while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){ PgHdr1 *p = pGroup->pLruTail; assert( p->pCache->pGroup==pGroup ); + assert( p->isPinned==0 ); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); @@ -37588,7 +38223,7 @@ static void pcache1TruncateUnsafe( if( pPage->iKey>=iLimit ){ pCache->nPage--; *pp = pPage->pNext; - pcache1PinPage(pPage); + if( !pPage->isPinned ) pcache1PinPage(pPage); pcache1FreePage(pPage); }else{ pp = &pPage->pNext; @@ -37798,6 +38433,7 @@ static sqlite3_pcache_page *pcache1Fetch( PGroup *pGroup; PgHdr1 *pPage = 0; + assert( offsetof(PgHdr1,page)==0 ); assert( pCache->bPurgeable || createFlag!=1 ); assert( pCache->bPurgeable || pCache->nMin==0 ); assert( pCache->bPurgeable==0 || pCache->nMin==10 ); @@ -37811,8 +38447,11 @@ static sqlite3_pcache_page *pcache1Fetch( } /* Step 2: Abort if no existing page is found and createFlag is 0 */ - if( pPage || createFlag==0 ){ - pcache1PinPage(pPage); + if( pPage ){ + if( !pPage->isPinned ) pcache1PinPage(pPage); + goto fetch_out; + } + if( createFlag==0 ){ goto fetch_out; } @@ -37853,6 +38492,7 @@ static sqlite3_pcache_page *pcache1Fetch( )){ PCache1 *pOther; pPage = pGroup->pLruTail; + assert( pPage->isPinned==0 ); pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); pOther = pPage->pCache; @@ -37889,6 +38529,7 @@ static sqlite3_pcache_page *pcache1Fetch( pPage->pCache = pCache; pPage->pLruPrev = 0; pPage->pLruNext = 0; + pPage->isPinned = 1; *(void **)pPage->page.pExtra = 0; pCache->apHash[h] = pPage; } @@ -37898,7 +38539,7 @@ fetch_out: pCache->iMaxKey = iKey; } pcache1LeaveMutex(pGroup); - return &pPage->page; + return (sqlite3_pcache_page*)pPage; } @@ -37924,6 +38565,7 @@ static void pcache1Unpin( */ assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage ); + assert( pPage->isPinned==1 ); if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){ pcache1RemoveFromHash(pPage); @@ -37939,6 +38581,7 @@ static void pcache1Unpin( pGroup->pLruHead = pPage; } pCache->nRecyclable++; + pPage->isPinned = 0; } pcache1LeaveMutex(pCache->pGroup); @@ -38065,6 +38708,7 @@ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ #ifdef SQLITE_PCACHE_SEPARATE_HEADER nFree += sqlite3MemSize(p); #endif + assert( p->isPinned==0 ); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); @@ -38089,6 +38733,7 @@ SQLITE_PRIVATE void sqlite3PcacheStats( PgHdr1 *p; int nRecyclable = 0; for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){ + assert( p->isPinned==0 ); nRecyclable++; } *pnCurrent = pcache1.grp.nCurrentPage; @@ -39775,15 +40420,12 @@ static char *print_pager_state(Pager *p){ static int subjRequiresPage(PgHdr *pPg){ Pager *pPager = pPg->pPager; PagerSavepoint *p; - Pgno pgno; + Pgno pgno = pPg->pgno; int i; - if( pPager->nSavepoint ){ - pgno = pPg->pgno; - for(i=0; inSavepoint; i++){ - p = &pPager->aSavepoint[i]; - if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){ - return 1; - } + for(i=0; inSavepoint; i++){ + p = &pPager->aSavepoint[i]; + if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){ + return 1; } } return 0; @@ -39792,8 +40434,8 @@ static int subjRequiresPage(PgHdr *pPg){ /* ** Return true if the page is already in the journal file. */ -static int pageInJournal(PgHdr *pPg){ - return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno); +static int pageInJournal(Pager *pPager, PgHdr *pPg){ + return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno); } /* @@ -40000,6 +40642,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){ || szJ<16 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) || len>=nMaster + || len==0 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) || memcmp(aMagic, aJournalMagic, 8) @@ -40436,7 +41079,7 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ ** already in memory. */ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ - PgHdr *p; /* Return value */ + PgHdr *p = 0; /* Return value */ /* It is not possible for a call to PcacheFetch() with createFlag==0 to ** fail, since no attempt to allocate dynamic memory will be made. @@ -40740,7 +41383,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ PgHdr *p = pager_lookup(pPager, 1); if( p ){ p->pageHash = 0; - sqlite3PagerUnref(p); + sqlite3PagerUnrefNotNull(p); } } #endif @@ -40769,6 +41412,11 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ rc = pager_truncate(pPager, pPager->dbSize); } + if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){ + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; + } + if( !pPager->exclusiveMode && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) ){ @@ -41582,7 +42230,7 @@ end_playback: if( rc==SQLITE_OK && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ - rc = sqlite3PagerSync(pPager); + rc = sqlite3PagerSync(pPager, 0); } if( rc==SQLITE_OK ){ rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0); @@ -41728,7 +42376,7 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){ if( rc==SQLITE_OK ){ pPager->xReiniter(pPg); } - sqlite3PagerUnref(pPg); + sqlite3PagerUnrefNotNull(pPg); } } @@ -43083,7 +43731,7 @@ static int subjournalPage(PgHdr *pPg){ assert( isOpen(pPager->jfd) || pagerUseWal(pPager) ); assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 ); assert( pagerUseWal(pPager) - || pageInJournal(pPg) + || pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize ); rc = openSubJournal(pPager); @@ -43548,6 +44196,30 @@ SQLITE_PRIVATE int sqlite3PagerOpen( } +/* Verify that the database file has not be deleted or renamed out from +** under the pager. Return SQLITE_OK if the database is still were it ought +** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error +** code from sqlite3OsAccess()) if the database has gone missing. +*/ +static int databaseIsUnmoved(Pager *pPager){ + int bHasMoved = 0; + int rc; + + if( pPager->tempFile ) return SQLITE_OK; + if( pPager->dbSize==0 ) return SQLITE_OK; + assert( pPager->zFilename && pPager->zFilename[0] ); + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); + if( rc==SQLITE_NOTFOUND ){ + /* If the HAS_MOVED file-control is unimplemented, assume that the file + ** has not been moved. That is the historical behavior of SQLite: prior to + ** version 3.8.3, it never checked */ + rc = SQLITE_OK; + }else if( rc==SQLITE_OK && bHasMoved ){ + rc = SQLITE_READONLY_DBMOVED; + } + return rc; +} + /* ** This function is called after transitioning from PAGER_UNLOCK to @@ -44019,7 +44691,7 @@ SQLITE_PRIVATE int sqlite3PagerAcquire( if( rc!=SQLITE_OK ) goto pager_acquire_err; } - if( iFrame==0 && bMmapOk ){ + if( bMmapOk && iFrame==0 ){ void *pData = 0; rc = sqlite3OsFetch(pPager->fd, @@ -44160,16 +44832,19 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ ** are released, a rollback occurs and the lock on the database is ** removed. */ -SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ - if( pPg ){ - Pager *pPager = pPg->pPager; - if( pPg->flags & PGHDR_MMAP ){ - pagerReleaseMapPage(pPg); - }else{ - sqlite3PcacheRelease(pPg); - } - pagerUnlockIfUnused(pPager); +SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){ + Pager *pPager; + assert( pPg!=0 ); + pPager = pPg->pPager; + if( pPg->flags & PGHDR_MMAP ){ + pagerReleaseMapPage(pPg); + }else{ + sqlite3PcacheRelease(pPg); } + pagerUnlockIfUnused(pPager); +} +SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ + if( pPg ) sqlite3PagerUnrefNotNull(pPg); } /* @@ -44224,13 +44899,19 @@ static int pager_open_journal(Pager *pPager){ (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL): (SQLITE_OPEN_MAIN_JOURNAL) ); - #ifdef SQLITE_ENABLE_ATOMIC_WRITE - rc = sqlite3JournalOpen( - pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) - ); - #else - rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); - #endif + + /* Verify that the database still has the same name as it did when + ** it was originally opened. */ + rc = databaseIsUnmoved(pPager); + if( rc==SQLITE_OK ){ +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + rc = sqlite3JournalOpen( + pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) + ); +#else + rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); +#endif + } } assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); } @@ -44351,9 +45032,9 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory ** of any open savepoints as appropriate. */ static int pager_write(PgHdr *pPg){ - void *pData = pPg->pData; Pager *pPager = pPg->pPager; int rc = SQLITE_OK; + int inJournal; /* This routine is not called unless a write-transaction has already ** been started. The journal file may or may not be open at this point. @@ -44364,14 +45045,8 @@ static int pager_write(PgHdr *pPg){ || pPager->eState==PAGER_WRITER_DBMOD ); assert( assert_pager_state(pPager) ); - - /* If an error has been previously detected, report the same error - ** again. This should not happen, but the check provides robustness. */ - if( NEVER(pPager->errCode) ) return pPager->errCode; - - /* Higher-level routines never call this function if database is not - ** writable. But check anyway, just for robustness. */ - if( NEVER(pPager->readOnly) ) return SQLITE_PERM; + assert( pPager->errCode==0 ); + assert( pPager->readOnly==0 ); CHECK_PAGE(pPg); @@ -44395,7 +45070,8 @@ static int pager_write(PgHdr *pPg){ ** to the journal then we can return right away. */ sqlite3PcacheMakeDirty(pPg); - if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ + inJournal = pageInJournal(pPager, pPg); + if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){ assert( !pagerUseWal(pPager) ); }else{ @@ -44403,7 +45079,7 @@ static int pager_write(PgHdr *pPg){ ** EXCLUSIVE lock on the main database file. Write the current page to ** the transaction journal if it is not there already. */ - if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){ + if( !inJournal && !pagerUseWal(pPager) ){ assert( pagerUseWal(pPager)==0 ); if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){ u32 cksum; @@ -44416,7 +45092,7 @@ static int pager_write(PgHdr *pPg){ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); assert( pPager->journalHdr<=pPager->journalOff ); - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); + CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); cksum = pager_cksum(pPager, (u8*)pData2); /* Even if an IO or diskfull error occurs while journalling the @@ -44468,7 +45144,7 @@ static int pager_write(PgHdr *pPg){ ** the statement journal format differs from the standard journal format ** in that it omits the checksums and the header. */ - if( subjRequiresPage(pPg) ){ + if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){ rc = subjournalPage(pPg); } } @@ -44500,19 +45176,19 @@ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){ PgHdr *pPg = pDbPage; Pager *pPager = pPg->pPager; - Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); assert( (pPg->flags & PGHDR_MMAP)==0 ); assert( pPager->eState>=PAGER_WRITER_LOCKED ); assert( pPager->eState!=PAGER_ERROR ); assert( assert_pager_state(pPager) ); - if( nPagePerSector>1 ){ + if( pPager->sectorSize > (u32)pPager->pageSize ){ Pgno nPageCount; /* Total number of pages in database file */ Pgno pg1; /* First page of the sector pPg is located on. */ int nPage = 0; /* Number of pages starting at pg1 to journal */ int ii; /* Loop counter */ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */ + Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow ** a journal header to be written between the pages journaled by @@ -44551,14 +45227,14 @@ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){ if( pPage->flags&PGHDR_NEED_SYNC ){ needSync = 1; } - sqlite3PagerUnref(pPage); + sqlite3PagerUnrefNotNull(pPage); } } }else if( (pPage = pager_lookup(pPager, pg))!=0 ){ if( pPage->flags&PGHDR_NEED_SYNC ){ needSync = 1; } - sqlite3PagerUnref(pPage); + sqlite3PagerUnrefNotNull(pPage); } } @@ -44574,7 +45250,7 @@ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){ PgHdr *pPage = pager_lookup(pPager, pg1+ii); if( pPage ){ pPage->flags |= PGHDR_NEED_SYNC; - sqlite3PagerUnref(pPage); + sqlite3PagerUnrefNotNull(pPage); } } } @@ -44727,17 +45403,17 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ ** If successful, or if called on a pager for which it is a no-op, this ** function returns SQLITE_OK. Otherwise, an IO error code is returned. */ -SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){ +SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){ int rc = SQLITE_OK; - if( !pPager->noSync ){ + + if( isOpen(pPager->fd) ){ + void *pArg = (void*)zMaster; + rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); + if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; + } + if( rc==SQLITE_OK && !pPager->noSync ){ assert( !MEMDB ); rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); - }else if( isOpen(pPager->fd) ){ - assert( !MEMDB ); - rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, 0); - if( rc==SQLITE_NOTFOUND ){ - rc = SQLITE_OK; - } } return rc; } @@ -44936,7 +45612,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( /* Finally, sync the database file. */ if( !noSync ){ - rc = sqlite3PagerSync(pPager); + rc = sqlite3PagerSync(pPager, zMaster); } IOTRACE(("DBSYNC %p\n", pPager)) } @@ -45065,7 +45741,9 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){ assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT - || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR ); + || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR + || rc==SQLITE_CANTOPEN + ); /* If an error occurs during a ROLLBACK, we can no longer trust the pager ** cache. So call pager_error() on the way out to make any error persistent. @@ -45468,7 +46146,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ needSyncPgno = pPg->pgno; assert( pPager->journalMode==PAGER_JOURNALMODE_OFF || - pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); + pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize ); assert( pPg->flags&PGHDR_DIRTY ); } @@ -45502,7 +46180,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i if( MEMDB ){ assert( pPgOld ); sqlite3PcacheMove(pPgOld, origPgno); - sqlite3PagerUnref(pPgOld); + sqlite3PagerUnrefNotNull(pPgOld); } if( needSyncPgno ){ @@ -45531,7 +46209,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i } pPgHdr->flags |= PGHDR_NEED_SYNC; sqlite3PcacheMakeDirty(pPgHdr); - sqlite3PagerUnref(pPgHdr); + sqlite3PagerUnrefNotNull(pPgHdr); } return SQLITE_OK; @@ -50682,7 +51360,7 @@ static int btreeMoveto( ){ int rc; /* Status code */ UnpackedRecord *pIdxKey; /* Unpacked index key */ - char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ + char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */ char *pFree = 0; if( pKey ){ @@ -50692,6 +51370,10 @@ static int btreeMoveto( ); if( pIdxKey==0 ) return SQLITE_NOMEM; sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); + if( pIdxKey->nField==0 ){ + sqlite3DbFree(pCur->pKeyInfo->db, pFree); + return SQLITE_CORRUPT_BKPT; + } }else{ pIdxKey = 0; } @@ -51646,7 +52328,7 @@ static int getAndInitPage( rc = SQLITE_CORRUPT_BKPT; }else{ rc = btreeGetPage(pBt, pgno, ppPage, bReadonly); - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){ rc = btreeInitPage(*ppPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); @@ -51667,10 +52349,11 @@ static void releasePage(MemPage *pPage){ if( pPage ){ assert( pPage->aData ); assert( pPage->pBt ); + assert( pPage->pDbPage!=0 ); assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - sqlite3PagerUnref(pPage->pDbPage); + sqlite3PagerUnrefNotNull(pPage->pDbPage); } } @@ -53747,7 +54430,7 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){ int iPage = pCur->iPage; memset(&info, 0, sizeof(info)); btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); - assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); + assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 ); } #else #define assertCellInfo(x) @@ -54186,10 +54869,10 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p /* ** Return a pointer to payload information from the entry that the ** pCur cursor is pointing to. The pointer is to the beginning of -** the key if skipKey==0 and it points to the beginning of data if -** skipKey==1. The number of bytes of available key/data is written -** into *pAmt. If *pAmt==0, then the value returned will not be -** a valid pointer. +** the key if index btrees (pPage->intKey==0) and is the data for +** table btrees (pPage->intKey==1). The number of bytes of available +** key/data is written into *pAmt. If *pAmt==0, then the value +** returned will not be a valid pointer. ** ** This routine is an optimization. It is common for the entire key ** and data to fit on the local page and for there to be no overflow @@ -54202,41 +54885,21 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p ** page of the database. The data might change or move the next time ** any btree routine is called. */ -static const unsigned char *fetchPayload( +static const void *fetchPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ - int *pAmt, /* Write the number of available bytes here */ - int skipKey /* read beginning at data if this is true */ + u32 *pAmt /* Write the number of available bytes here */ ){ - unsigned char *aPayload; - MemPage *pPage; - u32 nKey; - u32 nLocal; - assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); assert( pCur->eState==CURSOR_VALID ); + assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); - pPage = pCur->apPage[pCur->iPage]; - assert( pCur->aiIdx[pCur->iPage]nCell ); - if( NEVER(pCur->info.nSize==0) ){ + assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); + if( pCur->info.nSize==0 ){ btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage], &pCur->info); } - aPayload = pCur->info.pCell; - aPayload += pCur->info.nHeader; - if( pPage->intKey ){ - nKey = 0; - }else{ - nKey = (int)pCur->info.nKey; - } - if( skipKey ){ - aPayload += nKey; - nLocal = pCur->info.nLocal - nKey; - }else{ - nLocal = pCur->info.nLocal; - assert( nLocal<=nKey ); - } - *pAmt = nLocal; - return aPayload; + *pAmt = pCur->info.nLocal; + return (void*)(pCur->info.pCell + pCur->info.nHeader); } @@ -54254,23 +54917,11 @@ static const unsigned char *fetchPayload( ** These routines is used to get quick access to key and data ** in the common case where no overflow pages are used. */ -SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ - const void *p = 0; - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorHoldsMutex(pCur) ); - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - p = (const void*)fetchPayload(pCur, pAmt, 0); - } - return p; +SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){ + return fetchPayload(pCur, pAmt); } -SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ - const void *p = 0; - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - assert( cursorHoldsMutex(pCur) ); - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - p = (const void*)fetchPayload(pCur, pAmt, 1); - } - return p; +SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){ + return fetchPayload(pCur, pAmt); } @@ -54389,8 +55040,6 @@ static void moveToParent(BtCursor *pCur){ static int moveToRoot(BtCursor *pCur){ MemPage *pRoot; int rc = SQLITE_OK; - Btree *p = pCur->pBtree; - BtShared *pBt = p->pBt; assert( cursorHoldsMutex(pCur) ); assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); @@ -54405,56 +55054,52 @@ static int moveToRoot(BtCursor *pCur){ } if( pCur->iPage>=0 ){ - int i; - for(i=1; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - } - pCur->iPage = 0; + while( pCur->iPage ) releasePage(pCur->apPage[pCur->iPage--]); }else if( pCur->pgnoRoot==0 ){ pCur->eState = CURSOR_INVALID; return SQLITE_OK; }else{ - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0], + rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0], pCur->wrFlag==0 ? PAGER_GET_READONLY : 0); if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; return rc; } pCur->iPage = 0; - - /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor - ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is - ** NULL, the caller expects a table b-tree. If this is not the case, - ** return an SQLITE_CORRUPT error. */ - assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 ); - if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } } - - /* Assert that the root page is of the correct type. This must be the - ** case as the call to this function that loaded the root-page (either - ** this call or a previous invocation) would have detected corruption - ** if the assumption were not true, and it is not possible for the flags - ** byte to have been modified while this cursor is holding a reference - ** to the page. */ pRoot = pCur->apPage[0]; assert( pRoot->pgno==pCur->pgnoRoot ); - assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey ); + + /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor + ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is + ** NULL, the caller expects a table b-tree. If this is not the case, + ** return an SQLITE_CORRUPT error. + ** + ** Earlier versions of SQLite assumed that this test could not fail + ** if the root page was already loaded when this function was called (i.e. + ** if pCur->iPage>=0). But this is not so if the database is corrupted + ** in such a way that page pRoot is linked into a second b-tree table + ** (or the freelist). */ + assert( pRoot->intKey==1 || pRoot->intKey==0 ); + if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){ + return SQLITE_CORRUPT_BKPT; + } pCur->aiIdx[0] = 0; pCur->info.nSize = 0; pCur->atLast = 0; pCur->validNKey = 0; - if( pRoot->nCell==0 && !pRoot->leaf ){ + if( pRoot->nCell>0 ){ + pCur->eState = CURSOR_VALID; + }else if( !pRoot->leaf ){ Pgno subpage; if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); pCur->eState = CURSOR_VALID; rc = moveToChild(pCur, subpage); }else{ - pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID); + pCur->eState = CURSOR_INVALID; } return rc; } @@ -54645,10 +55290,10 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( } assert( pCur->apPage[0]->intKey || pIdxKey ); for(;;){ - int lwr, upr, idx; + int lwr, upr, idx, c; Pgno chldPg; MemPage *pPage = pCur->apPage[pCur->iPage]; - int c; + u8 *pCell; /* Pointer to current cell in pPage */ /* pPage->nCell must be greater than zero. If this is the root-page ** the cursor would have been INVALID above and this for(;;) loop @@ -54660,35 +55305,47 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( assert( pPage->intKey==(pIdxKey==0) ); lwr = 0; upr = pPage->nCell-1; - if( biasRight ){ - pCur->aiIdx[pCur->iPage] = (u16)(idx = upr); - }else{ - pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2); - } - for(;;){ - u8 *pCell; /* Pointer to current cell in pPage */ - - assert( idx==pCur->aiIdx[pCur->iPage] ); - pCur->info.nSize = 0; - pCell = findCell(pPage, idx) + pPage->childPtrSize; - if( pPage->intKey ){ + assert( biasRight==0 || biasRight==1 ); + idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ + pCur->aiIdx[pCur->iPage] = (u16)idx; + if( pPage->intKey ){ + for(;;){ i64 nCellKey; + pCell = findCell(pPage, idx) + pPage->childPtrSize; if( pPage->hasData ){ - u32 dummy; - pCell += getVarint32(pCell, dummy); + while( 0x80 <= *(pCell++) ){ + if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT; + } } getVarint(pCell, (u64*)&nCellKey); - if( nCellKey==intKey ){ - c = 0; - }else if( nCellKeyupr ){ c = -1; break; } + }else if( nCellKey>intKey ){ + upr = idx-1; + if( lwr>upr ){ c = +1; break; } }else{ - assert( nCellKey>intKey ); - c = +1; + assert( nCellKey==intKey ); + pCur->validNKey = 1; + pCur->info.nKey = nCellKey; + pCur->aiIdx[pCur->iPage] = (u16)idx; + if( !pPage->leaf ){ + lwr = idx; + goto moveto_next_layer; + }else{ + *pRes = 0; + rc = SQLITE_OK; + goto moveto_finish; + } } - pCur->validNKey = 1; - pCur->info.nKey = nCellKey; - }else{ + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */ + } + }else{ + for(;;){ + int nCell; + pCell = findCell(pPage, idx) + pPage->childPtrSize; + /* The maximum supported page-size is 65536 bytes. This means that ** the maximum number of record bytes stored on an index B-Tree ** page is less than 16384 bytes and may be stored as a 2-byte @@ -54697,10 +55354,8 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( ** stored entirely within the b-tree page by inspecting the first ** 2 bytes of the cell. */ - int nCell = pCell[0]; - if( nCell<=pPage->max1bytePayload - /* && (pCell+nCell)aDataEnd */ - ){ + nCell = pCell[0]; + if( nCell<=pPage->max1bytePayload ){ /* This branch runs if the record-size field of the cell is a ** single byte varint and the record fits entirely on the main ** b-tree page. */ @@ -54708,7 +55363,6 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey); }else if( !(pCell[1] & 0x80) && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal - /* && (pCell+nCell+2)<=pPage->aDataEnd */ ){ /* The record-size field is a 2 byte varint and the record ** fits entirely on the main b-tree page. */ @@ -54728,6 +55382,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( rc = SQLITE_NOMEM; goto moveto_finish; } + pCur->aiIdx[pCur->iPage] = (u16)idx; rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); if( rc ){ sqlite3_free(pCellKey); @@ -54736,49 +55391,44 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); } - } - if( c==0 ){ - if( pPage->intKey && !pPage->leaf ){ - lwr = idx; - break; + if( c<0 ){ + lwr = idx+1; + }else if( c>0 ){ + upr = idx-1; }else{ + assert( c==0 ); *pRes = 0; rc = SQLITE_OK; + pCur->aiIdx[pCur->iPage] = (u16)idx; goto moveto_finish; } + if( lwr>upr ) break; + assert( lwr+upr>=0 ); + idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */ } - if( c<0 ){ - lwr = idx+1; - }else{ - upr = idx-1; - } - if( lwr>upr ){ - break; - } - pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2); } assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) ); assert( pPage->isInit ); if( pPage->leaf ){ - chldPg = 0; - }else if( lwr>=pPage->nCell ){ - chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); - }else{ - chldPg = get4byte(findCell(pPage, lwr)); - } - if( chldPg==0 ){ assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); + pCur->aiIdx[pCur->iPage] = (u16)idx; *pRes = c; rc = SQLITE_OK; goto moveto_finish; } +moveto_next_layer: + if( lwr>=pPage->nCell ){ + chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]); + }else{ + chldPg = get4byte(findCell(pPage, lwr)); + } pCur->aiIdx[pCur->iPage] = (u16)lwr; - pCur->info.nSize = 0; - pCur->validNKey = 0; rc = moveToChild(pCur, chldPg); - if( rc ) goto moveto_finish; + if( rc ) break; } moveto_finish: + pCur->info.nSize = 0; + pCur->validNKey = 0; return rc; } @@ -55275,6 +55925,7 @@ end_allocate_page: if( rc==SQLITE_OK ){ if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){ releasePage(*ppPage); + *ppPage = 0; return SQLITE_CORRUPT_BKPT; } (*ppPage)->isInit = 0; @@ -55536,7 +56187,7 @@ static int fillInCell( nHeader += 4; } if( pPage->hasData ){ - nHeader += putVarint(&pCell[nHeader], nData+nZero); + nHeader += putVarint32(&pCell[nHeader], nData+nZero); }else{ nData = nZero = 0; } @@ -55664,7 +56315,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ u32 pc; /* Offset to cell content of cell being deleted */ u8 *data; /* pPage->aData */ u8 *ptr; /* Used to move bytes around within data[] */ - u8 *endPtr; /* End of loop */ int rc; /* The return code */ int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ @@ -55689,13 +56339,8 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ *pRC = rc; return; } - endPtr = &pPage->aCellIdx[2*pPage->nCell - 2]; - assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ - while( ptrnCell--; + memmove(ptr, ptr+2, 2*(pPage->nCell - idx)); put2byte(&data[hdr+3], pPage->nCell); pPage->nFree += 2; } @@ -55732,9 +56377,6 @@ static void insertCell( int ins; /* Index in data[] where new cell pointer is inserted */ int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ - u8 *ptr; /* Used for moving information around in data[] */ - u8 *endPtr; /* End of the loop */ - int nSkip = (iChild ? 4 : 0); if( *pRC ) return; @@ -55785,13 +56427,7 @@ static void insertCell( if( iChild ){ put4byte(&data[idx], iChild); } - ptr = &data[end]; - endPtr = &data[ins]; - assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ - while( ptr>endPtr ){ - *(u16*)ptr = *(u16*)&ptr[-2]; - ptr -= 2; - } + memmove(&data[ins+2], &data[ins], end-ins); put2byte(&data[ins], idx); put2byte(&data[pPage->hdrOffset+3], pPage->nCell); #ifndef SQLITE_OMIT_AUTOVACUUM @@ -57389,6 +58025,7 @@ static int clearDatabasePage( int rc; unsigned char *pCell; int i; + int hdr; assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ @@ -57397,6 +58034,7 @@ static int clearDatabasePage( rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; + hdr = pPage->hdrOffset; for(i=0; inCell; i++){ pCell = findCell(pPage, i); if( !pPage->leaf ){ @@ -57407,7 +58045,7 @@ static int clearDatabasePage( if( rc ) goto cleardatabasepage_out; } if( !pPage->leaf ){ - rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange); + rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange); if( rc ) goto cleardatabasepage_out; }else if( pnChange ){ assert( pPage->intKey ); @@ -57416,7 +58054,7 @@ static int clearDatabasePage( if( freePageFlag ){ freePage(pPage, &rc); }else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){ - zeroPage(pPage, pPage->aData[0] | PTF_LEAF); + zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF); } cleardatabasepage_out: @@ -57753,7 +58391,7 @@ static void checkAppendMsg( sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1); } if( zMsg1 ){ - sqlite3StrAccumAppend(&pCheck->errMsg, zMsg1, -1); + sqlite3StrAccumAppendAll(&pCheck->errMsg, zMsg1); } sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap); va_end(ap); @@ -58617,6 +59255,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ rc = SQLITE_ERROR; } sqlite3DbFree(pErrorDb, pParse->zErrMsg); + sqlite3ParserReset(pParse); sqlite3StackFree(pErrorDb, pParse); } if( rc ){ @@ -59046,7 +59685,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ /* Sync the database file to disk. */ if( rc==SQLITE_OK ){ - rc = sqlite3PagerSync(pDestPager); + rc = sqlite3PagerSync(pDestPager, 0); } }else{ sqlite3PagerTruncateImage(pDestPager, nDestTruncate); @@ -59121,10 +59760,10 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ /* Set the error code of the destination database handle. */ rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; - sqlite3Error(p->pDestDb, rc, 0); - - /* Exit the mutexes and free the backup context structure. */ if( p->pDestDb ){ + sqlite3Error(p->pDestDb, rc, 0); + + /* Exit the mutexes and free the backup context structure. */ sqlite3LeaveMutexAndCloseZombie(p->pDestDb); } sqlite3BtreeLeave(p->pSrc); @@ -59328,18 +59967,14 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ /* ** Make sure pMem->z points to a writable allocation of at least -** n bytes. +** min(n,32) bytes. ** -** If the third argument passed to this function is true, then memory -** cell pMem must contain a string or blob. In this case the content is -** preserved. Otherwise, if the third parameter to this function is false, -** any current string or blob value may be discarded. -** -** This function sets the MEM_Dyn flag and clears any xDel callback. -** It also clears MEM_Ephem and MEM_Static. If the preserve flag is -** not set, Mem.n is zeroed. +** If the bPreserve argument is true, then copy of the content of +** pMem->z into the new allocation. pMem must be either a string or +** blob if bPreserve is true. If bPreserve is false, any prior content +** in pMem->z is discarded. */ -SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve){ +SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ assert( 1 >= ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) + (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) + @@ -59348,37 +59983,39 @@ SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve){ ); assert( (pMem->flags&MEM_RowSet)==0 ); - /* If the preserve flag is set to true, then the memory cell must already + /* If the bPreserve flag is set to true, then the memory cell must already ** contain a valid string or blob value. */ - assert( preserve==0 || pMem->flags&(MEM_Blob|MEM_Str) ); + assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) ); + testcase( bPreserve && pMem->z==0 ); - if( n<32 ) n = 32; - if( sqlite3DbMallocSize(pMem->db, pMem->zMalloc)z==pMem->zMalloc ){ + if( pMem->zMalloc==0 || sqlite3DbMallocSize(pMem->db, pMem->zMalloc)z==pMem->zMalloc ){ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n); - preserve = 0; + bPreserve = 0; }else{ sqlite3DbFree(pMem->db, pMem->zMalloc); pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n); } + if( pMem->zMalloc==0 ){ + sqlite3VdbeMemRelease(pMem); + pMem->flags = MEM_Null; + return SQLITE_NOMEM; + } } - if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){ + if( pMem->z && bPreserve && pMem->z!=pMem->zMalloc ){ memcpy(pMem->zMalloc, pMem->z, pMem->n); } - if( pMem->flags&MEM_Dyn && pMem->xDel ){ + if( (pMem->flags&MEM_Dyn)!=0 && pMem->xDel ){ assert( pMem->xDel!=SQLITE_DYNAMIC ); pMem->xDel((void *)(pMem->z)); } pMem->z = pMem->zMalloc; - if( pMem->z==0 ){ - pMem->flags = MEM_Null; - }else{ - pMem->flags &= ~(MEM_Ephem|MEM_Static); - } + pMem->flags &= ~(MEM_Ephem|MEM_Static); pMem->xDel = 0; - return (pMem->z ? SQLITE_OK : SQLITE_NOMEM); + return SQLITE_OK; } /* @@ -59564,23 +60201,18 @@ SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){ */ SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){ VdbeMemRelease(p); - sqlite3DbFree(p->db, p->zMalloc); + if( p->zMalloc ){ + sqlite3DbFree(p->db, p->zMalloc); + p->zMalloc = 0; + } p->z = 0; - p->zMalloc = 0; - p->xDel = 0; + assert( p->xDel==0 ); /* Zeroed by VdbeMemRelease() above */ } /* ** Convert a 64-bit IEEE double into a 64-bit signed integer. -** If the double is too large, return 0x8000000000000000. -** -** Most systems appear to do this simply by assigning -** variables and without the extra range tests. But -** there are reports that windows throws an expection -** if the floating point value is out of range. (See ticket #2880.) -** Because we do not completely understand the problem, we will -** take the conservative approach and always do range tests -** before attempting the conversion. +** If the double is out of range of a 64-bit signed integer then +** return the closest available 64-bit signed integer. */ static i64 doubleToInt64(double r){ #ifdef SQLITE_OMIT_FLOATING_POINT @@ -59597,14 +60229,10 @@ static i64 doubleToInt64(double r){ static const i64 maxInt = LARGEST_INT64; static const i64 minInt = SMALLEST_INT64; - if( r<(double)minInt ){ - return minInt; - }else if( r>(double)maxInt ){ - /* minInt is correct here - not maxInt. It turns out that assigning - ** a very large positive number to an integer results in a very large - ** negative integer. This makes no sense, but it is what x86 hardware - ** does so for compatibility we will do the same in software. */ + if( r<=(double)minInt ){ return minInt; + }else if( r>=(double)maxInt ){ + return maxInt; }else{ return (i64)r; } @@ -59686,17 +60314,11 @@ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){ ** ** The second and third terms in the following conditional enforces ** the second condition under the assumption that addition overflow causes - ** values to wrap around. On x86 hardware, the third term is always - ** true and could be omitted. But we leave it in because other - ** architectures might behave differently. + ** values to wrap around. */ if( pMem->r==(double)pMem->u.i && pMem->u.i>SMALLEST_INT64 -#if defined(__i486__) || defined(__x86_64__) - && ALWAYS(pMem->u.iu.iflags |= MEM_Int; } @@ -59768,6 +60390,9 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){ MemSetTypeFlag(pMem, MEM_Null); pMem->type = SQLITE_NULL; } +SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){ + sqlite3VdbeMemSetNull((Mem*)p); +} /* ** Delete any previous value and set the value to be a BLOB of length @@ -59881,7 +60506,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ /* ** Size of struct Mem not including the Mem.zMalloc member. */ -#define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc)) +#define MEMCELLSIZE offsetof(Mem,zMalloc) /* ** Make an shallow copy of pFrom into pTo. Prior contents of @@ -60165,13 +60790,13 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C */ SQLITE_PRIVATE int sqlite3VdbeMemFromBtree( BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - int offset, /* Offset from the start of data to return bytes from. */ - int amt, /* Number of bytes to return. */ + u32 offset, /* Offset from the start of data to return bytes from. */ + u32 amt, /* Number of bytes to return. */ int key, /* If true, retrieve from the btree key, not data. */ Mem *pMem /* OUT: Return data in this Mem structure. */ ){ char *zData; /* Data from the btree layer */ - int available = 0; /* Number of bytes available on the local btree page */ + u32 available = 0; /* Number of bytes available on the local btree page */ int rc = SQLITE_OK; /* Return code */ assert( sqlite3BtreeCursorIsValid(pCur) ); @@ -60186,7 +60811,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemFromBtree( } assert( zData!=0 ); - if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){ + if( offset+amt<=available ){ sqlite3VdbeMemRelease(pMem); pMem->z = &zData[offset]; pMem->flags = MEM_Blob|MEM_Ephem; @@ -60205,7 +60830,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemFromBtree( sqlite3VdbeMemRelease(pMem); } } - pMem->n = amt; + pMem->n = (int)amt; return rc; } @@ -60300,17 +60925,17 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ Index *pIdx = p->pIdx; /* Index being probed */ int nByte; /* Bytes of space to allocate */ int i; /* Counter variable */ - int nCol = pIdx->nColumn+1; /* Number of index columns including rowid */ + int nCol = pIdx->nColumn; /* Number of index columns including rowid */ - nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord); + nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord)); pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); if( pRec ){ - pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx); + pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx); if( pRec->pKeyInfo ){ - assert( pRec->pKeyInfo->nField+1==nCol ); - pRec->pKeyInfo->enc = ENC(db); + assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol ); + assert( pRec->pKeyInfo->enc==ENC(db) ); pRec->flags = UNPACKED_PREFIX_MATCH; - pRec->aMem = (Mem *)&pRec[1]; + pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord))); for(i=0; iaMem[i].flags = MEM_Null; pRec->aMem[i].type = SQLITE_NULL; @@ -60364,16 +60989,7 @@ static int valueFromExpr( return SQLITE_OK; } op = pExpr->op; - - /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT4. - ** The ifdef here is to enable us to achieve 100% branch test coverage even - ** when SQLITE_ENABLE_STAT4 is omitted. - */ -#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - if( op==TK_REGISTER ) op = pExpr->op2; -#else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; -#endif /* Handle negative integers in a single step. This is needed in the ** case when the value is -9223372036854775808. @@ -60514,7 +61130,7 @@ static void recordFunc( }else{ aRet[0] = nSerial+1; sqlite3PutVarint(&aRet[1], iSerial); - sqlite3VdbeSerialPut(&aRet[1+nSerial], nVal, argv[0], file_format); + sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial); sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT); sqlite3DbFree(db, aRet); } @@ -60592,10 +61208,9 @@ SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue( pVal = valueNew(db, &alloc); if( pVal ){ sqlite3VdbeMemSetNull((Mem*)pVal); - *pbOk = 1; } }else if( pExpr->op==TK_VARIABLE - || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) + || NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) ){ Vdbe *v; int iBindVar = pExpr->iColumn; @@ -60608,16 +61223,13 @@ SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue( sqlite3ValueApplyAffinity(pVal, affinity, ENC(db)); } pVal->db = pParse->db; - *pbOk = 1; sqlite3VdbeMemStoreType((Mem*)pVal); } - }else{ - *pbOk = 0; } }else{ rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc); - *pbOk = (pVal!=0); } + *pbOk = (pVal!=0); assert( pVal==0 || pVal->db==db ); return rc; @@ -60631,13 +61243,13 @@ SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue( SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ if( pRec ){ int i; - int nCol = pRec->pKeyInfo->nField+1; + int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField; Mem *aMem = pRec->aMem; sqlite3 *db = aMem[0].db; for(i=0; ipKeyInfo); + sqlite3KeyInfoUnref(pRec->pKeyInfo); sqlite3DbFree(db, pRec); } } @@ -60703,7 +61315,8 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){ /* ** Create a new virtual database engine. */ -SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){ +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ + sqlite3 *db = pParse->db; Vdbe *p; p = sqlite3DbMallocZero(db, sizeof(Vdbe) ); if( p==0 ) return 0; @@ -60715,6 +61328,10 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){ p->pPrev = 0; db->pVdbe = p; p->magic = VDBE_MAGIC_INIT; + p->pParse = pParse; + assert( pParse->aLabel==0 ); + assert( pParse->nLabel==0 ); + assert( pParse->nOpAlloc==0 ); return p; } @@ -60761,15 +61378,6 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ pB->isPrepareV2 = pA->isPrepareV2; } -#ifdef SQLITE_DEBUG -/* -** Turn tracing on or off -*/ -SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe *p, FILE *trace){ - p->trace = trace; -} -#endif - /* ** Resize the Vdbe.aOp array so that it is at least one op larger than ** it was. @@ -60779,17 +61387,29 @@ SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe *p, FILE *trace){ ** unchanged (this is so that any opcodes already allocated can be ** correctly deallocated along with the rest of the Vdbe). */ -static int growOpArray(Vdbe *p){ +static int growOpArray(Vdbe *v){ VdbeOp *pNew; + Parse *p = v->pParse; int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); - pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op)); + pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); if( pNew ){ p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op); - p->aOp = pNew; + v->aOp = pNew; } return (pNew ? SQLITE_OK : SQLITE_NOMEM); } +#ifdef SQLITE_DEBUG +/* This routine is just a convenient place to set a breakpoint that will +** fire after each opcode is inserted and displayed using +** "PRAGMA vdbe_addoptrace=on". +*/ +static void test_addop_breakpoint(void){ + static int n = 0; + n++; +} +#endif + /* ** Add a new instruction to the list of instructions current in the ** VDBE. Return the address of the new instruction. @@ -60813,7 +61433,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ i = p->nOp; assert( p->magic==VDBE_MAGIC_INIT ); assert( op>0 && op<0xff ); - if( p->nOpAlloc<=i ){ + if( p->pParse->nOpAlloc<=i ){ if( growOpArray(p) ){ return 1; } @@ -60827,10 +61447,22 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; -#ifdef SQLITE_DEBUG +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS pOp->zComment = 0; +#endif +#ifdef SQLITE_DEBUG if( p->db->flags & SQLITE_VdbeAddopTrace ){ + int jj, kk; + Parse *pParse = p->pParse; + for(jj=kk=0; jjaColCache + jj; + if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue; + printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn); + kk++; + } + if( kk ) printf("\n"); sqlite3VdbePrintOp(0, i, &p->aOp[i]); + test_addop_breakpoint(); } #endif #ifdef VDBE_PROFILE @@ -60912,9 +61544,10 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( ** ** Zero is returned if a malloc() fails. */ -SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){ +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){ + Parse *p = v->pParse; int i = p->nLabel++; - assert( p->magic==VDBE_MAGIC_INIT ); + assert( v->magic==VDBE_MAGIC_INIT ); if( (i & (i-1))==0 ){ p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel, (i*2+1)*sizeof(p->aLabel[0])); @@ -60930,13 +61563,15 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){ ** be inserted. The parameter "x" must have been obtained from ** a prior call to sqlite3VdbeMakeLabel(). */ -SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *p, int x){ +SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ + Parse *p = v->pParse; int j = -1-x; - assert( p->magic==VDBE_MAGIC_INIT ); + assert( v->magic==VDBE_MAGIC_INIT ); assert( jnLabel ); if( j>=0 && p->aLabel ){ - p->aLabel[j] = p->nOp; + p->aLabel[j] = v->nOp; } + p->iFixedOp = v->nOp - 1; } /* @@ -61084,7 +61719,8 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ int i; int nMaxArgs = *pMaxFuncArgs; Op *pOp; - int *aLabel = p->aLabel; + Parse *pParse = p->pParse; + int *aLabel = pParse->aLabel; p->readOnly = 1; p->bIsReader = 0; for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ @@ -61131,12 +61767,14 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ } #endif case OP_Next: + case OP_NextIfOpen: case OP_SorterNext: { pOp->p4.xAdvance = sqlite3BtreeNext; pOp->p4type = P4_ADVANCE; break; } - case OP_Prev: { + case OP_Prev: + case OP_PrevIfOpen: { pOp->p4.xAdvance = sqlite3BtreePrevious; pOp->p4type = P4_ADVANCE; break; @@ -61145,12 +61783,13 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ pOp->opflags = sqlite3OpcodeProperty[opcode]; if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){ - assert( -1-pOp->p2nLabel ); + assert( -1-pOp->p2nLabel ); pOp->p2 = aLabel[-1-pOp->p2]; } } - sqlite3DbFree(p->db, p->aLabel); - p->aLabel = 0; + sqlite3DbFree(p->db, pParse->aLabel); + pParse->aLabel = 0; + pParse->nLabel = 0; *pMaxFuncArgs = nMaxArgs; assert( p->bIsReader!=0 || p->btreeMask==0 ); } @@ -61194,7 +61833,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg) SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){ int addr; assert( p->magic==VDBE_MAGIC_INIT ); - if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){ + if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p) ){ return 0; } addr = p->nOp; @@ -61206,7 +61845,8 @@ SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp) VdbeOp *pOut = &p->aOp[i+addr]; pOut->opcode = pIn->opcode; pOut->p1 = pIn->p1; - if( p2<0 && (sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP)!=0 ){ + if( p2<0 ){ + assert( sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP ); pOut->p2 = addr + ADDR(p2); }else{ pOut->p2 = p2; @@ -61215,8 +61855,10 @@ SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp) pOut->p4type = P4_NOTUSED; pOut->p4.p = 0; pOut->p5 = 0; -#ifdef SQLITE_DEBUG +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS pOut->zComment = 0; +#endif +#ifdef SQLITE_DEBUG if( p->db->flags & SQLITE_VdbeAddopTrace ){ sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]); } @@ -61278,7 +61920,8 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){ ** the address of the next instruction to be coded. */ SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){ - if( ALWAYS(addr>=0) ) sqlite3VdbeChangeP2(p, addr, p->nOp); + sqlite3VdbeChangeP2(p, addr, p->nOp); + p->pParse->iFixedOp = p->nOp - 1; } @@ -61304,12 +61947,14 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ case P4_REAL: case P4_INT64: case P4_DYNAMIC: - case P4_KEYINFO: - case P4_INTARRAY: - case P4_KEYINFO_HANDOFF: { + case P4_INTARRAY: { sqlite3DbFree(db, p4); break; } + case P4_KEYINFO: { + if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4); + break; + } case P4_MPRINTF: { if( db->pnBytesFreed==0 ) sqlite3_free(p4); break; @@ -61346,7 +61991,7 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ Op *pOp; for(pOp=aOp; pOp<&aOp[nOp]; pOp++){ freeP4(db, pOp->p4type, pOp->p4.p); -#ifdef SQLITE_DEBUG +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS sqlite3DbFree(db, pOp->zComment); #endif } @@ -61374,6 +62019,19 @@ SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){ freeP4(db, pOp->p4type, pOp->p4.p); memset(pOp, 0, sizeof(pOp[0])); pOp->opcode = OP_Noop; + if( addr==p->nOp-1 ) p->nOp--; + } +} + +/* +** Remove the last opcode inserted +*/ +SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){ + if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){ + sqlite3VdbeChangeToNoop(p, p->nOp-1); + return 1; + }else{ + return 0; } } @@ -61387,14 +62045,6 @@ SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){ ** the string is made into memory obtained from sqlite3_malloc(). ** A value of n==0 means copy bytes of zP4 up to and including the ** first null byte. If n>0 then copy n+1 bytes of zP4. -** -** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure. -** A copy is made of the KeyInfo structure into memory obtained from -** sqlite3_malloc, to be freed when the Vdbe is finalized. -** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure -** stored in memory that the caller has obtained from sqlite3_malloc. The -** caller should not free the allocation, it will be freed when the Vdbe is -** finalized. ** ** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points ** to a string or structure that is guaranteed to exist for the lifetime of @@ -61409,7 +62059,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int db = p->db; assert( p->magic==VDBE_MAGIC_INIT ); if( p->aOp==0 || db->mallocFailed ){ - if ( n!=P4_KEYINFO && n!=P4_VTAB ) { + if( n!=P4_VTAB ){ freeP4(db, n, (void*)*(char**)&zP4); } return; @@ -61432,19 +62082,6 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; }else if( n==P4_KEYINFO ){ - KeyInfo *pOrig, *pNew; - - pOrig = (KeyInfo*)zP4; - pOp->p4.pKeyInfo = pNew = sqlite3KeyInfoAlloc(db, pOrig->nField); - if( pNew ){ - memcpy(pNew->aColl, pOrig->aColl, pOrig->nField*sizeof(pNew->aColl[0])); - memcpy(pNew->aSortOrder, pOrig->aSortOrder, pOrig->nField); - pOp->p4type = P4_KEYINFO; - }else{ - p->db->mallocFailed = 1; - pOp->p4type = P4_NOTUSED; - } - }else if( n==P4_KEYINFO_HANDOFF ){ pOp->p4.p = (void*)zP4; pOp->p4type = P4_KEYINFO; }else if( n==P4_VTAB ){ @@ -61462,7 +62099,19 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int } } -#ifndef NDEBUG +/* +** Set the P4 on the most recently added opcode to the KeyInfo for the +** index given. +*/ +SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ + Vdbe *v = pParse->pVdbe; + assert( v!=0 ); + assert( pIdx!=0 ); + sqlite3VdbeChangeP4(v, -1, (char*)sqlite3KeyInfoOfIndex(pParse, pIdx), + P4_KEYINFO); +} + +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS /* ** Change the comment on the most recently coded instruction. Or ** insert a No-op and add the comment to that new instruction. This @@ -61537,6 +62186,97 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ } } +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) +/* +** Return an integer value for one of the parameters to the opcode pOp +** determined by character c. +*/ +static int translateP(char c, const Op *pOp){ + if( c=='1' ) return pOp->p1; + if( c=='2' ) return pOp->p2; + if( c=='3' ) return pOp->p3; + if( c=='4' ) return pOp->p4.i; + return pOp->p5; +} + +/* +** Compute a string for the "comment" field of a VDBE opcode listing. +** +** The Synopsis: field in comments in the vdbe.c source file gets converted +** to an extra string that is appended to the sqlite3OpcodeName(). In the +** absence of other comments, this synopsis becomes the comment on the opcode. +** Some translation occurs: +** +** "PX" -> "r[X]" +** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1 +** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 +** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x +*/ +static int displayComment( + const Op *pOp, /* The opcode to be commented */ + const char *zP4, /* Previously obtained value for P4 */ + char *zTemp, /* Write result here */ + int nTemp /* Space available in zTemp[] */ +){ + const char *zOpName; + const char *zSynopsis; + int nOpName; + int ii, jj; + zOpName = sqlite3OpcodeName(pOp->opcode); + nOpName = sqlite3Strlen30(zOpName); + if( zOpName[nOpName+1] ){ + int seenCom = 0; + char c; + zSynopsis = zOpName += nOpName + 1; + for(ii=jj=0; jjzComment); + seenCom = 1; + }else{ + int v1 = translateP(c, pOp); + int v2; + sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); + if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ + ii += 3; + jj += sqlite3Strlen30(zTemp+jj); + v2 = translateP(zSynopsis[ii], pOp); + if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ + ii += 2; + v2++; + } + if( v2>1 ){ + sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); + } + }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ + ii += 4; + } + } + jj += sqlite3Strlen30(zTemp+jj); + }else{ + zTemp[jj++] = c; + } + } + if( !seenCom && jjzComment ){ + sqlite3_snprintf(nTemp-jj, zTemp+jj, "; %s", pOp->zComment); + jj += sqlite3Strlen30(zTemp+jj); + } + if( jjzComment ){ + sqlite3_snprintf(nTemp, zTemp, "%s", pOp->zComment); + jj = sqlite3Strlen30(zTemp); + }else{ + zTemp[0] = 0; + jj = 0; + } + return jj; +} +#endif /* SQLITE_DEBUG */ + + #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) /* @@ -61547,17 +62287,20 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ char *zP4 = zTemp; assert( nTemp>=20 ); switch( pOp->p4type ){ - case P4_KEYINFO_STATIC: case P4_KEYINFO: { int i, j; KeyInfo *pKeyInfo = pOp->p4.pKeyInfo; assert( pKeyInfo->aSortOrder!=0 ); - sqlite3_snprintf(nTemp, zTemp, "keyinfo(%d", pKeyInfo->nField); + sqlite3_snprintf(nTemp, zTemp, "k(%d", pKeyInfo->nField); i = sqlite3Strlen30(zTemp); for(j=0; jnField; j++){ CollSeq *pColl = pKeyInfo->aColl[j]; const char *zColl = pColl ? pColl->zName : "nil"; int n = sqlite3Strlen30(zColl); + if( n==6 && memcmp(zColl,"BINARY",6)==0 ){ + zColl = "B"; + n = 1; + } if( i+n>nTemp-6 ){ memcpy(&zTemp[i],",...",4); break; @@ -61576,7 +62319,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ } case P4_COLLSEQ: { CollSeq *pColl = pOp->p4.pColl; - sqlite3_snprintf(nTemp, zTemp, "collseq(%.20s)", pColl->zName); + sqlite3_snprintf(nTemp, zTemp, "(%.20s)", pColl->zName); break; } case P4_FUNCDEF: { @@ -61730,16 +62473,21 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){ char *zP4; char zPtr[50]; - static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n"; + char zCom[100]; + static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; if( pOut==0 ) pOut = stdout; zP4 = displayP4(pOp, zPtr, sizeof(zPtr)); +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + displayComment(pOp, zP4, zCom, sizeof(zCom)); +#else + zCom[0] = 0 +#endif + /* NB: The sqlite3OpcodeName() function is implemented by code created + ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the + ** information from the vdbe.c source text */ fprintf(pOut, zFormat1, pc, sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, -#ifdef SQLITE_DEBUG - pOp->zComment ? pOp->zComment : "" -#else - "" -#endif + zCom ); fflush(pOut); } @@ -61885,7 +62633,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( rc = SQLITE_ERROR; sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc)); }else{ - char *z; + char *zP4; Op *pOp; if( inOp ){ /* The output line number is small enough that we are still in the @@ -61908,7 +62656,7 @@ SQLITE_PRIVATE int sqlite3VdbeList( pMem++; pMem->flags = MEM_Static|MEM_Str|MEM_Term; - pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ + pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ assert( pMem->z!=0 ); pMem->n = sqlite3Strlen30(pMem->z); pMem->type = SQLITE_TEXT; @@ -61955,9 +62703,9 @@ SQLITE_PRIVATE int sqlite3VdbeList( return SQLITE_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; - z = displayP4(pOp, pMem->z, 32); - if( z!=pMem->z ){ - sqlite3VdbeMemSetStr(pMem, z, -1, SQLITE_UTF8, 0); + zP4 = displayP4(pOp, pMem->z, 32); + if( zP4!=pMem->z ){ + sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0); }else{ assert( pMem->z!=0 ); pMem->n = sqlite3Strlen30(pMem->z); @@ -61978,19 +62726,19 @@ SQLITE_PRIVATE int sqlite3VdbeList( pMem->enc = SQLITE_UTF8; pMem++; -#ifdef SQLITE_DEBUG - if( pOp->zComment ){ - pMem->flags = MEM_Str|MEM_Term; - pMem->z = pOp->zComment; - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - pMem->type = SQLITE_TEXT; - }else -#endif - { - pMem->flags = MEM_Null; /* Comment */ - pMem->type = SQLITE_NULL; +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + if( sqlite3VdbeMemGrow(pMem, 500, 0) ){ + assert( p->db->mallocFailed ); + return SQLITE_ERROR; } + pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; + pMem->n = displayComment(pOp, zP4, pMem->z, 500); + pMem->type = SQLITE_TEXT; + pMem->enc = SQLITE_UTF8; +#else + pMem->flags = MEM_Null; /* Comment */ + pMem->type = SQLITE_NULL; +#endif } p->nResColumn = 8 - 4*(p->explain-1); @@ -62007,15 +62755,17 @@ SQLITE_PRIVATE int sqlite3VdbeList( ** Print the SQL that was used to generate a VDBE program. */ SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe *p){ - int nOp = p->nOp; - VdbeOp *pOp; - if( nOp<1 ) return; - pOp = &p->aOp[0]; - if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ - const char *z = pOp->p4.z; - while( sqlite3Isspace(*z) ) z++; - printf("SQL: [%s]\n", z); + const char *z = 0; + if( p->zSql ){ + z = p->zSql; + }else if( p->nOp>=1 ){ + const VdbeOp *pOp = &p->aOp[0]; + if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ + z = pOp->p4.z; + while( sqlite3Isspace(*z) ) z++; + } } + if( z ) printf("SQL: [%s]\n", z); } #endif @@ -62166,6 +62916,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( assert( p->nOp>0 ); assert( pParse!=0 ); assert( p->magic==VDBE_MAGIC_INIT ); + assert( pParse==p->pParse ); db = p->db; assert( db->mallocFailed==0 ); nVar = pParse->nVar; @@ -62189,8 +62940,8 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( /* Allocate space for memory registers, SQL variables, VDBE cursors and ** an array to marshal SQL function arguments in. */ - zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */ - zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */ + zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */ + zEnd = (u8*)&p->aOp[pParse->nOpAlloc]; /* First byte past end of zCsr[] */ resolveP2Values(p, &nArg); p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); @@ -62273,7 +63024,7 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( pCx->pVtabCursor ){ sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; - const sqlite3_module *pModule = pCx->pModule; + const sqlite3_module *pModule = pVtabCursor->pVtab->pModule; p->inVtabMethod = 1; pModule->xClose(pVtabCursor); p->inVtabMethod = 0; @@ -62781,7 +63532,7 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ ){ p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; p->errorAction = OE_Abort; - sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed"); + sqlite3SetString(&p->zErrMsg, db, "FOREIGN KEY constraint failed"); return SQLITE_ERROR; } return SQLITE_OK; @@ -63017,6 +63768,7 @@ SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){ if( p->zErrMsg ){ u8 mallocFailed = db->mallocFailed; sqlite3BeginBenignMalloc(); + if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db); sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); sqlite3EndBenignMalloc(); db->mallocFailed = mallocFailed; @@ -63085,8 +63837,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ ** to sqlite3_step(). For consistency (since sqlite3_step() was ** called), set the database error in this case as well. */ - sqlite3Error(db, p->rc, 0); - sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); + sqlite3Error(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; } @@ -63159,8 +63910,9 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){ while( *pp ){ AuxData *pAux = *pp; if( (iOp<0) - || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & ((u32)1<iArg)))) + || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg)))) ){ + testcase( pAux->iArg==31 ); if( pAux->xDelete ){ pAux->xDelete(pAux->pAux); } @@ -63193,7 +63945,6 @@ SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ } for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]); vdbeFreeOpArray(db, p->aOp, p->nOp); - sqlite3DbFree(db, p->aLabel); sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); sqlite3DbFree(db, p->pFree); @@ -63257,7 +64008,7 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){ #endif p->deferredMoveto = 0; p->cacheStatus = CACHE_STALE; - }else if( ALWAYS(p->pCursor) ){ + }else if( p->pCursor ){ int hasMoved; int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved); if( rc ) return rc; @@ -63424,21 +64175,15 @@ static u64 floatSwap(u64 in){ ** buf. It is assumed that the caller has allocated sufficient space. ** Return the number of bytes written. ** -** nBuf is the amount of space left in buf[]. nBuf must always be -** large enough to hold the entire field. Except, if the field is -** a blob with a zero-filled tail, then buf[] might be just the right -** size to hold everything except for the zero-filled tail. If buf[] -** is only big enough to hold the non-zero prefix, then only write that -** prefix into buf[]. But if buf[] is large enough to hold both the -** prefix and the tail then write the prefix and set the tail to all -** zeros. +** nBuf is the amount of space left in buf[]. The caller is responsible +** for allocating enough space to buf[] to hold the entire field, exclusive +** of the pMem->u.nZero bytes for a MEM_Zero value. ** ** Return the number of bytes actually written into buf[]. The number ** of bytes in the zero-filled tail is included in the return value only ** if those bytes were zeroed in buf[]. */ -SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){ - u32 serial_type = sqlite3VdbeSerialType(pMem, file_format); +SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ u32 len; /* Integer and Real */ @@ -63453,7 +64198,6 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f v = pMem->u.i; } len = i = sqlite3VdbeSerialTypeLen(serial_type); - assert( len<=(u32)nBuf ); while( i-- ){ buf[i] = (u8)(v&0xFF); v >>= 8; @@ -63465,17 +64209,8 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f if( serial_type>=12 ){ assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0) == (int)sqlite3VdbeSerialTypeLen(serial_type) ); - assert( pMem->n<=nBuf ); len = pMem->n; memcpy(buf, pMem->z, len); - if( pMem->flags & MEM_Zero ){ - len += pMem->u.nZero; - assert( nBuf>=0 ); - if( len > (u32)nBuf ){ - len = (u32)nBuf; - } - memset(&buf[pMem->n], 0, len-pMem->n); - } return len; } @@ -63492,6 +64227,9 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( u32 serial_type, /* Serial type to deserialize */ Mem *pMem /* Memory cell to write value into */ ){ + u64 x; + u32 y; + int i; switch( serial_type ){ case 10: /* Reserved for future use */ case 11: /* Reserved for future use */ @@ -63505,23 +64243,26 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( return 1; } case 2: { /* 2-byte signed integer */ - pMem->u.i = (((signed char)buf[0])<<8) | buf[1]; + i = 256*(signed char)buf[0] | buf[1]; + pMem->u.i = (i64)i; pMem->flags = MEM_Int; return 2; } case 3: { /* 3-byte signed integer */ - pMem->u.i = (((signed char)buf[0])<<16) | (buf[1]<<8) | buf[2]; + i = 65536*(signed char)buf[0] | (buf[1]<<8) | buf[2]; + pMem->u.i = (i64)i; pMem->flags = MEM_Int; return 3; } case 4: { /* 4-byte signed integer */ - pMem->u.i = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; + y = ((unsigned)buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; + pMem->u.i = (i64)*(int*)&y; pMem->flags = MEM_Int; return 4; } case 5: { /* 6-byte signed integer */ - u64 x = (((signed char)buf[0])<<8) | buf[1]; - u32 y = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]; + x = 256*(signed char)buf[0] + buf[1]; + y = ((unsigned)buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]; x = (x<<32) | y; pMem->u.i = *(i64*)&x; pMem->flags = MEM_Int; @@ -63529,8 +64270,6 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( } case 6: /* 8-byte signed integer */ case 7: { /* IEEE floating point */ - u64 x; - u32 y; #if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT) /* Verify that integers and floating point values use the same ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is @@ -63543,9 +64282,8 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( swapMixedEndianFloat(t2); assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); #endif - - x = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; - y = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7]; + x = ((unsigned)buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; + y = ((unsigned)buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7]; x = (x<<32) | y; if( serial_type==6 ){ pMem->u.i = *(i64*)&x; @@ -63565,15 +64303,12 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( return 0; } default: { + static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem }; u32 len = (serial_type-12)/2; pMem->z = (char *)buf; pMem->n = len; pMem->xDel = 0; - if( serial_type&0x01 ){ - pMem->flags = MEM_Str | MEM_Ephem; - }else{ - pMem->flags = MEM_Blob | MEM_Ephem; - } + pMem->flags = aFlag[serial_type&1]; return len; } } @@ -63712,9 +64447,11 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare( idx1 = getVarint32(aKey1, szHdr1); d1 = szHdr1; - assert( pKeyInfo->nField+1>=pPKey2->nField ); + assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB ); assert( pKeyInfo->aSortOrder!=0 ); - while( idx1nField ){ + assert( pKeyInfo->nField>0 ); + assert( idx1<=szHdr1 || CORRUPT_DB ); + do{ u32 serial_type1; /* Read the serial types for the next element in each key. */ @@ -63741,28 +64478,13 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare( rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]); if( rc!=0 ){ assert( mem1.zMalloc==0 ); /* See comment below */ - - /* Invert the result if we are using DESC sort order. */ if( pKeyInfo->aSortOrder[i] ){ - rc = -rc; + rc = -rc; /* Invert the result for DESC sort order. */ } - - /* If the PREFIX_SEARCH flag is set and all fields except the final - ** rowid field were equal, then clear the PREFIX_SEARCH flag and set - ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1). - ** This is used by the OP_IsUnique opcode. - */ - if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){ - assert( idx1==szHdr1 && rc ); - assert( mem1.flags & MEM_Int ); - pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH; - pPKey2->rowid = mem1.u.i; - } - return rc; } i++; - } + }while( idx1nField ); /* No memory allocation is ever used on mem1. Prove this using ** the following assert(). If the assert() fails, it indicates a @@ -63820,7 +64542,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ /* Read in the complete content of the index entry */ memset(&m, 0, sizeof(m)); - rc = sqlite3VdbeMemFromBtree(pCur, 0, (int)nCellKey, 1, &m); + rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m); if( rc ){ return rc; } @@ -63898,7 +64620,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( return SQLITE_CORRUPT_BKPT; } memset(&m, 0, sizeof(m)); - rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); + rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (u32)nCellKey, 1, &m); if( rc ){ return rc; } @@ -64492,7 +65214,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ v->doingRerun = 1; assert( v->expired==0 ); } - if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){ + if( rc2!=SQLITE_OK ){ /* This case occurs after failing to recompile an sql statement. ** The error message from the SQL compiler has already been loaded ** into the database handle. This block copies the error message @@ -64502,6 +65224,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ ** sqlite3_errmsg() and sqlite3_errcode(). */ const char *zErr = (const char *)sqlite3_value_text(db->pErr); + assert( zErr!=0 || db->mallocFailed ); sqlite3DbFree(db, v->zErrMsg); if( !db->mallocFailed ){ v->zErrMsg = sqlite3DbStrDup(db, zErr); @@ -65417,6 +66140,7 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( const char *zStart = zRawSql; while( *(zRawSql++)!='\n' && *zRawSql ); sqlite3StrAccumAppend(&out, "-- ", 3); + assert( (zRawSql - zStart) > 0 ); sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); } }else{ @@ -65449,9 +66173,9 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( if( pVar->flags & MEM_Null ){ sqlite3StrAccumAppend(&out, "NULL", 4); }else if( pVar->flags & MEM_Int ){ - sqlite3XPrintf(&out, "%lld", pVar->u.i); + sqlite3XPrintf(&out, 0, "%lld", pVar->u.i); }else if( pVar->flags & MEM_Real ){ - sqlite3XPrintf(&out, "%!.15g", pVar->r); + sqlite3XPrintf(&out, 0, "%!.15g", pVar->r); }else if( pVar->flags & MEM_Str ){ int nOut; /* Number of bytes of the string text to include in output */ #ifndef SQLITE_OMIT_UTF16 @@ -65472,15 +66196,17 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( while( nOutn && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; } } #endif - sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z); + sqlite3XPrintf(&out, 0, "'%.*q'", nOut, pVar->z); #ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOutn ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut); + if( nOutn ){ + sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut); + } #endif #ifndef SQLITE_OMIT_UTF16 if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8); #endif }else if( pVar->flags & MEM_Zero ){ - sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero); + sqlite3XPrintf(&out, 0, "zeroblob(%d)", pVar->u.nZero); }else{ int nOut; /* Number of bytes of the blob to include in output */ assert( pVar->flags & MEM_Blob ); @@ -65490,11 +66216,13 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT; #endif for(i=0; iz[i]&0xff); + sqlite3XPrintf(&out, 0, "%02x", pVar->z[i]&0xff); } sqlite3StrAccumAppend(&out, "'", 1); #ifdef SQLITE_TRACE_SIZE_LIMIT - if( nOutn ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut); + if( nOutn ){ + sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut); + } #endif } } @@ -65553,7 +66281,7 @@ SQLITE_PRIVATE void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){ sqlite3AppendSpace(&p->str, p->aIndent[n-1]); } va_start(ap, zFormat); - sqlite3VXPrintf(&p->str, 1, zFormat, ap); + sqlite3VXPrintf(&p->str, SQLITE_PRINTF_INTERNAL, zFormat, ap); va_end(ap); } } @@ -65833,9 +66561,8 @@ static VdbeCursor *allocateCursor( int nByte; VdbeCursor *pCx = 0; nByte = - ROUND8(sizeof(VdbeCursor)) + - (isBtreeCursor?sqlite3BtreeCursorSize():0) + - 2*nField*sizeof(u32); + ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + + (isBtreeCursor?sqlite3BtreeCursorSize():0); assert( iCurnCursor ); if( p->apCsr[iCur] ){ @@ -65847,12 +66574,9 @@ static VdbeCursor *allocateCursor( memset(pCx, 0, sizeof(VdbeCursor)); pCx->iDb = iDb; pCx->nField = nField; - if( nField ){ - pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))]; - } if( isBtreeCursor ){ pCx->pCursor = (BtCursor*) - &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)]; + &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; sqlite3BtreeCursorZero(pCx->pCursor); } } @@ -66038,37 +66762,36 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){ /* ** Print the value of a register for tracing purposes: */ -static void memTracePrint(FILE *out, Mem *p){ +static void memTracePrint(Mem *p){ if( p->flags & MEM_Invalid ){ - fprintf(out, " undefined"); + printf(" undefined"); }else if( p->flags & MEM_Null ){ - fprintf(out, " NULL"); + printf(" NULL"); }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ - fprintf(out, " si:%lld", p->u.i); + printf(" si:%lld", p->u.i); }else if( p->flags & MEM_Int ){ - fprintf(out, " i:%lld", p->u.i); + printf(" i:%lld", p->u.i); #ifndef SQLITE_OMIT_FLOATING_POINT }else if( p->flags & MEM_Real ){ - fprintf(out, " r:%g", p->r); + printf(" r:%g", p->r); #endif }else if( p->flags & MEM_RowSet ){ - fprintf(out, " (rowset)"); + printf(" (rowset)"); }else{ char zBuf[200]; sqlite3VdbeMemPrettyPrint(p, zBuf); - fprintf(out, " "); - fprintf(out, "%s", zBuf); + printf(" %s", zBuf); } } -static void registerTrace(FILE *out, int iReg, Mem *p){ - fprintf(out, "REG[%d] = ", iReg); - memTracePrint(out, p); - fprintf(out, "\n"); +static void registerTrace(int iReg, Mem *p){ + printf("REG[%d] = ", iReg); + memTracePrint(p); + printf("\n"); } #endif #ifdef SQLITE_DEBUG -# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M) +# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M) #else # define REGISTER_TRACE(R,M) #endif @@ -66265,436 +66988,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( u64 start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ #endif - /******************************************************************** - ** Automatically generated code - ** - ** The following union is automatically generated by the - ** vdbe-compress.tcl script. The purpose of this union is to - ** reduce the amount of stack space required by this function. - ** See comments in the vdbe-compress.tcl script for details. - */ - union vdbeExecUnion { - struct OP_Yield_stack_vars { - int pcDest; - } aa; - struct OP_Null_stack_vars { - int cnt; - u16 nullFlag; - } ab; - struct OP_Variable_stack_vars { - Mem *pVar; /* Value being transferred */ - } ac; - struct OP_Move_stack_vars { - char *zMalloc; /* Holding variable for allocated memory */ - int n; /* Number of registers left to copy */ - int p1; /* Register to copy from */ - int p2; /* Register to copy to */ - } ad; - struct OP_Copy_stack_vars { - int n; - } ae; - struct OP_ResultRow_stack_vars { - Mem *pMem; - int i; - } af; - struct OP_Concat_stack_vars { - i64 nByte; - } ag; - struct OP_Remainder_stack_vars { - char bIntint; /* Started out as two integer operands */ - int flags; /* Combined MEM_* flags from both inputs */ - i64 iA; /* Integer value of left operand */ - i64 iB; /* Integer value of right operand */ - double rA; /* Real value of left operand */ - double rB; /* Real value of right operand */ - } ah; - struct OP_Function_stack_vars { - int i; - Mem *pArg; - sqlite3_context ctx; - sqlite3_value **apVal; - int n; - } ai; - struct OP_ShiftRight_stack_vars { - i64 iA; - u64 uA; - i64 iB; - u8 op; - } aj; - struct OP_Ge_stack_vars { - int res; /* Result of the comparison of pIn1 against pIn3 */ - char affinity; /* Affinity to use for comparison */ - u16 flags1; /* Copy of initial value of pIn1->flags */ - u16 flags3; /* Copy of initial value of pIn3->flags */ - } ak; - struct OP_Compare_stack_vars { - int n; - int i; - int p1; - int p2; - const KeyInfo *pKeyInfo; - int idx; - CollSeq *pColl; /* Collating sequence to use on this term */ - int bRev; /* True for DESCENDING sort order */ - } al; - struct OP_Or_stack_vars { - int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ - } am; - struct OP_IfNot_stack_vars { - int c; - } an; - struct OP_Column_stack_vars { - u32 payloadSize; /* Number of bytes in the record */ - i64 payloadSize64; /* Number of bytes in the record */ - int p1; /* P1 value of the opcode */ - int p2; /* column number to retrieve */ - VdbeCursor *pC; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ - BtCursor *pCrsr; /* The BTree cursor */ - u32 *aType; /* aType[i] holds the numeric type of the i-th column */ - u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int nField; /* number of fields in the record */ - int len; /* The length of the serialized data for the column */ - int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ - Mem *pDest; /* Where to write the extracted value */ - Mem sMem; /* For storing the record being decoded */ - u8 *zIdx; /* Index into header */ - u8 *zEndHdr; /* Pointer to first byte after the header */ - u32 offset; /* Offset into the data */ - u32 szField; /* Number of bytes in the content of a field */ - int szHdr; /* Size of the header size field at start of record */ - int avail; /* Number of bytes of available data */ - u32 t; /* A type code from the record header */ - Mem *pReg; /* PseudoTable input register */ - } ao; - struct OP_Affinity_stack_vars { - const char *zAffinity; /* The affinity to be applied */ - char cAff; /* A single character of affinity */ - } ap; - struct OP_MakeRecord_stack_vars { - u8 *zNewRecord; /* A buffer to hold the data for the new record */ - Mem *pRec; /* The new record */ - u64 nData; /* Number of bytes of data space */ - int nHdr; /* Number of bytes of header space */ - i64 nByte; /* Data space required for this record */ - int nZero; /* Number of zero bytes at the end of the record */ - int nVarint; /* Number of bytes in a varint */ - u32 serial_type; /* Type field */ - Mem *pData0; /* First field to be combined into the record */ - Mem *pLast; /* Last field of the record */ - int nField; /* Number of fields in the record */ - char *zAffinity; /* The affinity string for the record */ - int file_format; /* File format to use for encoding */ - int i; /* Space used in zNewRecord[] */ - int len; /* Length of a field */ - } aq; - struct OP_Count_stack_vars { - i64 nEntry; - BtCursor *pCrsr; - } ar; - struct OP_Savepoint_stack_vars { - int p1; /* Value of P1 operand */ - char *zName; /* Name of savepoint */ - int nName; - Savepoint *pNew; - Savepoint *pSavepoint; - Savepoint *pTmp; - int iSavepoint; - int ii; - } as; - struct OP_AutoCommit_stack_vars { - int desiredAutoCommit; - int iRollback; - int turnOnAC; - } at; - struct OP_Transaction_stack_vars { - Btree *pBt; - } au; - struct OP_ReadCookie_stack_vars { - int iMeta; - int iDb; - int iCookie; - } av; - struct OP_SetCookie_stack_vars { - Db *pDb; - } aw; - struct OP_VerifyCookie_stack_vars { - int iMeta; - int iGen; - Btree *pBt; - } ax; - struct OP_OpenWrite_stack_vars { - int nField; - KeyInfo *pKeyInfo; - int p2; - int iDb; - int wrFlag; - Btree *pX; - VdbeCursor *pCur; - Db *pDb; - } ay; - struct OP_OpenEphemeral_stack_vars { - VdbeCursor *pCx; - } az; - struct OP_SorterOpen_stack_vars { - VdbeCursor *pCx; - } ba; - struct OP_OpenPseudo_stack_vars { - VdbeCursor *pCx; - } bb; - struct OP_SeekGt_stack_vars { - int res; - int oc; - VdbeCursor *pC; - UnpackedRecord r; - int nField; - i64 iKey; /* The rowid we are to seek to */ - } bc; - struct OP_Seek_stack_vars { - VdbeCursor *pC; - } bd; - struct OP_Found_stack_vars { - int alreadyExists; - VdbeCursor *pC; - int res; - char *pFree; - UnpackedRecord *pIdxKey; - UnpackedRecord r; - char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; - } be; - struct OP_IsUnique_stack_vars { - u16 ii; - VdbeCursor *pCx; - BtCursor *pCrsr; - u16 nField; - Mem *aMx; - UnpackedRecord r; /* B-Tree index search key */ - i64 R; /* Rowid stored in register P3 */ - } bf; - struct OP_NotExists_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - u64 iKey; - } bg; - struct OP_NewRowid_stack_vars { - i64 v; /* The new rowid */ - VdbeCursor *pC; /* Cursor of table to get the new rowid */ - int res; /* Result of an sqlite3BtreeLast() */ - int cnt; /* Counter to limit the number of searches */ - Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ - VdbeFrame *pFrame; /* Root frame of VDBE */ - } bh; - struct OP_InsertInt_stack_vars { - Mem *pData; /* MEM cell holding data for the record to be inserted */ - Mem *pKey; /* MEM cell holding key for the record */ - i64 iKey; /* The integer ROWID or key for the record to be inserted */ - VdbeCursor *pC; /* Cursor to table into which insert is written */ - int nZero; /* Number of zero-bytes to append */ - int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ - const char *zDb; /* database name - used by the update hook */ - const char *zTbl; /* Table name - used by the opdate hook */ - int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ - } bi; - struct OP_Delete_stack_vars { - i64 iKey; - VdbeCursor *pC; - } bj; - struct OP_SorterCompare_stack_vars { - VdbeCursor *pC; - int res; - } bk; - struct OP_SorterData_stack_vars { - VdbeCursor *pC; - } bl; - struct OP_RowData_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - u32 n; - i64 n64; - } bm; - struct OP_Rowid_stack_vars { - VdbeCursor *pC; - i64 v; - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - } bn; - struct OP_NullRow_stack_vars { - VdbeCursor *pC; - } bo; - struct OP_Last_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - } bp; - struct OP_Rewind_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - } bq; - struct OP_Next_stack_vars { - VdbeCursor *pC; - int res; - } br; - struct OP_IdxInsert_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int nKey; - const char *zKey; - } bs; - struct OP_IdxDelete_stack_vars { - VdbeCursor *pC; - BtCursor *pCrsr; - int res; - UnpackedRecord r; - } bt; - struct OP_IdxRowid_stack_vars { - BtCursor *pCrsr; - VdbeCursor *pC; - i64 rowid; - } bu; - struct OP_IdxGE_stack_vars { - VdbeCursor *pC; - int res; - UnpackedRecord r; - } bv; - struct OP_Destroy_stack_vars { - int iMoved; - int iCnt; - Vdbe *pVdbe; - int iDb; - } bw; - struct OP_Clear_stack_vars { - int nChange; - } bx; - struct OP_CreateTable_stack_vars { - int pgno; - int flags; - Db *pDb; - } by; - struct OP_ParseSchema_stack_vars { - int iDb; - const char *zMaster; - char *zSql; - InitData initData; - } bz; - struct OP_IntegrityCk_stack_vars { - int nRoot; /* Number of tables to check. (Number of root pages.) */ - int *aRoot; /* Array of rootpage numbers for tables to be checked */ - int j; /* Loop counter */ - int nErr; /* Number of errors reported */ - char *z; /* Text of the error report */ - Mem *pnErr; /* Register keeping track of errors remaining */ - } ca; - struct OP_RowSetRead_stack_vars { - i64 val; - } cb; - struct OP_RowSetTest_stack_vars { - int iSet; - int exists; - } cc; - struct OP_Program_stack_vars { - int nMem; /* Number of memory registers for sub-program */ - int nByte; /* Bytes of runtime space required for sub-program */ - Mem *pRt; /* Register to allocate runtime space */ - Mem *pMem; /* Used to iterate through memory cells */ - Mem *pEnd; /* Last memory cell in new array */ - VdbeFrame *pFrame; /* New vdbe frame to execute in */ - SubProgram *pProgram; /* Sub-program to execute */ - void *t; /* Token identifying trigger */ - } cd; - struct OP_Param_stack_vars { - VdbeFrame *pFrame; - Mem *pIn; - } ce; - struct OP_MemMax_stack_vars { - Mem *pIn1; - VdbeFrame *pFrame; - } cf; - struct OP_AggStep_stack_vars { - int n; - int i; - Mem *pMem; - Mem *pRec; - sqlite3_context ctx; - sqlite3_value **apVal; - } cg; - struct OP_AggFinal_stack_vars { - Mem *pMem; - } ch; - struct OP_Checkpoint_stack_vars { - int i; /* Loop counter */ - int aRes[3]; /* Results */ - Mem *pMem; /* Write results here */ - } ci; - struct OP_JournalMode_stack_vars { - Btree *pBt; /* Btree to change journal mode of */ - Pager *pPager; /* Pager associated with pBt */ - int eNew; /* New journal mode */ - int eOld; /* The old journal mode */ -#ifndef SQLITE_OMIT_WAL - const char *zFilename; /* Name of database file for pPager */ -#endif - } cj; - struct OP_IncrVacuum_stack_vars { - Btree *pBt; - } ck; - struct OP_VBegin_stack_vars { - VTable *pVTab; - } cl; - struct OP_VOpen_stack_vars { - VdbeCursor *pCur; - sqlite3_vtab_cursor *pVtabCursor; - sqlite3_vtab *pVtab; - sqlite3_module *pModule; - } cm; - struct OP_VFilter_stack_vars { - int nArg; - int iQuery; - const sqlite3_module *pModule; - Mem *pQuery; - Mem *pArgc; - sqlite3_vtab_cursor *pVtabCursor; - sqlite3_vtab *pVtab; - VdbeCursor *pCur; - int res; - int i; - Mem **apArg; - } cn; - struct OP_VColumn_stack_vars { - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - Mem *pDest; - sqlite3_context sContext; - } co; - struct OP_VNext_stack_vars { - sqlite3_vtab *pVtab; - const sqlite3_module *pModule; - int res; - VdbeCursor *pCur; - } cp; - struct OP_VRename_stack_vars { - sqlite3_vtab *pVtab; - Mem *pName; - } cq; - struct OP_VUpdate_stack_vars { - sqlite3_vtab *pVtab; - sqlite3_module *pModule; - int nArg; - int i; - sqlite_int64 rowid; - Mem **apArg; - Mem *pX; - } cr; - struct OP_Trace_stack_vars { - char *zTrace; - char *z; - } cs; - } u; - /* End automatically generated code - ********************************************************************/ + /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); @@ -66725,13 +67019,28 @@ SQLITE_PRIVATE int sqlite3VdbeExec( #endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); - if( p->pc==0 && (p->db->flags & SQLITE_VdbeListing)!=0 ){ + if( p->pc==0 + && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 + ){ int i; - printf("VDBE Program Listing:\n"); + int once = 1; sqlite3VdbePrintSql(p); - for(i=0; inOp; i++){ - sqlite3VdbePrintOp(stdout, i, &aOp[i]); + if( p->db->flags & SQLITE_VdbeListing ){ + printf("VDBE Program Listing:\n"); + for(i=0; inOp; i++){ + sqlite3VdbePrintOp(stdout, i, &aOp[i]); + } } + if( p->db->flags & SQLITE_VdbeEQP ){ + for(i=0; inOp; i++){ + if( aOp[i].opcode==OP_Explain ){ + if( once ) printf("VDBE Query Plan:\n"); + printf("%s\n", aOp[i].p4.z); + once = 0; + } + } + } + if( p->db->flags & SQLITE_VdbeTrace ) printf("VDBE Trace:\n"); } sqlite3EndBenignMalloc(); #endif @@ -66748,12 +67057,8 @@ SQLITE_PRIVATE int sqlite3VdbeExec( /* Only allow tracing if SQLITE_DEBUG is defined. */ #ifdef SQLITE_DEBUG - if( p->trace ){ - if( pc==0 ){ - printf("VDBE Execution Trace:\n"); - sqlite3VdbePrintSql(p); - } - sqlite3VdbePrintOp(p->trace, pc, pOp); + if( db->flags & SQLITE_VdbeTrace ){ + sqlite3VdbePrintOp(stdout, pc, pOp); } #endif @@ -66884,15 +67189,12 @@ check_for_interrupt: ** a return code SQLITE_ABORT. */ if( db->xProgress!=0 && nVmStep>=nProgressLimit ){ - int prc; - prc = db->xProgress(db->pProgressArg); - if( prc!=0 ){ + assert( db->nProgressOps!=0 ); + nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps); + if( db->xProgress(db->pProgressArg) ){ rc = SQLITE_INTERRUPT; goto vdbe_error_halt; } - if( db->xProgress!=0 ){ - nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps); - } } #endif @@ -66932,24 +67234,24 @@ case OP_Return: { /* in1 */ ** Swap the program counter with the value in register P1. */ case OP_Yield: { /* in1 */ -#if 0 /* local variables moved into u.aa */ int pcDest; -#endif /* local variables moved into u.aa */ pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); pIn1->flags = MEM_Int; - u.aa.pcDest = (int)pIn1->u.i; + pcDest = (int)pIn1->u.i; pIn1->u.i = pc; REGISTER_TRACE(pOp->p1, pIn1); - pc = u.aa.pcDest; + pc = pcDest; break; } -/* Opcode: HaltIfNull P1 P2 P3 P4 * +/* Opcode: HaltIfNull P1 P2 P3 P4 P5 +** Synopsis: if r[P3] null then halt ** ** Check the value in register P3. If it is NULL then Halt using ** parameter P1, P2, and P4 as if this were a Halt instruction. If the ** value in register P3 is not NULL, then this routine is a no-op. +** The P5 parameter should be 1. */ case OP_HaltIfNull: { /* in3 */ pIn3 = &aMem[pOp->p3]; @@ -66957,7 +67259,7 @@ case OP_HaltIfNull: { /* in3 */ /* Fall through into OP_Halt */ } -/* Opcode: Halt P1 P2 * P4 * +/* Opcode: Halt P1 P2 * P4 P5 ** ** Exit immediately. All open cursors, etc are closed ** automatically. @@ -66972,11 +67274,25 @@ case OP_HaltIfNull: { /* in3 */ ** ** If P4 is not null then it is an error message string. ** +** P5 is a value between 0 and 4, inclusive, that modifies the P4 string. +** +** 0: (no change) +** 1: NOT NULL contraint failed: P4 +** 2: UNIQUE constraint failed: P4 +** 3: CHECK constraint failed: P4 +** 4: FOREIGN KEY constraint failed: P4 +** +** If P5 is not zero and P4 is NULL, then everything after the ":" is +** omitted. +** ** There is an implied "Halt 0 0 0" instruction inserted at the very end of ** every program. So a jump past the last instruction of the program ** is the same as executing Halt. */ case OP_Halt: { + const char *zType; + const char *zLogFmt; + if( pOp->p1==SQLITE_OK && p->pFrame ){ /* Halt the sub-program. Return control to the parent frame. */ VdbeFrame *pFrame = p->pFrame; @@ -66997,18 +67313,33 @@ case OP_Halt: { aMem = p->aMem; break; } - p->rc = pOp->p1; p->errorAction = (u8)pOp->p2; p->pc = pc; - if( pOp->p4.z ){ - assert( p->rc!=SQLITE_OK ); - sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z); - }else if( p->rc ){ - testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql); + if( p->rc ){ + if( pOp->p5 ){ + static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", + "FOREIGN KEY" }; + assert( pOp->p5>=1 && pOp->p5<=4 ); + testcase( pOp->p5==1 ); + testcase( pOp->p5==2 ); + testcase( pOp->p5==3 ); + testcase( pOp->p5==4 ); + zType = azType[pOp->p5-1]; + }else{ + zType = 0; + } + assert( zType!=0 || pOp->p4.z!=0 ); + zLogFmt = "abort at %d in [%s]: %s"; + if( zType && pOp->p4.z ){ + sqlite3SetString(&p->zErrMsg, db, "%s constraint failed: %s", + zType, pOp->p4.z); + }else if( pOp->p4.z ){ + sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); + }else{ + sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType); + } + sqlite3_log(pOp->p1, zLogFmt, pc, p->zSql, p->zErrMsg); } rc = sqlite3VdbeHalt(p); assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); @@ -67023,6 +67354,7 @@ case OP_Halt: { } /* Opcode: Integer P1 P2 * * * +** Synopsis: r[P2]=P1 ** ** The 32-bit integer value P1 is written into register P2. */ @@ -67032,6 +67364,7 @@ case OP_Integer: { /* out2-prerelease */ } /* Opcode: Int64 * P2 * P4 * +** Synopsis: r[P2]=P4 ** ** P4 is a pointer to a 64-bit integer value. ** Write that value into register P2. @@ -67044,6 +67377,7 @@ case OP_Int64: { /* out2-prerelease */ #ifndef SQLITE_OMIT_FLOATING_POINT /* Opcode: Real * P2 * P4 * +** Synopsis: r[P2]=P4 ** ** P4 is a pointer to a 64-bit floating point value. ** Write that value into register P2. @@ -67057,6 +67391,7 @@ case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ #endif /* Opcode: String8 * P2 * P4 * +** Synopsis: r[P2]='P4' ** ** P4 points to a nul terminated UTF-8 string. This opcode is transformed ** into an OP_String before it is executed for the first time. @@ -67091,6 +67426,7 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */ } /* Opcode: String P1 P2 * P4 * +** Synopsis: r[P2]='P4' (len=P1) ** ** The string value P4 of length P1 (bytes) is stored in register P2. */ @@ -67105,6 +67441,7 @@ case OP_String: { /* out2-prerelease */ } /* Opcode: Null P1 P2 P3 * * +** Synopsis: r[P2..P3]=NULL ** ** Write a NULL into registers P2. If P3 greater than P2, then also write ** NULL into register P3 and every register in between P2 and P3. If P3 @@ -67116,25 +67453,24 @@ case OP_String: { /* out2-prerelease */ ** OP_Ne or OP_Eq. */ case OP_Null: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ab */ int cnt; u16 nullFlag; -#endif /* local variables moved into u.ab */ - u.ab.cnt = pOp->p3-pOp->p2; + cnt = pOp->p3-pOp->p2; assert( pOp->p3<=(p->nMem-p->nCursor) ); - pOut->flags = u.ab.nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; - while( u.ab.cnt>0 ){ + pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; + while( cnt>0 ){ pOut++; memAboutToChange(p, pOut); VdbeMemRelease(pOut); - pOut->flags = u.ab.nullFlag; - u.ab.cnt--; + pOut->flags = nullFlag; + cnt--; } break; } /* Opcode: Blob P1 P2 * P4 +** Synopsis: r[P2]=P4 (len=P1) ** ** P4 points to a blob of data P1 bytes long. Store this ** blob in register P2. @@ -67148,6 +67484,7 @@ case OP_Blob: { /* out2-prerelease */ } /* Opcode: Variable P1 P2 * P4 * +** Synopsis: r[P2]=parameter(P1,P4) ** ** Transfer the values of bound parameter P1 into register P2 ** @@ -67155,22 +67492,21 @@ case OP_Blob: { /* out2-prerelease */ ** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ac */ Mem *pVar; /* Value being transferred */ -#endif /* local variables moved into u.ac */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] ); - u.ac.pVar = &p->aVar[pOp->p1 - 1]; - if( sqlite3VdbeMemTooBig(u.ac.pVar) ){ + pVar = &p->aVar[pOp->p1 - 1]; + if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; } - sqlite3VdbeMemShallowCopy(pOut, u.ac.pVar, MEM_Static); + sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Move P1 P2 P3 * * +** Synopsis: r[P2@P3]=r[P1@P3] ** ** Move the values in register P1..P1+P3 over into ** registers P2..P2+P3. Registers P1..P1+P3 are @@ -67178,43 +67514,42 @@ case OP_Variable: { /* out2-prerelease */ ** P1..P1+P3 and P2..P2+P3 to overlap. */ case OP_Move: { -#if 0 /* local variables moved into u.ad */ char *zMalloc; /* Holding variable for allocated memory */ int n; /* Number of registers left to copy */ int p1; /* Register to copy from */ int p2; /* Register to copy to */ -#endif /* local variables moved into u.ad */ - u.ad.n = pOp->p3 + 1; - u.ad.p1 = pOp->p1; - u.ad.p2 = pOp->p2; - assert( u.ad.n>0 && u.ad.p1>0 && u.ad.p2>0 ); - assert( u.ad.p1+u.ad.n<=u.ad.p2 || u.ad.p2+u.ad.n<=u.ad.p1 ); + n = pOp->p3; + p1 = pOp->p1; + p2 = pOp->p2; + assert( n>=0 && p1>0 && p2>0 ); + assert( p1+n<=p2 || p2+n<=p1 ); - pIn1 = &aMem[u.ad.p1]; - pOut = &aMem[u.ad.p2]; - while( u.ad.n-- ){ + pIn1 = &aMem[p1]; + pOut = &aMem[p2]; + do{ assert( pOut<=&aMem[(p->nMem-p->nCursor)] ); assert( pIn1<=&aMem[(p->nMem-p->nCursor)] ); assert( memIsValid(pIn1) ); memAboutToChange(p, pOut); - u.ad.zMalloc = pOut->zMalloc; + zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); #ifdef SQLITE_DEBUG - if( pOut->pScopyFrom>=&aMem[u.ad.p1] && pOut->pScopyFrom<&aMem[u.ad.p1+pOp->p3] ){ - pOut->pScopyFrom += u.ad.p1 - pOp->p2; + if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<&aMem[p1+pOp->p3] ){ + pOut->pScopyFrom += p1 - pOp->p2; } #endif - pIn1->zMalloc = u.ad.zMalloc; - REGISTER_TRACE(u.ad.p2++, pOut); + pIn1->zMalloc = zMalloc; + REGISTER_TRACE(p2++, pOut); pIn1++; pOut++; - } + }while( n-- ); break; } /* Opcode: Copy P1 P2 P3 * * +** Synopsis: r[P2@P3+1]=r[P1@P3+1] ** ** Make a copy of registers P1..P1+P3 into registers P2..P2+P3. ** @@ -67222,11 +67557,9 @@ case OP_Move: { ** is made of any string or blob constant. See also OP_SCopy. */ case OP_Copy: { -#if 0 /* local variables moved into u.ae */ int n; -#endif /* local variables moved into u.ae */ - u.ae.n = pOp->p3; + n = pOp->p3; pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); @@ -67236,8 +67569,8 @@ case OP_Copy: { #ifdef SQLITE_DEBUG pOut->pScopyFrom = 0; #endif - REGISTER_TRACE(pOp->p2+pOp->p3-u.ae.n, pOut); - if( (u.ae.n--)==0 ) break; + REGISTER_TRACE(pOp->p2+pOp->p3-n, pOut); + if( (n--)==0 ) break; pOut++; pIn1++; } @@ -67245,6 +67578,7 @@ case OP_Copy: { } /* Opcode: SCopy P1 P2 * * * +** Synopsis: r[P2]=r[P1] ** ** Make a shallow copy of register P1 into register P2. ** @@ -67256,7 +67590,7 @@ case OP_Copy: { ** during the lifetime of the copy. Use OP_Copy to make a complete ** copy. */ -case OP_SCopy: { /* in1, out2 */ +case OP_SCopy: { /* out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); @@ -67264,11 +67598,11 @@ case OP_SCopy: { /* in1, out2 */ #ifdef SQLITE_DEBUG if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1; #endif - REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: ResultRow P1 P2 * * * +** Synopsis: output=r[P1@P2] ** ** The registers P1 through P1+P2-1 contain a single row of ** results. This opcode causes the sqlite3_step() call to terminate @@ -67277,14 +67611,24 @@ case OP_SCopy: { /* in1, out2 */ ** row. */ case OP_ResultRow: { -#if 0 /* local variables moved into u.af */ Mem *pMem; int i; -#endif /* local variables moved into u.af */ assert( p->nResColumn==pOp->p2 ); assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=(p->nMem-p->nCursor)+1 ); +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + /* Run the progress counter just before returning. + */ + if( db->xProgress!=0 + && nVmStep>=nProgressLimit + && db->xProgress(db->pProgressArg)!=0 + ){ + rc = SQLITE_INTERRUPT; + goto vdbe_error_halt; + } +#endif + /* If this statement has violated immediate foreign key constraints, do ** not return the number of rows modified. And do not RELEASE the statement ** transaction. It needs to be rolled back. */ @@ -67294,8 +67638,8 @@ case OP_ResultRow: { break; } - /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then - ** DML statements invoke this opcode to return the number of rows + /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then + ** DML statements invoke this opcode to return the number of rows ** modified to the user. This is the only way that a VM that ** opens a statement transaction may invoke this opcode. ** @@ -67322,15 +67666,15 @@ case OP_ResultRow: { ** and have an assigned type. The results are de-ephemeralized as ** a side effect. */ - u.af.pMem = p->pResultSet = &aMem[pOp->p1]; - for(u.af.i=0; u.af.ip2; u.af.i++){ - assert( memIsValid(&u.af.pMem[u.af.i]) ); - Deephemeralize(&u.af.pMem[u.af.i]); - assert( (u.af.pMem[u.af.i].flags & MEM_Ephem)==0 - || (u.af.pMem[u.af.i].flags & (MEM_Str|MEM_Blob))==0 ); - sqlite3VdbeMemNulTerminate(&u.af.pMem[u.af.i]); - sqlite3VdbeMemStoreType(&u.af.pMem[u.af.i]); - REGISTER_TRACE(pOp->p1+u.af.i, &u.af.pMem[u.af.i]); + pMem = p->pResultSet = &aMem[pOp->p1]; + for(i=0; ip2; i++){ + assert( memIsValid(&pMem[i]) ); + Deephemeralize(&pMem[i]); + assert( (pMem[i].flags & MEM_Ephem)==0 + || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); + sqlite3VdbeMemNulTerminate(&pMem[i]); + sqlite3VdbeMemStoreType(&pMem[i]); + REGISTER_TRACE(pOp->p1+i, &pMem[i]); } if( db->mallocFailed ) goto no_mem; @@ -67342,6 +67686,7 @@ case OP_ResultRow: { } /* Opcode: Concat P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]+r[P1] ** ** Add the text in register P1 onto the end of the text in ** register P2 and store the result in register P3. @@ -67354,9 +67699,7 @@ case OP_ResultRow: { ** to avoid a memcpy(). */ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ -#if 0 /* local variables moved into u.ag */ i64 nByte; -#endif /* local variables moved into u.ag */ pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; @@ -67369,34 +67712,36 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ if( ExpandBlob(pIn1) || ExpandBlob(pIn2) ) goto no_mem; Stringify(pIn1, encoding); Stringify(pIn2, encoding); - u.ag.nByte = pIn1->n + pIn2->n; - if( u.ag.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + nByte = pIn1->n + pIn2->n; + if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } MemSetTypeFlag(pOut, MEM_Str); - if( sqlite3VdbeMemGrow(pOut, (int)u.ag.nByte+2, pOut==pIn2) ){ + if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){ goto no_mem; } if( pOut!=pIn2 ){ memcpy(pOut->z, pIn2->z, pIn2->n); } memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n); - pOut->z[u.ag.nByte] = 0; - pOut->z[u.ag.nByte+1] = 0; + pOut->z[nByte]=0; + pOut->z[nByte+1] = 0; pOut->flags |= MEM_Term; - pOut->n = (int)u.ag.nByte; + pOut->n = (int)nByte; pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Add P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]+r[P2] ** ** Add the value in register P1 to the value in register P2 ** and store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: Multiply P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]*r[P2] ** ** ** Multiply the value in register P1 by the value in register P2 @@ -67404,12 +67749,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ ** If either input is NULL, the result is NULL. */ /* Opcode: Subtract P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]-r[P1] ** ** Subtract the value in register P1 from the value in register P2 ** and store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: Divide P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]/r[P1] ** ** Divide the value in register P1 by the value in register P2 ** and store the result in register P3 (P3=P2/P1). If the value in @@ -67417,10 +67764,11 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ ** NULL, the result is NULL. */ /* Opcode: Remainder P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]%r[P1] ** -** Compute the remainder after integer division of the value in -** register P1 by the value in register P2 and store the result in P3. -** If the value in register P2 is zero the result is NULL. +** Compute the remainder after integer register P2 is divided by +** register P1 and store the result in register P3. +** If the value in register P1 is zero the result is NULL. ** If either operand is NULL, the result is NULL. */ case OP_Add: /* same as TK_PLUS, in1, in2, out3 */ @@ -67428,79 +67776,77 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */ case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */ case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ -#if 0 /* local variables moved into u.ah */ char bIntint; /* Started out as two integer operands */ int flags; /* Combined MEM_* flags from both inputs */ i64 iA; /* Integer value of left operand */ i64 iB; /* Integer value of right operand */ double rA; /* Real value of left operand */ double rB; /* Real value of right operand */ -#endif /* local variables moved into u.ah */ pIn1 = &aMem[pOp->p1]; applyNumericAffinity(pIn1); pIn2 = &aMem[pOp->p2]; applyNumericAffinity(pIn2); pOut = &aMem[pOp->p3]; - u.ah.flags = pIn1->flags | pIn2->flags; - if( (u.ah.flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; + flags = pIn1->flags | pIn2->flags; + if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ - u.ah.iA = pIn1->u.i; - u.ah.iB = pIn2->u.i; - u.ah.bIntint = 1; + iA = pIn1->u.i; + iB = pIn2->u.i; + bIntint = 1; switch( pOp->opcode ){ - case OP_Add: if( sqlite3AddInt64(&u.ah.iB,u.ah.iA) ) goto fp_math; break; - case OP_Subtract: if( sqlite3SubInt64(&u.ah.iB,u.ah.iA) ) goto fp_math; break; - case OP_Multiply: if( sqlite3MulInt64(&u.ah.iB,u.ah.iA) ) goto fp_math; break; + case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break; + case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break; + case OP_Multiply: if( sqlite3MulInt64(&iB,iA) ) goto fp_math; break; case OP_Divide: { - if( u.ah.iA==0 ) goto arithmetic_result_is_null; - if( u.ah.iA==-1 && u.ah.iB==SMALLEST_INT64 ) goto fp_math; - u.ah.iB /= u.ah.iA; + if( iA==0 ) goto arithmetic_result_is_null; + if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math; + iB /= iA; break; } default: { - if( u.ah.iA==0 ) goto arithmetic_result_is_null; - if( u.ah.iA==-1 ) u.ah.iA = 1; - u.ah.iB %= u.ah.iA; + if( iA==0 ) goto arithmetic_result_is_null; + if( iA==-1 ) iA = 1; + iB %= iA; break; } } - pOut->u.i = u.ah.iB; + pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); }else{ - u.ah.bIntint = 0; + bIntint = 0; fp_math: - u.ah.rA = sqlite3VdbeRealValue(pIn1); - u.ah.rB = sqlite3VdbeRealValue(pIn2); + rA = sqlite3VdbeRealValue(pIn1); + rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ - case OP_Add: u.ah.rB += u.ah.rA; break; - case OP_Subtract: u.ah.rB -= u.ah.rA; break; - case OP_Multiply: u.ah.rB *= u.ah.rA; break; + case OP_Add: rB += rA; break; + case OP_Subtract: rB -= rA; break; + case OP_Multiply: rB *= rA; break; case OP_Divide: { /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - if( u.ah.rA==(double)0 ) goto arithmetic_result_is_null; - u.ah.rB /= u.ah.rA; + if( rA==(double)0 ) goto arithmetic_result_is_null; + rB /= rA; break; } default: { - u.ah.iA = (i64)u.ah.rA; - u.ah.iB = (i64)u.ah.rB; - if( u.ah.iA==0 ) goto arithmetic_result_is_null; - if( u.ah.iA==-1 ) u.ah.iA = 1; - u.ah.rB = (double)(u.ah.iB % u.ah.iA); + iA = (i64)rA; + iB = (i64)rB; + if( iA==0 ) goto arithmetic_result_is_null; + if( iA==-1 ) iA = 1; + rB = (double)(iB % iA); break; } } #ifdef SQLITE_OMIT_FLOATING_POINT - pOut->u.i = u.ah.rB; + pOut->u.i = rB; MemSetTypeFlag(pOut, MEM_Int); #else - if( sqlite3IsNaN(u.ah.rB) ){ + if( sqlite3IsNaN(rB) ){ goto arithmetic_result_is_null; } - pOut->r = u.ah.rB; + pOut->r = rB; MemSetTypeFlag(pOut, MEM_Real); - if( (u.ah.flags & MEM_Real)==0 && !u.ah.bIntint ){ + if( (flags & MEM_Real)==0 && !bIntint ){ sqlite3VdbeIntegerAffinity(pOut); } #endif @@ -67536,6 +67882,7 @@ case OP_CollSeq: { } /* Opcode: Function P1 P2 P3 P4 P5 +** Synopsis: r[P3]=func(r[P2@P5]) ** ** Invoke a user function (P4 is a pointer to a Function structure that ** defines the function) with P5 arguments taken from register P2 and @@ -67552,57 +67899,54 @@ case OP_CollSeq: { ** See also: AggStep and AggFinal */ case OP_Function: { -#if 0 /* local variables moved into u.ai */ int i; Mem *pArg; sqlite3_context ctx; sqlite3_value **apVal; int n; -#endif /* local variables moved into u.ai */ - u.ai.n = pOp->p5; - u.ai.apVal = p->apArg; - assert( u.ai.apVal || u.ai.n==0 ); + n = pOp->p5; + apVal = p->apArg; + assert( apVal || n==0 ); assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); - assert( u.ai.n==0 || (pOp->p2>0 && pOp->p2+u.ai.n<=(p->nMem-p->nCursor)+1) ); - assert( pOp->p3p2 || pOp->p3>=pOp->p2+u.ai.n ); - u.ai.pArg = &aMem[pOp->p2]; - for(u.ai.i=0; u.ai.ip2+u.ai.i, u.ai.pArg); + assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) ); + assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); + pArg = &aMem[pOp->p2]; + for(i=0; ip2+i, pArg); } assert( pOp->p4type==P4_FUNCDEF ); - u.ai.ctx.pFunc = pOp->p4.pFunc; - u.ai.ctx.s.flags = MEM_Null; - u.ai.ctx.s.db = db; - u.ai.ctx.s.xDel = 0; - u.ai.ctx.s.zMalloc = 0; - u.ai.ctx.iOp = pc; - u.ai.ctx.pVdbe = p; + ctx.pFunc = pOp->p4.pFunc; + ctx.iOp = pc; + ctx.pVdbe = p; /* The output cell may already have a buffer allocated. Move - ** the pointer to u.ai.ctx.s so in case the user-function can use + ** the pointer to ctx.s so in case the user-function can use ** the already allocated buffer instead of allocating a new one. */ - sqlite3VdbeMemMove(&u.ai.ctx.s, pOut); - MemSetTypeFlag(&u.ai.ctx.s, MEM_Null); + memcpy(&ctx.s, pOut, sizeof(Mem)); + pOut->flags = MEM_Null; + pOut->xDel = 0; + pOut->zMalloc = 0; + MemSetTypeFlag(&ctx.s, MEM_Null); - u.ai.ctx.fErrorOrAux = 0; - if( u.ai.ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ + ctx.fErrorOrAux = 0; + if( ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); - u.ai.ctx.pColl = pOp[-1].p4.pColl; + ctx.pColl = pOp[-1].p4.pColl; } db->lastRowid = lastRowid; - (*u.ai.ctx.pFunc->xFunc)(&u.ai.ctx, u.ai.n, u.ai.apVal); /* IMP: R-24505-23230 */ + (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */ lastRowid = db->lastRowid; if( db->mallocFailed ){ @@ -67611,22 +67955,23 @@ case OP_Function: { ** to return a value. The following call releases any resources ** associated with such a value. */ - sqlite3VdbeMemRelease(&u.ai.ctx.s); + sqlite3VdbeMemRelease(&ctx.s); goto no_mem; } /* If the function returned an error, throw an exception */ - if( u.ai.ctx.fErrorOrAux ){ - if( u.ai.ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ai.ctx.s)); - rc = u.ai.ctx.isError; + if( ctx.fErrorOrAux ){ + if( ctx.isError ){ + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); + rc = ctx.isError; } sqlite3VdbeDeleteAuxData(p, pc, pOp->p1); } /* Copy the result of the function into register P3 */ - sqlite3VdbeChangeEncoding(&u.ai.ctx.s, encoding); - sqlite3VdbeMemMove(pOut, &u.ai.ctx.s); + sqlite3VdbeChangeEncoding(&ctx.s, encoding); + assert( pOut->flags==MEM_Null ); + memcpy(pOut, &ctx.s, sizeof(Mem)); if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } @@ -67645,18 +67990,21 @@ case OP_Function: { } /* Opcode: BitAnd P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]&r[P2] ** ** Take the bit-wise AND of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: BitOr P1 P2 P3 * * +** Synopsis: r[P3]=r[P1]|r[P2] ** ** Take the bit-wise OR of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftLeft P1 P2 P3 * * +** Synopsis: r[P3]=r[P2]<>r[P1] ** ** Shift the integer value in register P2 to the right by the ** number of bits specified by the integer in register P1. @@ -67674,12 +68023,10 @@ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ -#if 0 /* local variables moved into u.aj */ i64 iA; u64 uA; i64 iB; u8 op; -#endif /* local variables moved into u.aj */ pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; @@ -67688,43 +68035,44 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ sqlite3VdbeMemSetNull(pOut); break; } - u.aj.iA = sqlite3VdbeIntValue(pIn2); - u.aj.iB = sqlite3VdbeIntValue(pIn1); - u.aj.op = pOp->opcode; - if( u.aj.op==OP_BitAnd ){ - u.aj.iA &= u.aj.iB; - }else if( u.aj.op==OP_BitOr ){ - u.aj.iA |= u.aj.iB; - }else if( u.aj.iB!=0 ){ - assert( u.aj.op==OP_ShiftRight || u.aj.op==OP_ShiftLeft ); + iA = sqlite3VdbeIntValue(pIn2); + iB = sqlite3VdbeIntValue(pIn1); + op = pOp->opcode; + if( op==OP_BitAnd ){ + iA &= iB; + }else if( op==OP_BitOr ){ + iA |= iB; + }else if( iB!=0 ){ + assert( op==OP_ShiftRight || op==OP_ShiftLeft ); /* If shifting by a negative amount, shift in the other direction */ - if( u.aj.iB<0 ){ + if( iB<0 ){ assert( OP_ShiftRight==OP_ShiftLeft+1 ); - u.aj.op = 2*OP_ShiftLeft + 1 - u.aj.op; - u.aj.iB = u.aj.iB>(-64) ? -u.aj.iB : 64; + op = 2*OP_ShiftLeft + 1 - op; + iB = iB>(-64) ? -iB : 64; } - if( u.aj.iB>=64 ){ - u.aj.iA = (u.aj.iA>=0 || u.aj.op==OP_ShiftLeft) ? 0 : -1; + if( iB>=64 ){ + iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1; }else{ - memcpy(&u.aj.uA, &u.aj.iA, sizeof(u.aj.uA)); - if( u.aj.op==OP_ShiftLeft ){ - u.aj.uA <<= u.aj.iB; + memcpy(&uA, &iA, sizeof(uA)); + if( op==OP_ShiftLeft ){ + uA <<= iB; }else{ - u.aj.uA >>= u.aj.iB; + uA >>= iB; /* Sign-extend on a right shift of a negative number */ - if( u.aj.iA<0 ) u.aj.uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-u.aj.iB); + if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB); } - memcpy(&u.aj.iA, &u.aj.uA, sizeof(u.aj.iA)); + memcpy(&iA, &uA, sizeof(iA)); } } - pOut->u.i = u.aj.iA; + pOut->u.i = iA; MemSetTypeFlag(pOut, MEM_Int); break; } /* Opcode: AddImm P1 P2 * * * +** Synopsis: r[P1]=r[P1]+P2 ** ** Add the constant P2 to the value in register P1. ** The result is always an integer. @@ -67748,17 +68096,19 @@ case OP_AddImm: { /* in1 */ */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; - applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ - if( pOp->p2==0 ){ - rc = SQLITE_MISMATCH; - goto abort_due_to_error; - }else{ - pc = pOp->p2 - 1; + applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); + if( (pIn1->flags & MEM_Int)==0 ){ + if( pOp->p2==0 ){ + rc = SQLITE_MISMATCH; + goto abort_due_to_error; + }else{ + pc = pOp->p2 - 1; + break; + } } - }else{ - MemSetTypeFlag(pIn1, MEM_Int); } + MemSetTypeFlag(pIn1, MEM_Int); break; } @@ -67883,6 +68233,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ #endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 +** Synopsis: if r[P1]r[P3] goto P2 ** ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is greater than the content of ** register P1. See the Lt opcode for additional information. */ /* Opcode: Ge P1 P2 P3 P4 P5 +** Synopsis: if r[P1]>=r[P3] goto P2 ** ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is greater than or equal to the content of @@ -67964,18 +68320,16 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */ case OP_Le: /* same as TK_LE, jump, in1, in3 */ case OP_Gt: /* same as TK_GT, jump, in1, in3 */ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ -#if 0 /* local variables moved into u.ak */ int res; /* Result of the comparison of pIn1 against pIn3 */ char affinity; /* Affinity to use for comparison */ u16 flags1; /* Copy of initial value of pIn1->flags */ u16 flags3; /* Copy of initial value of pIn3->flags */ -#endif /* local variables moved into u.ak */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; - u.ak.flags1 = pIn1->flags; - u.ak.flags3 = pIn3->flags; - if( (u.ak.flags1 | u.ak.flags3)&MEM_Null ){ + flags1 = pIn1->flags; + flags3 = pIn3->flags; + if( (flags1 | flags3)&MEM_Null ){ /* One or both operands are NULL */ if( pOp->p5 & SQLITE_NULLEQ ){ /* If SQLITE_NULLEQ is set (which will only happen if the operator is @@ -67983,14 +68337,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** or not both operands are null. */ assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); - assert( (u.ak.flags1 & MEM_Cleared)==0 ); - if( (u.ak.flags1&MEM_Null)!=0 - && (u.ak.flags3&MEM_Null)!=0 - && (u.ak.flags3&MEM_Cleared)==0 + assert( (flags1 & MEM_Cleared)==0 ); + if( (flags1&MEM_Null)!=0 + && (flags3&MEM_Null)!=0 + && (flags3&MEM_Cleared)==0 ){ - u.ak.res = 0; /* Results are equal */ + res = 0; /* Results are equal */ }else{ - u.ak.res = 1; /* Results are not equal */ + res = 1; /* Results are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, @@ -68008,40 +68362,40 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ } }else{ /* Neither operand is NULL. Do a comparison. */ - u.ak.affinity = pOp->p5 & SQLITE_AFF_MASK; - if( u.ak.affinity ){ - applyAffinity(pIn1, u.ak.affinity, encoding); - applyAffinity(pIn3, u.ak.affinity, encoding); + affinity = pOp->p5 & SQLITE_AFF_MASK; + if( affinity ){ + applyAffinity(pIn1, affinity, encoding); + applyAffinity(pIn3, affinity, encoding); if( db->mallocFailed ) goto no_mem; } assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); ExpandBlob(pIn1); ExpandBlob(pIn3); - u.ak.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } switch( pOp->opcode ){ - case OP_Eq: u.ak.res = u.ak.res==0; break; - case OP_Ne: u.ak.res = u.ak.res!=0; break; - case OP_Lt: u.ak.res = u.ak.res<0; break; - case OP_Le: u.ak.res = u.ak.res<=0; break; - case OP_Gt: u.ak.res = u.ak.res>0; break; - default: u.ak.res = u.ak.res>=0; break; + case OP_Eq: res = res==0; break; + case OP_Ne: res = res!=0; break; + case OP_Lt: res = res<0; break; + case OP_Le: res = res<=0; break; + case OP_Gt: res = res>0; break; + default: res = res>=0; break; } if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = u.ak.res; + pOut->u.i = res; REGISTER_TRACE(pOp->p2, pOut); - }else if( u.ak.res ){ + }else if( res ){ pc = pOp->p2-1; } /* Undo any changes made by applyAffinity() to the input registers. */ - pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (u.ak.flags1&MEM_TypeMask); - pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (u.ak.flags3&MEM_TypeMask); + pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (flags1&MEM_TypeMask); + pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (flags3&MEM_TypeMask); break; } @@ -68081,7 +68435,6 @@ case OP_Permutation: { ** and strings are less than blobs. */ case OP_Compare: { -#if 0 /* local variables moved into u.al */ int n; int i; int p1; @@ -68090,38 +68443,37 @@ case OP_Compare: { int idx; CollSeq *pColl; /* Collating sequence to use on this term */ int bRev; /* True for DESCENDING sort order */ -#endif /* local variables moved into u.al */ if( (pOp->p5 & OPFLAG_PERMUTE)==0 ) aPermute = 0; - u.al.n = pOp->p3; - u.al.pKeyInfo = pOp->p4.pKeyInfo; - assert( u.al.n>0 ); - assert( u.al.pKeyInfo!=0 ); - u.al.p1 = pOp->p1; - u.al.p2 = pOp->p2; + n = pOp->p3; + pKeyInfo = pOp->p4.pKeyInfo; + assert( n>0 ); + assert( pKeyInfo!=0 ); + p1 = pOp->p1; + p2 = pOp->p2; #if SQLITE_DEBUG if( aPermute ){ int k, mx = 0; - for(k=0; kmx ) mx = aPermute[k]; - assert( u.al.p1>0 && u.al.p1+mx<=(p->nMem-p->nCursor)+1 ); - assert( u.al.p2>0 && u.al.p2+mx<=(p->nMem-p->nCursor)+1 ); + for(k=0; kmx ) mx = aPermute[k]; + assert( p1>0 && p1+mx<=(p->nMem-p->nCursor)+1 ); + assert( p2>0 && p2+mx<=(p->nMem-p->nCursor)+1 ); }else{ - assert( u.al.p1>0 && u.al.p1+u.al.n<=(p->nMem-p->nCursor)+1 ); - assert( u.al.p2>0 && u.al.p2+u.al.n<=(p->nMem-p->nCursor)+1 ); + assert( p1>0 && p1+n<=(p->nMem-p->nCursor)+1 ); + assert( p2>0 && p2+n<=(p->nMem-p->nCursor)+1 ); } #endif /* SQLITE_DEBUG */ - for(u.al.i=0; u.al.inField ); - u.al.pColl = u.al.pKeyInfo->aColl[u.al.i]; - u.al.bRev = u.al.pKeyInfo->aSortOrder[u.al.i]; - iCompare = sqlite3MemCompare(&aMem[u.al.p1+u.al.idx], &aMem[u.al.p2+u.al.idx], u.al.pColl); + for(i=0; inField ); + pColl = pKeyInfo->aColl[i]; + bRev = pKeyInfo->aSortOrder[i]; + iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); if( iCompare ){ - if( u.al.bRev ) iCompare = -iCompare; + if( bRev ) iCompare = -iCompare; break; } } @@ -68147,6 +68499,7 @@ case OP_Jump: { /* jump */ } /* Opcode: And P1 P2 P3 * * +** Synopsis: r[P3]=(r[P1] && r[P2]) ** ** Take the logical AND of the values in registers P1 and P2 and ** write the result into register P3. @@ -68156,6 +68509,7 @@ case OP_Jump: { /* jump */ ** a NULL output. */ /* Opcode: Or P1 P2 P3 * * +** Synopsis: r[P3]=(r[P1] || r[P2]) ** ** Take the logical OR of the values in register P1 and P2 and ** store the answer in register P3. @@ -68166,41 +68520,40 @@ case OP_Jump: { /* jump */ */ case OP_And: /* same as TK_AND, in1, in2, out3 */ case OP_Or: { /* same as TK_OR, in1, in2, out3 */ -#if 0 /* local variables moved into u.am */ int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ -#endif /* local variables moved into u.am */ pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ){ - u.am.v1 = 2; + v1 = 2; }else{ - u.am.v1 = sqlite3VdbeIntValue(pIn1)!=0; + v1 = sqlite3VdbeIntValue(pIn1)!=0; } pIn2 = &aMem[pOp->p2]; if( pIn2->flags & MEM_Null ){ - u.am.v2 = 2; + v2 = 2; }else{ - u.am.v2 = sqlite3VdbeIntValue(pIn2)!=0; + v2 = sqlite3VdbeIntValue(pIn2)!=0; } if( pOp->opcode==OP_And ){ static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; - u.am.v1 = and_logic[u.am.v1*3+u.am.v2]; + v1 = and_logic[v1*3+v2]; }else{ static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; - u.am.v1 = or_logic[u.am.v1*3+u.am.v2]; + v1 = or_logic[v1*3+v2]; } pOut = &aMem[pOp->p3]; - if( u.am.v1==2 ){ + if( v1==2 ){ MemSetTypeFlag(pOut, MEM_Null); }else{ - pOut->u.i = u.am.v1; + pOut->u.i = v1; MemSetTypeFlag(pOut, MEM_Int); } break; } /* Opcode: Not P1 P2 * * * +** Synopsis: r[P2]= !r[P1] ** ** Interpret the value in register P1 as a boolean value. Store the ** boolean complement in register P2. If the value in register P1 is @@ -68218,6 +68571,7 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */ } /* Opcode: BitNot P1 P2 * * * +** Synopsis: r[P1]= ~r[P1] ** ** Interpret the content of register P1 as an integer. Store the ** ones-complement of the P1 value into register P2. If P1 holds @@ -68263,27 +68617,26 @@ case OP_Once: { /* jump */ */ case OP_If: /* jump, in1 */ case OP_IfNot: { /* jump, in1 */ -#if 0 /* local variables moved into u.an */ int c; -#endif /* local variables moved into u.an */ pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ){ - u.an.c = pOp->p3; + c = pOp->p3; }else{ #ifdef SQLITE_OMIT_FLOATING_POINT - u.an.c = sqlite3VdbeIntValue(pIn1)!=0; + c = sqlite3VdbeIntValue(pIn1)!=0; #else - u.an.c = sqlite3VdbeRealValue(pIn1)!=0.0; + c = sqlite3VdbeRealValue(pIn1)!=0.0; #endif - if( pOp->opcode==OP_IfNot ) u.an.c = !u.an.c; + if( pOp->opcode==OP_IfNot ) c = !c; } - if( u.an.c ){ + if( c ){ pc = pOp->p2-1; } break; } /* Opcode: IsNull P1 P2 * * * +** Synopsis: if r[P1]==NULL goto P2 ** ** Jump to P2 if the value in register P1 is NULL. */ @@ -68296,6 +68649,7 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ } /* Opcode: NotNull P1 P2 * * * +** Synopsis: if r[P1]!=NULL goto P2 ** ** Jump to P2 if the value in register P1 is not NULL. */ @@ -68308,6 +68662,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ } /* Opcode: Column P1 P2 P3 P4 P5 +** Synopsis: r[P3]=PX ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional @@ -68332,155 +68687,103 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { -#if 0 /* local variables moved into u.ao */ - u32 payloadSize; /* Number of bytes in the record */ i64 payloadSize64; /* Number of bytes in the record */ - int p1; /* P1 value of the opcode */ int p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ BtCursor *pCrsr; /* The BTree cursor */ u32 *aType; /* aType[i] holds the numeric type of the i-th column */ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ - int nField; /* number of fields in the record */ int len; /* The length of the serialized data for the column */ int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ Mem *pDest; /* Where to write the extracted value */ Mem sMem; /* For storing the record being decoded */ - u8 *zIdx; /* Index into header */ - u8 *zEndHdr; /* Pointer to first byte after the header */ + const u8 *zData; /* Part of the record being decoded */ + const u8 *zHdr; /* Next unparsed byte of the header */ + const u8 *zEndHdr; /* Pointer to first byte after the header */ u32 offset; /* Offset into the data */ u32 szField; /* Number of bytes in the content of a field */ - int szHdr; /* Size of the header size field at start of record */ - int avail; /* Number of bytes of available data */ + u32 avail; /* Number of bytes of available data */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ -#endif /* local variables moved into u.ao */ - - u.ao.p1 = pOp->p1; - u.ao.p2 = pOp->p2; - u.ao.pC = 0; - memset(&u.ao.sMem, 0, sizeof(u.ao.sMem)); - assert( u.ao.p1nCursor ); + p2 = pOp->p2; assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); - u.ao.pDest = &aMem[pOp->p3]; - memAboutToChange(p, u.ao.pDest); - u.ao.zRec = 0; - - /* This block sets the variable u.ao.payloadSize to be the total number of - ** bytes in the record. - ** - ** u.ao.zRec is set to be the complete text of the record if it is available. - ** The complete record text is always available for pseudo-tables - ** If the record is stored in a cursor, the complete record text - ** might be available in the u.ao.pC->aRow cache. Or it might not be. - ** If the data is unavailable, u.ao.zRec is set to NULL. - ** - ** We also compute the number of columns in the record. For cursors, - ** the number of columns is stored in the VdbeCursor.nField element. - */ - u.ao.pC = p->apCsr[u.ao.p1]; - assert( u.ao.pC!=0 ); + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( p2nField ); + aType = pC->aType; + aOffset = aType + pC->nField; #ifndef SQLITE_OMIT_VIRTUALTABLE - assert( u.ao.pC->pVtabCursor==0 ); + assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */ #endif - u.ao.pCrsr = u.ao.pC->pCursor; - if( u.ao.pCrsr!=0 ){ - /* The record is stored in a B-Tree */ - rc = sqlite3VdbeCursorMoveto(u.ao.pC); - if( rc ) goto abort_due_to_error; - if( u.ao.pC->nullRow ){ - u.ao.payloadSize = 0; - }else if( u.ao.pC->cacheStatus==p->cacheCtr ){ - u.ao.payloadSize = u.ao.pC->payloadSize; - u.ao.zRec = (char*)u.ao.pC->aRow; - }else if( u.ao.pC->isIndex ){ - assert( sqlite3BtreeCursorIsValid(u.ao.pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(u.ao.pCrsr, &u.ao.payloadSize64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the - ** payload size, so it is impossible for u.ao.payloadSize64 to be - ** larger than 32 bits. */ - assert( (u.ao.payloadSize64 & SQLITE_MAX_U32)==(u64)u.ao.payloadSize64 ); - u.ao.payloadSize = (u32)u.ao.payloadSize64; - }else{ - assert( sqlite3BtreeCursorIsValid(u.ao.pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeDataSize(u.ao.pCrsr, &u.ao.payloadSize); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - } - }else if( ALWAYS(u.ao.pC->pseudoTableReg>0) ){ - u.ao.pReg = &aMem[u.ao.pC->pseudoTableReg]; - if( u.ao.pC->multiPseudo ){ - sqlite3VdbeMemShallowCopy(u.ao.pDest, u.ao.pReg+u.ao.p2, MEM_Ephem); - Deephemeralize(u.ao.pDest); - goto op_column_out; - } - assert( u.ao.pReg->flags & MEM_Blob ); - assert( memIsValid(u.ao.pReg) ); - u.ao.payloadSize = u.ao.pReg->n; - u.ao.zRec = u.ao.pReg->z; - u.ao.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; - assert( u.ao.payloadSize==0 || u.ao.zRec!=0 ); - }else{ - /* Consider the row to be NULL */ - u.ao.payloadSize = 0; - } + pCrsr = pC->pCursor; + assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */ + assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */ - /* If u.ao.payloadSize is 0, then just store a NULL. This can happen because of - ** nullRow or because of a corrupt database. */ - if( u.ao.payloadSize==0 ){ - MemSetTypeFlag(u.ao.pDest, MEM_Null); - goto op_column_out; - } - assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 ); - if( u.ao.payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - - u.ao.nField = u.ao.pC->nField; - assert( u.ao.p2aType; - if( u.ao.pC->cacheStatus==p->cacheCtr ){ - u.ao.aOffset = u.ao.pC->aOffset; - }else{ - assert(u.ao.aType); - u.ao.avail = 0; - u.ao.pC->aOffset = u.ao.aOffset = &u.ao.aType[u.ao.nField]; - u.ao.pC->payloadSize = u.ao.payloadSize; - u.ao.pC->cacheStatus = p->cacheCtr; - - /* Figure out how many bytes are in the header */ - if( u.ao.zRec ){ - u.ao.zData = u.ao.zRec; - }else{ - if( u.ao.pC->isIndex ){ - u.ao.zData = (char*)sqlite3BtreeKeyFetch(u.ao.pCrsr, &u.ao.avail); + /* If the cursor cache is stale, bring it up-to-date */ + rc = sqlite3VdbeCursorMoveto(pC); + if( rc ) goto abort_due_to_error; + if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){ + if( pC->nullRow ){ + if( pCrsr==0 ){ + assert( pC->pseudoTableReg>0 ); + pReg = &aMem[pC->pseudoTableReg]; + if( pC->multiPseudo ){ + sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); + Deephemeralize(pDest); + goto op_column_out; + } + assert( pReg->flags & MEM_Blob ); + assert( memIsValid(pReg) ); + pC->payloadSize = pC->szRow = avail = pReg->n; + pC->aRow = (u8*)pReg->z; }else{ - u.ao.zData = (char*)sqlite3BtreeDataFetch(u.ao.pCrsr, &u.ao.avail); + MemSetTypeFlag(pDest, MEM_Null); + goto op_column_out; } - /* If KeyFetch()/DataFetch() managed to get the entire payload, - ** save the payload in the u.ao.pC->aRow cache. That will save us from - ** having to make additional calls to fetch the content portion of - ** the record. - */ - assert( u.ao.avail>=0 ); - if( u.ao.payloadSize <= (u32)u.ao.avail ){ - u.ao.zRec = u.ao.zData; - u.ao.pC->aRow = (u8*)u.ao.zData; + }else{ + assert( pCrsr ); + if( pC->isTable==0 ){ + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ + /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the + ** payload size, so it is impossible for payloadSize64 to be + ** larger than 32 bits. */ + assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); + pC->aRow = sqlite3BtreeKeyFetch(pCrsr, &avail); + pC->payloadSize = (u32)payloadSize64; }else{ - u.ao.pC->aRow = 0; + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &pC->payloadSize); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ + pC->aRow = sqlite3BtreeDataFetch(pCrsr, &avail); + } + assert( avail<=65536 ); /* Maximum page size is 64KiB */ + if( pC->payloadSize <= (u32)avail ){ + pC->szRow = pC->payloadSize; + }else{ + pC->szRow = avail; + } + if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; } } - /* The following assert is true in all cases except when - ** the database file has been corrupted externally. - ** assert( u.ao.zRec!=0 || u.ao.avail>=u.ao.payloadSize || u.ao.avail>=9 ); */ - u.ao.szHdr = getVarint32((u8*)u.ao.zData, u.ao.offset); + pC->cacheStatus = p->cacheCtr; + pC->iHdrOffset = getVarint32(pC->aRow, offset); + pC->nHdrParsed = 0; + aOffset[0] = offset; + if( availaRow does not have to hold the entire row, but it does at least + ** need to cover the header of the record. If pC->aRow does not contain + ** the complete header, then set it to zero, forcing the header to be + ** dynamically allocated. */ + pC->aRow = 0; + pC->szRow = 0; + } /* Make sure a corrupt database has not given us an oversize header. ** Do this now to avoid an oversize memory allocation. @@ -68491,161 +68794,155 @@ case OP_Column: { ** 3-byte type for each of the maximum of 32768 columns plus three ** extra bytes for the header length itself. 32768*3 + 3 = 98307. */ - if( u.ao.offset > 98307 ){ + if( offset > 98307 || offset > pC->payloadSize ){ rc = SQLITE_CORRUPT_BKPT; - goto op_column_out; + goto op_column_error; } + } - /* Compute in u.ao.len the number of bytes of data we need to read in order - ** to get u.ao.nField type values. u.ao.offset is an upper bound on this. But - ** u.ao.nField might be significantly less than the true number of columns - ** in the table, and in that case, 5*u.ao.nField+3 might be smaller than u.ao.offset. - ** We want to minimize u.ao.len in order to limit the size of the memory - ** allocation, especially if a corrupt database file has caused u.ao.offset - ** to be oversized. Offset is limited to 98307 above. But 98307 might - ** still exceed Robson memory allocation limits on some configurations. - ** On systems that cannot tolerate large memory allocations, u.ao.nField*5+3 - ** will likely be much smaller since u.ao.nField will likely be less than - ** 20 or so. This insures that Robson memory allocation limits are - ** not exceeded even for corrupt database files. + /* Make sure at least the first p2+1 entries of the header have been + ** parsed and valid information is in aOffset[] and aType[]. + */ + if( pC->nHdrParsed<=p2 ){ + /* If there is more header available for parsing in the record, try + ** to extract additional fields up through the p2+1-th field */ - u.ao.len = u.ao.nField*5 + 3; - if( u.ao.len > (int)u.ao.offset ) u.ao.len = (int)u.ao.offset; - - /* The KeyFetch() or DataFetch() above are fast and will get the entire - ** record header in most cases. But they will fail to get the complete - ** record header if the record header does not fit on a single page - ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to - ** acquire the complete header text. - */ - if( !u.ao.zRec && u.ao.availisIndex, &u.ao.sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; - } - u.ao.zData = u.ao.sMem.z; - } - u.ao.zEndHdr = (u8 *)&u.ao.zData[u.ao.len]; - u.ao.zIdx = (u8 *)&u.ao.zData[u.ao.szHdr]; - - /* Scan the header and use it to fill in the u.ao.aType[] and u.ao.aOffset[] - ** arrays. u.ao.aType[u.ao.i] will contain the type integer for the u.ao.i-th - ** column and u.ao.aOffset[u.ao.i] will contain the u.ao.offset from the beginning - ** of the record to the start of the data for the u.ao.i-th column - */ - for(u.ao.i=0; u.ao.iiHdrOffsetaRow==0 ){ + memset(&sMem, 0, sizeof(sMem)); + rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0], + !pC->isTable, &sMem); + if( rc!=SQLITE_OK ){ + goto op_column_error; } - u.ao.aType[u.ao.i] = u.ao.t; - u.ao.szField = sqlite3VdbeSerialTypeLen(u.ao.t); - u.ao.offset += u.ao.szField; - if( u.ao.offsetaRow; + } + + /* Fill in aType[i] and aOffset[i] values through the p2-th field. */ + i = pC->nHdrParsed; + offset = aOffset[i]; + zHdr = zData + pC->iHdrOffset; + zEndHdr = zData + aOffset[0]; + assert( i<=p2 && zHdrnHdrParsed = i; + pC->iHdrOffset = (u32)(zHdr - zData); + if( pC->aRow==0 ){ + sqlite3VdbeMemRelease(&sMem); + sMem.flags = MEM_Null; + } + + /* If we have read more header data than was contained in the header, + ** or if the end of the last field appears to be past the end of the + ** record, or if the end of the last field appears to be before the end + ** of the record (when all fields present), then we must be dealing + ** with a corrupt database. + */ + if( (zHdr > zEndHdr) + || (offset > pC->payloadSize) + || (zHdr==zEndHdr && offset!=pC->payloadSize) + ){ + rc = SQLITE_CORRUPT_BKPT; + goto op_column_error; } } - sqlite3VdbeMemRelease(&u.ao.sMem); - u.ao.sMem.flags = MEM_Null; - /* If we have read more header data than was contained in the header, - ** or if the end of the last field appears to be past the end of the - ** record, or if the end of the last field appears to be before the end - ** of the record (when all fields present), then we must be dealing - ** with a corrupt database. + /* If after trying to extra new entries from the header, nHdrParsed is + ** still not up to p2, that means that the record has fewer than p2 + ** columns. So the result will be either the default value or a NULL. */ - if( (u.ao.zIdx > u.ao.zEndHdr) || (u.ao.offset > u.ao.payloadSize) - || (u.ao.zIdx==u.ao.zEndHdr && u.ao.offset!=u.ao.payloadSize) ){ - rc = SQLITE_CORRUPT_BKPT; + if( pC->nHdrParsed<=p2 ){ + if( pOp->p4type==P4_MEM ){ + sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static); + }else{ + MemSetTypeFlag(pDest, MEM_Null); + } goto op_column_out; } } - /* Get the column information. If u.ao.aOffset[u.ao.p2] is non-zero, then - ** deserialize the value from the record. If u.ao.aOffset[u.ao.p2] is zero, - ** then there are not enough fields in the record to satisfy the - ** request. In this case, set the value NULL or to P4 if P4 is - ** a pointer to a Mem object. + /* Extract the content for the p2+1-th column. Control can only + ** reach this point if aOffset[p2], aOffset[p2+1], and aType[p2] are + ** all valid. */ - if( u.ao.aOffset[u.ao.p2] ){ - assert( rc==SQLITE_OK ); - if( u.ao.zRec ){ - /* This is the common case where the whole row fits on a single page */ - VdbeMemRelease(u.ao.pDest); - sqlite3VdbeSerialGet((u8 *)&u.ao.zRec[u.ao.aOffset[u.ao.p2]], u.ao.aType[u.ao.p2], u.ao.pDest); - }else{ - /* This branch happens only when the row overflows onto multiple pages */ - u.ao.t = u.ao.aType[u.ao.p2]; - if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 - && ((u.ao.t>=12 && (u.ao.t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0) - ){ - /* Content is irrelevant for the typeof() function and for - ** the length(X) function if X is a blob. So we might as well use - ** bogus content rather than reading content from disk. NULL works - ** for text and blob and whatever is in the u.ao.payloadSize64 variable - ** will work for everything else. */ - u.ao.zData = u.ao.t<12 ? (char*)&u.ao.payloadSize64 : 0; - }else{ - u.ao.len = sqlite3VdbeSerialTypeLen(u.ao.t); - sqlite3VdbeMemMove(&u.ao.sMem, u.ao.pDest); - rc = sqlite3VdbeMemFromBtree(u.ao.pCrsr, u.ao.aOffset[u.ao.p2], u.ao.len, u.ao.pC->isIndex, - &u.ao.sMem); - if( rc!=SQLITE_OK ){ - goto op_column_out; - } - u.ao.zData = u.ao.sMem.z; - } - sqlite3VdbeSerialGet((u8*)u.ao.zData, u.ao.t, u.ao.pDest); - } - u.ao.pDest->enc = encoding; + assert( p2nHdrParsed ); + assert( rc==SQLITE_OK ); + if( pC->szRow>=aOffset[p2+1] ){ + /* This is the common case where the desired content fits on the original + ** page - where the content is not on an overflow page */ + VdbeMemRelease(pDest); + sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); }else{ - if( pOp->p4type==P4_MEM ){ - sqlite3VdbeMemShallowCopy(u.ao.pDest, pOp->p4.pMem, MEM_Static); + /* This branch happens only when content is on overflow pages */ + t = aType[p2]; + if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 + && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) + || (len = sqlite3VdbeSerialTypeLen(t))==0 + ){ + /* Content is irrelevant for the typeof() function and for + ** the length(X) function if X is a blob. So we might as well use + ** bogus content rather than reading content from disk. NULL works + ** for text and blob and whatever is in the payloadSize64 variable + ** will work for everything else. Content is also irrelevant if + ** the content length is 0. */ + zData = t<=13 ? (u8*)&payloadSize64 : 0; + sMem.zMalloc = 0; }else{ - MemSetTypeFlag(u.ao.pDest, MEM_Null); + memset(&sMem, 0, sizeof(sMem)); + sqlite3VdbeMemMove(&sMem, pDest); + rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable, + &sMem); + if( rc!=SQLITE_OK ){ + goto op_column_error; + } + zData = (u8*)sMem.z; + } + sqlite3VdbeSerialGet(zData, t, pDest); + /* If we dynamically allocated space to hold the data (in the + ** sqlite3VdbeMemFromBtree() call above) then transfer control of that + ** dynamically allocated space over to the pDest structure. + ** This prevents a memory copy. */ + if( sMem.zMalloc ){ + assert( sMem.z==sMem.zMalloc ); + assert( !(pDest->flags & MEM_Dyn) ); + assert( !(pDest->flags & (MEM_Blob|MEM_Str)) || pDest->z==sMem.z ); + pDest->flags &= ~(MEM_Ephem|MEM_Static); + pDest->flags |= MEM_Term; + pDest->z = sMem.z; + pDest->zMalloc = sMem.zMalloc; } } - - /* If we dynamically allocated space to hold the data (in the - ** sqlite3VdbeMemFromBtree() call above) then transfer control of that - ** dynamically allocated space over to the u.ao.pDest structure. - ** This prevents a memory copy. - */ - if( u.ao.sMem.zMalloc ){ - assert( u.ao.sMem.z==u.ao.sMem.zMalloc ); - assert( !(u.ao.pDest->flags & MEM_Dyn) ); - assert( !(u.ao.pDest->flags & (MEM_Blob|MEM_Str)) || u.ao.pDest->z==u.ao.sMem.z ); - u.ao.pDest->flags &= ~(MEM_Ephem|MEM_Static); - u.ao.pDest->flags |= MEM_Term; - u.ao.pDest->z = u.ao.sMem.z; - u.ao.pDest->zMalloc = u.ao.sMem.zMalloc; - } - - rc = sqlite3VdbeMemMakeWriteable(u.ao.pDest); + pDest->enc = encoding; op_column_out: - UPDATE_MAX_BLOBSIZE(u.ao.pDest); - REGISTER_TRACE(pOp->p3, u.ao.pDest); + Deephemeralize(pDest); +op_column_error: + UPDATE_MAX_BLOBSIZE(pDest); + REGISTER_TRACE(pOp->p3, pDest); break; } /* Opcode: Affinity P1 P2 * P4 * +** Synopsis: affinity(r[P1@P2]) ** ** Apply affinities to a range of P2 registers starting with P1. ** @@ -68654,26 +68951,25 @@ op_column_out: ** memory cell in the range. */ case OP_Affinity: { -#if 0 /* local variables moved into u.ap */ const char *zAffinity; /* The affinity to be applied */ char cAff; /* A single character of affinity */ -#endif /* local variables moved into u.ap */ - u.ap.zAffinity = pOp->p4.z; - assert( u.ap.zAffinity!=0 ); - assert( u.ap.zAffinity[pOp->p2]==0 ); + zAffinity = pOp->p4.z; + assert( zAffinity!=0 ); + assert( zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; - while( (u.ap.cAff = *(u.ap.zAffinity++))!=0 ){ + while( (cAff = *(zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] ); assert( memIsValid(pIn1) ); ExpandBlob(pIn1); - applyAffinity(pIn1, u.ap.cAff, encoding); + applyAffinity(pIn1, cAff, encoding); pIn1++; } break; } /* Opcode: MakeRecord P1 P2 P3 P4 * +** Synopsis: r[P3]=mkrec(r[P1@P2]) ** ** Convert P2 registers beginning with P1 into the [record format] ** use as a data record in a database table or as a key @@ -68689,7 +68985,6 @@ case OP_Affinity: { ** If P4 is NULL then all index fields have the affinity NONE. */ case OP_MakeRecord: { -#if 0 /* local variables moved into u.aq */ u8 *zNewRecord; /* A buffer to hold the data for the new record */ Mem *pRec; /* The new record */ u64 nData; /* Number of bytes of data space */ @@ -68703,102 +68998,119 @@ case OP_MakeRecord: { int nField; /* Number of fields in the record */ char *zAffinity; /* The affinity string for the record */ int file_format; /* File format to use for encoding */ - int i; /* Space used in zNewRecord[] */ + int i; /* Space used in zNewRecord[] header */ + int j; /* Space used in zNewRecord[] content */ int len; /* Length of a field */ -#endif /* local variables moved into u.aq */ /* Assuming the record contains N fields, the record format looks ** like this: ** ** ------------------------------------------------------------------------ - ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 | + ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 | ** ------------------------------------------------------------------------ ** ** Data(0) is taken from register P1. Data(1) comes from register P1+1 ** and so froth. ** - ** Each type field is a varint representing the serial type of the + ** Each type field is a varint representing the serial type of the ** corresponding data element (see sqlite3VdbeSerialType()). The ** hdr-size field is also a varint which is the offset from the beginning ** of the record to data0. */ - u.aq.nData = 0; /* Number of bytes of data space */ - u.aq.nHdr = 0; /* Number of bytes of header space */ - u.aq.nZero = 0; /* Number of zero bytes at the end of the record */ - u.aq.nField = pOp->p1; - u.aq.zAffinity = pOp->p4.z; - assert( u.aq.nField>0 && pOp->p2>0 && pOp->p2+u.aq.nField<=(p->nMem-p->nCursor)+1 ); - u.aq.pData0 = &aMem[u.aq.nField]; - u.aq.nField = pOp->p2; - u.aq.pLast = &u.aq.pData0[u.aq.nField-1]; - u.aq.file_format = p->minWriteFileFormat; + nData = 0; /* Number of bytes of data space */ + nHdr = 0; /* Number of bytes of header space */ + nZero = 0; /* Number of zero bytes at the end of the record */ + nField = pOp->p1; + zAffinity = pOp->p4.z; + assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem-p->nCursor)+1 ); + pData0 = &aMem[nField]; + nField = pOp->p2; + pLast = &pData0[nField-1]; + file_format = p->minWriteFileFormat; /* Identify the output register */ assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); + /* Apply the requested affinity to all inputs + */ + assert( pData0<=pLast ); + if( zAffinity ){ + pRec = pData0; + do{ + applyAffinity(pRec, *(zAffinity++), encoding); + }while( (++pRec)<=pLast ); + } + /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ - for(u.aq.pRec=u.aq.pData0; u.aq.pRec<=u.aq.pLast; u.aq.pRec++){ - assert( memIsValid(u.aq.pRec) ); - if( u.aq.zAffinity ){ - applyAffinity(u.aq.pRec, u.aq.zAffinity[u.aq.pRec-u.aq.pData0], encoding); + pRec = pLast; + do{ + assert( memIsValid(pRec) ); + serial_type = sqlite3VdbeSerialType(pRec, file_format); + len = sqlite3VdbeSerialTypeLen(serial_type); + if( pRec->flags & MEM_Zero ){ + if( nData ){ + sqlite3VdbeMemExpandBlob(pRec); + }else{ + nZero += pRec->u.nZero; + len -= pRec->u.nZero; + } } - if( u.aq.pRec->flags&MEM_Zero && u.aq.pRec->n>0 ){ - sqlite3VdbeMemExpandBlob(u.aq.pRec); - } - u.aq.serial_type = sqlite3VdbeSerialType(u.aq.pRec, u.aq.file_format); - u.aq.len = sqlite3VdbeSerialTypeLen(u.aq.serial_type); - u.aq.nData += u.aq.len; - u.aq.nHdr += sqlite3VarintLen(u.aq.serial_type); - if( u.aq.pRec->flags & MEM_Zero ){ - /* Only pure zero-filled BLOBs can be input to this Opcode. - ** We do not allow blobs with a prefix and a zero-filled tail. */ - u.aq.nZero += u.aq.pRec->u.nZero; - }else if( u.aq.len ){ - u.aq.nZero = 0; - } - } + nData += len; + testcase( serial_type==127 ); + testcase( serial_type==128 ); + nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type); + }while( (--pRec)>=pData0 ); /* Add the initial header varint and total the size */ - u.aq.nHdr += u.aq.nVarint = sqlite3VarintLen(u.aq.nHdr); - if( u.aq.nVarintdb->aLimit[SQLITE_LIMIT_LENGTH] ){ + nByte = nHdr+nData; + if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } - /* Make sure the output register has a buffer large enough to store + /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ - if( sqlite3VdbeMemGrow(pOut, (int)u.aq.nByte, 0) ){ + if( sqlite3VdbeMemGrow(pOut, (int)nByte, 0) ){ goto no_mem; } - u.aq.zNewRecord = (u8 *)pOut->z; + zNewRecord = (u8 *)pOut->z; /* Write the record */ - u.aq.i = putVarint32(u.aq.zNewRecord, u.aq.nHdr); - for(u.aq.pRec=u.aq.pData0; u.aq.pRec<=u.aq.pLast; u.aq.pRec++){ - u.aq.serial_type = sqlite3VdbeSerialType(u.aq.pRec, u.aq.file_format); - u.aq.i += putVarint32(&u.aq.zNewRecord[u.aq.i], u.aq.serial_type); /* serial type */ - } - for(u.aq.pRec=u.aq.pData0; u.aq.pRec<=u.aq.pLast; u.aq.pRec++){ /* serial data */ - u.aq.i += sqlite3VdbeSerialPut(&u.aq.zNewRecord[u.aq.i], (int)(u.aq.nByte-u.aq.i), u.aq.pRec,u.aq.file_format); - } - assert( u.aq.i==u.aq.nByte ); + i = putVarint32(zNewRecord, nHdr); + j = nHdr; + assert( pData0<=pLast ); + pRec = pData0; + do{ + serial_type = sqlite3VdbeSerialType(pRec, file_format); + i += putVarint32(&zNewRecord[i], serial_type); /* serial type */ + j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */ + }while( (++pRec)<=pLast ); + assert( i==nHdr ); + assert( j==nByte ); assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); - pOut->n = (int)u.aq.nByte; + pOut->n = (int)nByte; pOut->flags = MEM_Blob | MEM_Dyn; pOut->xDel = 0; - if( u.aq.nZero ){ - pOut->u.nZero = u.aq.nZero; + if( nZero ){ + pOut->u.nZero = nZero; pOut->flags |= MEM_Zero; } pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */ @@ -68808,24 +69120,21 @@ case OP_MakeRecord: { } /* Opcode: Count P1 P2 * * * +** Synopsis: r[P2]=count() ** ** Store the number of entries (an integer value) in the table or index ** opened by cursor P1 in register P2 */ #ifndef SQLITE_OMIT_BTREECOUNT case OP_Count: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ar */ i64 nEntry; BtCursor *pCrsr; -#endif /* local variables moved into u.ar */ - u.ar.pCrsr = p->apCsr[pOp->p1]->pCursor; - if( ALWAYS(u.ar.pCrsr) ){ - rc = sqlite3BtreeCount(u.ar.pCrsr, &u.ar.nEntry); - }else{ - u.ar.nEntry = 0; - } - pOut->u.i = u.ar.nEntry; + pCrsr = p->apCsr[pOp->p1]->pCursor; + assert( pCrsr ); + nEntry = 0; /* Not needed. Only used to silence a warning. */ + rc = sqlite3BtreeCount(pCrsr, &nEntry); + pOut->u.i = nEntry; break; } #endif @@ -68837,7 +69146,6 @@ case OP_Count: { /* out2-prerelease */ ** existing savepoint, P1==1, or to rollback an existing savepoint P1==2. */ case OP_Savepoint: { -#if 0 /* local variables moved into u.as */ int p1; /* Value of P1 operand */ char *zName; /* Name of savepoint */ int nName; @@ -68846,30 +69154,29 @@ case OP_Savepoint: { Savepoint *pTmp; int iSavepoint; int ii; -#endif /* local variables moved into u.as */ - u.as.p1 = pOp->p1; - u.as.zName = pOp->p4.z; + p1 = pOp->p1; + zName = pOp->p4.z; - /* Assert that the u.as.p1 parameter is valid. Also that if there is no open - ** transaction, then there cannot be any savepoints. + /* Assert that the p1 parameter is valid. Also that if there is no open + ** transaction, then there cannot be any savepoints. */ assert( db->pSavepoint==0 || db->autoCommit==0 ); - assert( u.as.p1==SAVEPOINT_BEGIN||u.as.p1==SAVEPOINT_RELEASE||u.as.p1==SAVEPOINT_ROLLBACK ); + assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK ); assert( db->pSavepoint || db->isTransactionSavepoint==0 ); assert( checkSavepointCount(db) ); assert( p->bIsReader ); - if( u.as.p1==SAVEPOINT_BEGIN ){ + if( p1==SAVEPOINT_BEGIN ){ if( db->nVdbeWrite>0 ){ - /* A new savepoint cannot be created if there are active write + /* A new savepoint cannot be created if there are active write ** statements (i.e. open read/write incremental blob handles). */ sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - " "SQL statements in progress"); rc = SQLITE_BUSY; }else{ - u.as.nName = sqlite3Strlen30(u.as.zName); + nName = sqlite3Strlen30(zName); #ifndef SQLITE_OMIT_VIRTUALTABLE /* This call is Ok even if this savepoint is actually a transaction @@ -68883,11 +69190,11 @@ case OP_Savepoint: { #endif /* Create a new savepoint structure. */ - u.as.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.as.nName+1); - if( u.as.pNew ){ - u.as.pNew->zName = (char *)&u.as.pNew[1]; - memcpy(u.as.pNew->zName, u.as.zName, u.as.nName+1); - + pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1); + if( pNew ){ + pNew->zName = (char *)&pNew[1]; + memcpy(pNew->zName, zName, nName+1); + /* If there is no open transaction, then mark this as a special ** "transaction savepoint". */ if( db->autoCommit ){ @@ -68896,45 +69203,45 @@ case OP_Savepoint: { }else{ db->nSavepoint++; } - + /* Link the new savepoint into the database handle's list. */ - u.as.pNew->pNext = db->pSavepoint; - db->pSavepoint = u.as.pNew; - u.as.pNew->nDeferredCons = db->nDeferredCons; - u.as.pNew->nDeferredImmCons = db->nDeferredImmCons; + pNew->pNext = db->pSavepoint; + db->pSavepoint = pNew; + pNew->nDeferredCons = db->nDeferredCons; + pNew->nDeferredImmCons = db->nDeferredImmCons; } } }else{ - u.as.iSavepoint = 0; + iSavepoint = 0; /* Find the named savepoint. If there is no such savepoint, then an ** an error is returned to the user. */ for( - u.as.pSavepoint = db->pSavepoint; - u.as.pSavepoint && sqlite3StrICmp(u.as.pSavepoint->zName, u.as.zName); - u.as.pSavepoint = u.as.pSavepoint->pNext + pSavepoint = db->pSavepoint; + pSavepoint && sqlite3StrICmp(pSavepoint->zName, zName); + pSavepoint = pSavepoint->pNext ){ - u.as.iSavepoint++; + iSavepoint++; } - if( !u.as.pSavepoint ){ - sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.as.zName); + if( !pSavepoint ){ + sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName); rc = SQLITE_ERROR; - }else if( db->nVdbeWrite>0 && u.as.p1==SAVEPOINT_RELEASE ){ - /* It is not possible to release (commit) a savepoint if there are + }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){ + /* It is not possible to release (commit) a savepoint if there are ** active write statements. */ - sqlite3SetString(&p->zErrMsg, db, + sqlite3SetString(&p->zErrMsg, db, "cannot release savepoint - SQL statements in progress" ); rc = SQLITE_BUSY; }else{ /* Determine whether or not this is a transaction savepoint. If so, - ** and this is a RELEASE command, then the current transaction - ** is committed. + ** and this is a RELEASE command, then the current transaction + ** is committed. */ - int isTransaction = u.as.pSavepoint->pNext==0 && db->isTransactionSavepoint; - if( isTransaction && u.as.p1==SAVEPOINT_RELEASE ){ + int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; + if( isTransaction && p1==SAVEPOINT_RELEASE ){ if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; } @@ -68948,52 +69255,52 @@ case OP_Savepoint: { db->isTransactionSavepoint = 0; rc = p->rc; }else{ - u.as.iSavepoint = db->nSavepoint - u.as.iSavepoint - 1; - if( u.as.p1==SAVEPOINT_ROLLBACK ){ - for(u.as.ii=0; u.as.iinDb; u.as.ii++){ - sqlite3BtreeTripAllCursors(db->aDb[u.as.ii].pBt, SQLITE_ABORT); + iSavepoint = db->nSavepoint - iSavepoint - 1; + if( p1==SAVEPOINT_ROLLBACK ){ + for(ii=0; iinDb; ii++){ + sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT); } } - for(u.as.ii=0; u.as.iinDb; u.as.ii++){ - rc = sqlite3BtreeSavepoint(db->aDb[u.as.ii].pBt, u.as.p1, u.as.iSavepoint); + for(ii=0; iinDb; ii++){ + rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } - if( u.as.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ + if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetAllSchemasOfConnection(db); db->flags = (db->flags | SQLITE_InternChanges); } } - - /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all + + /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ - while( db->pSavepoint!=u.as.pSavepoint ){ - u.as.pTmp = db->pSavepoint; - db->pSavepoint = u.as.pTmp->pNext; - sqlite3DbFree(db, u.as.pTmp); + while( db->pSavepoint!=pSavepoint ){ + pTmp = db->pSavepoint; + db->pSavepoint = pTmp->pNext; + sqlite3DbFree(db, pTmp); db->nSavepoint--; } - /* If it is a RELEASE, then destroy the savepoint being operated on - ** too. If it is a ROLLBACK TO, then set the number of deferred + /* If it is a RELEASE, then destroy the savepoint being operated on + ** too. If it is a ROLLBACK TO, then set the number of deferred ** constraint violations present in the database to the value stored ** when the savepoint was created. */ - if( u.as.p1==SAVEPOINT_RELEASE ){ - assert( u.as.pSavepoint==db->pSavepoint ); - db->pSavepoint = u.as.pSavepoint->pNext; - sqlite3DbFree(db, u.as.pSavepoint); + if( p1==SAVEPOINT_RELEASE ){ + assert( pSavepoint==db->pSavepoint ); + db->pSavepoint = pSavepoint->pNext; + sqlite3DbFree(db, pSavepoint); if( !isTransaction ){ db->nSavepoint--; } }else{ - db->nDeferredCons = u.as.pSavepoint->nDeferredCons; - db->nDeferredImmCons = u.as.pSavepoint->nDeferredImmCons; + db->nDeferredCons = pSavepoint->nDeferredCons; + db->nDeferredImmCons = pSavepoint->nDeferredImmCons; } if( !isTransaction ){ - rc = sqlite3VtabSavepoint(db, u.as.p1, u.as.iSavepoint); + rc = sqlite3VtabSavepoint(db, p1, iSavepoint); if( rc!=SQLITE_OK ) goto abort_due_to_error; } } @@ -69012,50 +69319,48 @@ case OP_Savepoint: { ** This instruction causes the VM to halt. */ case OP_AutoCommit: { -#if 0 /* local variables moved into u.at */ int desiredAutoCommit; int iRollback; int turnOnAC; -#endif /* local variables moved into u.at */ - u.at.desiredAutoCommit = pOp->p1; - u.at.iRollback = pOp->p2; - u.at.turnOnAC = u.at.desiredAutoCommit && !db->autoCommit; - assert( u.at.desiredAutoCommit==1 || u.at.desiredAutoCommit==0 ); - assert( u.at.desiredAutoCommit==1 || u.at.iRollback==0 ); + desiredAutoCommit = pOp->p1; + iRollback = pOp->p2; + turnOnAC = desiredAutoCommit && !db->autoCommit; + assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); + assert( desiredAutoCommit==1 || iRollback==0 ); assert( db->nVdbeActive>0 ); /* At least this one VM is active */ assert( p->bIsReader ); #if 0 - if( u.at.turnOnAC && u.at.iRollback && db->nVdbeActive>1 ){ + if( turnOnAC && iRollback && db->nVdbeActive>1 ){ /* If this instruction implements a ROLLBACK and other VMs are ** still running, and a transaction is active, return an error indicating - ** that the other VMs must complete first. + ** that the other VMs must complete first. */ sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; }else #endif - if( u.at.turnOnAC && !u.at.iRollback && db->nVdbeWrite>0 ){ + if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){ /* If this instruction implements a COMMIT and other VMs are writing - ** return an error indicating that the other VMs must complete first. + ** return an error indicating that the other VMs must complete first. */ sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; - }else if( u.at.desiredAutoCommit!=db->autoCommit ){ - if( u.at.iRollback ){ - assert( u.at.desiredAutoCommit==1 ); + }else if( desiredAutoCommit!=db->autoCommit ){ + if( iRollback ){ + assert( desiredAutoCommit==1 ); sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); db->autoCommit = 1; }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; }else{ - db->autoCommit = (u8)u.at.desiredAutoCommit; + db->autoCommit = (u8)desiredAutoCommit; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = pc; - db->autoCommit = (u8)(1-u.at.desiredAutoCommit); + db->autoCommit = (u8)(1-desiredAutoCommit); p->rc = rc = SQLITE_BUSY; goto vdbe_return; } @@ -69070,10 +69375,10 @@ case OP_AutoCommit: { goto vdbe_return; }else{ sqlite3SetString(&p->zErrMsg, db, - (!u.at.desiredAutoCommit)?"cannot start a transaction within a transaction":( - (u.at.iRollback)?"cannot rollback - no transaction is active": + (!desiredAutoCommit)?"cannot start a transaction within a transaction":( + (iRollback)?"cannot rollback - no transaction is active": "cannot commit - no transaction is active")); - + rc = SQLITE_ERROR; } break; @@ -69111,9 +69416,7 @@ case OP_AutoCommit: { ** If P2 is zero, then a read-lock is obtained on the database file. */ case OP_Transaction: { -#if 0 /* local variables moved into u.au */ Btree *pBt; -#endif /* local variables moved into u.au */ assert( p->bIsReader ); assert( p->readOnly==0 || pOp->p2==0 ); @@ -69123,10 +69426,10 @@ case OP_Transaction: { rc = SQLITE_READONLY; goto abort_due_to_error; } - u.au.pBt = db->aDb[pOp->p1].pBt; + pBt = db->aDb[pOp->p1].pBt; - if( u.au.pBt ){ - rc = sqlite3BtreeBeginTrans(u.au.pBt, pOp->p2); + if( pBt ){ + rc = sqlite3BtreeBeginTrans(pBt, pOp->p2); if( rc==SQLITE_BUSY ){ p->pc = pc; p->rc = rc = SQLITE_BUSY; @@ -69136,19 +69439,19 @@ case OP_Transaction: { goto abort_due_to_error; } - if( pOp->p2 && p->usesStmtJournal - && (db->autoCommit==0 || db->nVdbeRead>1) + if( pOp->p2 && p->usesStmtJournal + && (db->autoCommit==0 || db->nVdbeRead>1) ){ - assert( sqlite3BtreeIsInTrans(u.au.pBt) ); + assert( sqlite3BtreeIsInTrans(pBt) ); if( p->iStatement==0 ){ assert( db->nStatement>=0 && db->nSavepoint>=0 ); - db->nStatement++; + db->nStatement++; p->iStatement = db->nSavepoint + db->nStatement; } rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginStmt(u.au.pBt, p->iStatement); + rc = sqlite3BtreeBeginStmt(pBt, p->iStatement); } /* Store the current value of the database handles deferred constraint @@ -69174,22 +69477,20 @@ case OP_Transaction: { ** executing this instruction. */ case OP_ReadCookie: { /* out2-prerelease */ -#if 0 /* local variables moved into u.av */ int iMeta; int iDb; int iCookie; -#endif /* local variables moved into u.av */ assert( p->bIsReader ); - u.av.iDb = pOp->p1; - u.av.iCookie = pOp->p3; + iDb = pOp->p1; + iCookie = pOp->p3; assert( pOp->p3=0 && u.av.iDbnDb ); - assert( db->aDb[u.av.iDb].pBt!=0 ); - assert( (p->btreeMask & (((yDbMask)1)<=0 && iDbnDb ); + assert( db->aDb[iDb].pBt!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<aDb[u.av.iDb].pBt, u.av.iCookie, (u32 *)&u.av.iMeta); - pOut->u.i = u.av.iMeta; + sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); + pOut->u.i = iMeta; break; } @@ -69204,27 +69505,25 @@ case OP_ReadCookie: { /* out2-prerelease */ ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { /* in3 */ -#if 0 /* local variables moved into u.aw */ Db *pDb; -#endif /* local variables moved into u.aw */ assert( pOp->p2p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); assert( p->readOnly==0 ); - u.aw.pDb = &db->aDb[pOp->p1]; - assert( u.aw.pDb->pBt!=0 ); + pDb = &db->aDb[pOp->p1]; + assert( pDb->pBt!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); /* See note about index shifting on OP_ReadCookie */ - rc = sqlite3BtreeUpdateMeta(u.aw.pDb->pBt, pOp->p2, (int)pIn3->u.i); + rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ - u.aw.pDb->pSchema->schema_cookie = (int)pIn3->u.i; + pDb->pSchema->schema_cookie = (int)pIn3->u.i; db->flags |= SQLITE_InternChanges; }else if( pOp->p2==BTREE_FILE_FORMAT ){ /* Record changes in the file format */ - u.aw.pDb->pSchema->file_format = (u8)pIn3->u.i; + pDb->pSchema->file_format = (u8)pIn3->u.i; } if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database @@ -69254,27 +69553,25 @@ case OP_SetCookie: { /* in3 */ ** invoked. */ case OP_VerifyCookie: { -#if 0 /* local variables moved into u.ax */ int iMeta; int iGen; Btree *pBt; -#endif /* local variables moved into u.ax */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); assert( p->bIsReader ); - u.ax.pBt = db->aDb[pOp->p1].pBt; - if( u.ax.pBt ){ - sqlite3BtreeGetMeta(u.ax.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.ax.iMeta); - u.ax.iGen = db->aDb[pOp->p1].pSchema->iGeneration; + pBt = db->aDb[pOp->p1].pBt; + if( pBt ){ + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); + iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ - u.ax.iGen = u.ax.iMeta = 0; + iGen = iMeta = 0; } - if( u.ax.iMeta!=pOp->p2 || u.ax.iGen!=pOp->p3 ){ + if( iMeta!=pOp->p2 || iGen!=pOp->p3 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); - /* If the schema-cookie from the database file matches the cookie + /* If the schema-cookie from the database file matches the cookie ** stored with the in-memory representation of the schema, do ** not reload the schema from the database file. ** @@ -69284,10 +69581,10 @@ case OP_VerifyCookie: { ** prepared queries. If such a query is out-of-date, we do not want to ** discard the database schema, as the user code implementing the ** v-table would have to be ready for the sqlite3_vtab structure itself - ** to be invalidated whenever sqlite3_step() is called from within + ** to be invalidated whenever sqlite3_step() is called from within ** a v-table method. */ - if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.ax.iMeta ){ + if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ sqlite3ResetOneSchema(db, pOp->p1); } @@ -69298,6 +69595,7 @@ case OP_VerifyCookie: { } /* Opcode: OpenRead P1 P2 P3 P4 P5 +** Synopsis: root=P2 iDb=P3 ** ** Open a read-only cursor for the database table whose root page is ** P2 in a database file. The database file is determined by P3. @@ -69328,6 +69626,7 @@ case OP_VerifyCookie: { ** See also OpenWrite. */ /* Opcode: OpenWrite P1 P2 P3 P4 P5 +** Synopsis: root=P2 iDb=P3 ** ** Open a read/write cursor named P1 on the table or index whose root ** page is P2. Or if P5!=0 use the content of register P2 to find the @@ -69348,7 +69647,6 @@ case OP_VerifyCookie: { */ case OP_OpenRead: case OP_OpenWrite: { -#if 0 /* local variables moved into u.ay */ int nField; KeyInfo *pKeyInfo; int p2; @@ -69357,7 +69655,6 @@ case OP_OpenWrite: { Btree *pX; VdbeCursor *pCur; Db *pDb; -#endif /* local variables moved into u.ay */ assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 ); assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 ); @@ -69369,72 +69666,75 @@ case OP_OpenWrite: { break; } - u.ay.nField = 0; - u.ay.pKeyInfo = 0; - u.ay.p2 = pOp->p2; - u.ay.iDb = pOp->p3; - assert( u.ay.iDb>=0 && u.ay.iDbnDb ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[u.ay.iDb]; - u.ay.pX = u.ay.pDb->pBt; - assert( u.ay.pX!=0 ); + nField = 0; + pKeyInfo = 0; + p2 = pOp->p2; + iDb = pOp->p3; + assert( iDb>=0 && iDbnDb ); + assert( (p->btreeMask & (((yDbMask)1)<aDb[iDb]; + pX = pDb->pBt; + assert( pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ - u.ay.wrFlag = 1; - assert( sqlite3SchemaMutexHeld(db, u.ay.iDb, 0) ); - if( u.ay.pDb->pSchema->file_format < p->minWriteFileFormat ){ - p->minWriteFileFormat = u.ay.pDb->pSchema->file_format; + wrFlag = 1; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + if( pDb->pSchema->file_format < p->minWriteFileFormat ){ + p->minWriteFileFormat = pDb->pSchema->file_format; } }else{ - u.ay.wrFlag = 0; + wrFlag = 0; } if( pOp->p5 & OPFLAG_P2ISREG ){ - assert( u.ay.p2>0 ); - assert( u.ay.p2<=(p->nMem-p->nCursor) ); - pIn2 = &aMem[u.ay.p2]; + assert( p2>0 ); + assert( p2<=(p->nMem-p->nCursor) ); + pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); - u.ay.p2 = (int)pIn2->u.i; - /* The u.ay.p2 value always comes from a prior OP_CreateTable opcode and - ** that opcode will always set the u.ay.p2 value to 2 or more or else fail. + p2 = (int)pIn2->u.i; + /* The p2 value always comes from a prior OP_CreateTable opcode and + ** that opcode will always set the p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ - if( NEVER(u.ay.p2<2) ) { + if( NEVER(p2<2) ) { rc = SQLITE_CORRUPT_BKPT; goto abort_due_to_error; } } if( pOp->p4type==P4_KEYINFO ){ - u.ay.pKeyInfo = pOp->p4.pKeyInfo; - u.ay.pKeyInfo->enc = ENC(p->db); - u.ay.nField = u.ay.pKeyInfo->nField+1; + pKeyInfo = pOp->p4.pKeyInfo; + assert( pKeyInfo->enc==ENC(db) ); + assert( pKeyInfo->db==db ); + nField = pKeyInfo->nField+pKeyInfo->nXField; }else if( pOp->p4type==P4_INT32 ){ - u.ay.nField = pOp->p4.i; + nField = pOp->p4.i; } assert( pOp->p1>=0 ); - u.ay.pCur = allocateCursor(p, pOp->p1, u.ay.nField, u.ay.iDb, 1); - if( u.ay.pCur==0 ) goto no_mem; - u.ay.pCur->nullRow = 1; - u.ay.pCur->isOrdered = 1; - rc = sqlite3BtreeCursor(u.ay.pX, u.ay.p2, u.ay.wrFlag, u.ay.pKeyInfo, u.ay.pCur->pCursor); - u.ay.pCur->pKeyInfo = u.ay.pKeyInfo; + assert( nField>=0 ); + testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ + pCur = allocateCursor(p, pOp->p1, nField, iDb, 1); + if( pCur==0 ) goto no_mem; + pCur->nullRow = 1; + pCur->isOrdered = 1; + rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); + pCur->pKeyInfo = pKeyInfo; assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); - sqlite3BtreeCursorHints(u.ay.pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR)); + sqlite3BtreeCursorHints(pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR)); /* Since it performs no memory allocation or IO, the only value that ** sqlite3BtreeCursor() may return is SQLITE_OK. */ assert( rc==SQLITE_OK ); - /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of + /* Set the VdbeCursor.isTable variable. Previous versions of ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has - ** since moved into the btree layer. */ - u.ay.pCur->isTable = pOp->p4type!=P4_KEYINFO; - u.ay.pCur->isIndex = !u.ay.pCur->isTable; + ** since moved into the btree layer. */ + pCur->isTable = pOp->p4type!=P4_KEYINFO; break; } /* Opcode: OpenEphemeral P1 P2 * P4 P5 +** Synopsis: nColumn=P2 ** ** Open a new cursor P1 to a transient table. ** The cursor is always opened read/write even if @@ -69446,18 +69746,13 @@ case OP_OpenWrite: { ** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure ** that defines the format of keys in the index. ** -** This opcode was once called OpenTemp. But that created -** confusion because the term "temp table", might refer either -** to a TEMP table at the SQL level, or to a table opened by -** this opcode. Then this opcode was call OpenVirtual. But -** that created confusion with the whole virtual-table idea. -** ** The P5 parameter can be a mask of the BTREE_* flags defined ** in btree.h. These flags control aspects of the operation of ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are ** added automatically. */ /* Opcode: OpenAutoindex P1 P2 * P4 * +** Synopsis: nColumn=P2 ** ** This opcode works the same as OP_OpenEphemeral. It has a ** different name to distinguish its use. Tables created using @@ -69466,24 +69761,24 @@ case OP_OpenWrite: { */ case OP_OpenAutoindex: case OP_OpenEphemeral: { -#if 0 /* local variables moved into u.az */ VdbeCursor *pCx; -#endif /* local variables moved into u.az */ - static const int vfsFlags = + KeyInfo *pKeyInfo; + + static const int vfsFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TRANSIENT_DB; - assert( pOp->p1>=0 ); - u.az.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); - if( u.az.pCx==0 ) goto no_mem; - u.az.pCx->nullRow = 1; - rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.az.pCx->pBt, + assert( pOp->p2>=0 ); + pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(u.az.pCx->pBt, 1); + rc = sqlite3BtreeBeginTrans(pCx->pBt, 1); } if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling @@ -69491,49 +69786,49 @@ case OP_OpenEphemeral: { ** opening it. If a transient table is required, just use the ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ - if( pOp->p4.pKeyInfo ){ + if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ int pgno; assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(u.az.pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5); + rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); - rc = sqlite3BtreeCursor(u.az.pCx->pBt, pgno, 1, - (KeyInfo*)pOp->p4.z, u.az.pCx->pCursor); - u.az.pCx->pKeyInfo = pOp->p4.pKeyInfo; - u.az.pCx->pKeyInfo->enc = ENC(p->db); + assert( pKeyInfo->db==db ); + assert( pKeyInfo->enc==ENC(db) ); + pCx->pKeyInfo = pKeyInfo; + rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, pKeyInfo, pCx->pCursor); } - u.az.pCx->isTable = 0; + pCx->isTable = 0; }else{ - rc = sqlite3BtreeCursor(u.az.pCx->pBt, MASTER_ROOT, 1, 0, u.az.pCx->pCursor); - u.az.pCx->isTable = 1; + rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor); + pCx->isTable = 1; } } - u.az.pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); - u.az.pCx->isIndex = !u.az.pCx->isTable; + pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); break; } -/* Opcode: SorterOpen P1 P2 * P4 * +/* Opcode: SorterOpen P1 * * P4 * ** ** This opcode works like OP_OpenEphemeral except that it opens ** a transient index that is specifically designed to sort large ** tables using an external merge-sort algorithm. */ case OP_SorterOpen: { -#if 0 /* local variables moved into u.ba */ VdbeCursor *pCx; -#endif /* local variables moved into u.ba */ - u.ba.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); - if( u.ba.pCx==0 ) goto no_mem; - u.ba.pCx->pKeyInfo = pOp->p4.pKeyInfo; - u.ba.pCx->pKeyInfo->enc = ENC(p->db); - u.ba.pCx->isSorter = 1; - rc = sqlite3VdbeSorterInit(db, u.ba.pCx); + assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); + pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); + if( pCx==0 ) goto no_mem; + pCx->pKeyInfo = pOp->p4.pKeyInfo; + assert( pCx->pKeyInfo->db==db ); + assert( pCx->pKeyInfo->enc==ENC(db) ); + rc = sqlite3VdbeSorterInit(db, pCx); break; } /* Opcode: OpenPseudo P1 P2 P3 * P5 +** Synopsis: content in r[P2@P3] ** ** Open a new cursor that points to a fake table that contains a single ** row of data. The content of that one row in the content of memory @@ -69550,18 +69845,16 @@ case OP_SorterOpen: { ** the pseudo-table. */ case OP_OpenPseudo: { -#if 0 /* local variables moved into u.bb */ VdbeCursor *pCx; -#endif /* local variables moved into u.bb */ assert( pOp->p1>=0 ); - u.bb.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); - if( u.bb.pCx==0 ) goto no_mem; - u.bb.pCx->nullRow = 1; - u.bb.pCx->pseudoTableReg = pOp->p2; - u.bb.pCx->isTable = 1; - u.bb.pCx->isIndex = 0; - u.bb.pCx->multiPseudo = pOp->p5; + assert( pOp->p3>=0 ); + pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->pseudoTableReg = pOp->p2; + pCx->isTable = 1; + pCx->multiPseudo = pOp->p5; break; } @@ -69578,6 +69871,7 @@ case OP_Close: { } /* Opcode: SeekGe P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** ** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), ** use the value in register P3 as the key. If cursor P1 refers @@ -69591,6 +69885,7 @@ case OP_Close: { ** See also: Found, NotFound, Distinct, SeekLt, SeekGt, SeekLe */ /* Opcode: SeekGt P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** ** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), ** use the value in register P3 as a key. If cursor P1 refers @@ -69604,6 +69899,7 @@ case OP_Close: { ** See also: Found, NotFound, Distinct, SeekLt, SeekGe, SeekLe */ /* Opcode: SeekLt P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** ** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), ** use the value in register P3 as a key. If cursor P1 refers @@ -69617,6 +69913,7 @@ case OP_Close: { ** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLe */ /* Opcode: SeekLe P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** ** If cursor P1 refers to an SQL table (B-Tree that uses integer keys), ** use the value in register P3 as a key. If cursor P1 refers @@ -69633,157 +69930,141 @@ case OP_SeekLt: /* jump, in3 */ case OP_SeekLe: /* jump, in3 */ case OP_SeekGe: /* jump, in3 */ case OP_SeekGt: { /* jump, in3 */ -#if 0 /* local variables moved into u.bc */ int res; int oc; VdbeCursor *pC; UnpackedRecord r; int nField; i64 iKey; /* The rowid we are to seek to */ -#endif /* local variables moved into u.bc */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p2!=0 ); - u.bc.pC = p->apCsr[pOp->p1]; - assert( u.bc.pC!=0 ); - assert( u.bc.pC->pseudoTableReg==0 ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); - assert( u.bc.pC->isOrdered ); - if( ALWAYS(u.bc.pC->pCursor!=0) ){ - u.bc.oc = pOp->opcode; - u.bc.pC->nullRow = 0; - if( u.bc.pC->isTable ){ - /* The input value in P3 might be of any type: integer, real, string, - ** blob, or NULL. But it needs to be an integer before we can do - ** the seek, so covert it. */ - pIn3 = &aMem[pOp->p3]; - applyNumericAffinity(pIn3); - u.bc.iKey = sqlite3VdbeIntValue(pIn3); - u.bc.pC->rowidIsValid = 0; + assert( pC->isOrdered ); + assert( pC->pCursor!=0 ); + oc = pOp->opcode; + pC->nullRow = 0; + if( pC->isTable ){ + /* The input value in P3 might be of any type: integer, real, string, + ** blob, or NULL. But it needs to be an integer before we can do + ** the seek, so covert it. */ + pIn3 = &aMem[pOp->p3]; + applyNumericAffinity(pIn3); + iKey = sqlite3VdbeIntValue(pIn3); + pC->rowidIsValid = 0; - /* If the P3 value could not be converted into an integer without - ** loss of information, then special processing is required... */ - if( (pIn3->flags & MEM_Int)==0 ){ - if( (pIn3->flags & MEM_Real)==0 ){ - /* If the P3 value cannot be converted into any kind of a number, - ** then the seek is not possible, so jump to P2 */ - pc = pOp->p2 - 1; - break; - } - /* If we reach this point, then the P3 value must be a floating - ** point number. */ - assert( (pIn3->flags & MEM_Real)!=0 ); + /* If the P3 value could not be converted into an integer without + ** loss of information, then special processing is required... */ + if( (pIn3->flags & MEM_Int)==0 ){ + if( (pIn3->flags & MEM_Real)==0 ){ + /* If the P3 value cannot be converted into any kind of a number, + ** then the seek is not possible, so jump to P2 */ + pc = pOp->p2 - 1; + break; + } - if( u.bc.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.bc.iKey || pIn3->r>0) ){ - /* The P3 value is too large in magnitude to be expressed as an - ** integer. */ - u.bc.res = 1; - if( pIn3->r<0 ){ - if( u.bc.oc>=OP_SeekGe ){ assert( u.bc.oc==OP_SeekGe || u.bc.oc==OP_SeekGt ); - rc = sqlite3BtreeFirst(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - }else{ - if( u.bc.oc<=OP_SeekLe ){ assert( u.bc.oc==OP_SeekLt || u.bc.oc==OP_SeekLe ); - rc = sqlite3BtreeLast(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - } - } - if( u.bc.res ){ - pc = pOp->p2 - 1; - } - break; - }else if( u.bc.oc==OP_SeekLt || u.bc.oc==OP_SeekGe ){ - /* Use the ceiling() function to convert real->int */ - if( pIn3->r > (double)u.bc.iKey ) u.bc.iKey++; - }else{ - /* Use the floor() function to convert real->int */ - assert( u.bc.oc==OP_SeekLe || u.bc.oc==OP_SeekGt ); - if( pIn3->r < (double)u.bc.iKey ) u.bc.iKey--; - } - } - rc = sqlite3BtreeMovetoUnpacked(u.bc.pC->pCursor, 0, (u64)u.bc.iKey, 0, &u.bc.res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( u.bc.res==0 ){ - u.bc.pC->rowidIsValid = 1; - u.bc.pC->lastRowid = u.bc.iKey; - } - }else{ - u.bc.nField = pOp->p4.i; - assert( pOp->p4type==P4_INT32 ); - assert( u.bc.nField>0 ); - u.bc.r.pKeyInfo = u.bc.pC->pKeyInfo; - u.bc.r.nField = (u16)u.bc.nField; - - /* The next line of code computes as follows, only faster: - ** if( u.bc.oc==OP_SeekGt || u.bc.oc==OP_SeekLe ){ - ** u.bc.r.flags = UNPACKED_INCRKEY; - ** }else{ - ** u.bc.r.flags = 0; - ** } + /* If the approximation iKey is larger than the actual real search + ** term, substitute >= for > and < for <=. e.g. if the search term + ** is 4.9 and the integer approximation 5: + ** + ** (x > 4.9) -> (x >= 5) + ** (x <= 4.9) -> (x < 5) */ - u.bc.r.flags = (u8)(UNPACKED_INCRKEY * (1 & (u.bc.oc - OP_SeekLt))); - assert( u.bc.oc!=OP_SeekGt || u.bc.r.flags==UNPACKED_INCRKEY ); - assert( u.bc.oc!=OP_SeekLe || u.bc.r.flags==UNPACKED_INCRKEY ); - assert( u.bc.oc!=OP_SeekGe || u.bc.r.flags==0 ); - assert( u.bc.oc!=OP_SeekLt || u.bc.r.flags==0 ); + if( pIn3->r<(double)iKey ){ + assert( OP_SeekGe==(OP_SeekGt-1) ); + assert( OP_SeekLt==(OP_SeekLe-1) ); + assert( (OP_SeekLe & 0x0001)==(OP_SeekGt & 0x0001) ); + if( (oc & 0x0001)==(OP_SeekGt & 0x0001) ) oc--; + } - u.bc.r.aMem = &aMem[pOp->p3]; -#ifdef SQLITE_DEBUG - { int i; for(i=0; ipCursor, &u.bc.r, 0, 0, &u.bc.res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; + /* If the approximation iKey is smaller than the actual real search + ** term, substitute <= for < and > for >=. */ + else if( pIn3->r>(double)iKey ){ + assert( OP_SeekLe==(OP_SeekLt+1) ); + assert( OP_SeekGt==(OP_SeekGe+1) ); + assert( (OP_SeekLt & 0x0001)==(OP_SeekGe & 0x0001) ); + if( (oc & 0x0001)==(OP_SeekLt & 0x0001) ) oc++; } - u.bc.pC->rowidIsValid = 0; + } + rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; } - u.bc.pC->deferredMoveto = 0; - u.bc.pC->cacheStatus = CACHE_STALE; -#ifdef SQLITE_TEST - sqlite3_search_count++; -#endif - if( u.bc.oc>=OP_SeekGe ){ assert( u.bc.oc==OP_SeekGe || u.bc.oc==OP_SeekGt ); - if( u.bc.res<0 || (u.bc.res==0 && u.bc.oc==OP_SeekGt) ){ - rc = sqlite3BtreeNext(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.bc.pC->rowidIsValid = 0; - }else{ - u.bc.res = 0; - } - }else{ - assert( u.bc.oc==OP_SeekLt || u.bc.oc==OP_SeekLe ); - if( u.bc.res>0 || (u.bc.res==0 && u.bc.oc==OP_SeekLt) ){ - rc = sqlite3BtreePrevious(u.bc.pC->pCursor, &u.bc.res); - if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.bc.pC->rowidIsValid = 0; - }else{ - /* u.bc.res might be negative because the table is empty. Check to - ** see if this is the case. - */ - u.bc.res = sqlite3BtreeEof(u.bc.pC->pCursor); - } - } - assert( pOp->p2>0 ); - if( u.bc.res ){ - pc = pOp->p2 - 1; + if( res==0 ){ + pC->rowidIsValid = 1; + pC->lastRowid = iKey; } }else{ - /* This happens when attempting to open the sqlite3_master table - ** for read access returns SQLITE_EMPTY. In this case always - ** take the jump (since there are no records in the table). + nField = pOp->p4.i; + assert( pOp->p4type==P4_INT32 ); + assert( nField>0 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)nField; + + /* The next line of code computes as follows, only faster: + ** if( oc==OP_SeekGt || oc==OP_SeekLe ){ + ** r.flags = UNPACKED_INCRKEY; + ** }else{ + ** r.flags = 0; + ** } */ + r.flags = (u8)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt))); + assert( oc!=OP_SeekGt || r.flags==UNPACKED_INCRKEY ); + assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY ); + assert( oc!=OP_SeekGe || r.flags==0 ); + assert( oc!=OP_SeekLt || r.flags==0 ); + + r.aMem = &aMem[pOp->p3]; +#ifdef SQLITE_DEBUG + { int i; for(i=0; ipCursor, &r, 0, 0, &res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + pC->rowidIsValid = 0; + } + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; +#ifdef SQLITE_TEST + sqlite3_search_count++; +#endif + if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt ); + if( res<0 || (res==0 && oc==OP_SeekGt) ){ + rc = sqlite3BtreeNext(pC->pCursor, &res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + pC->rowidIsValid = 0; + }else{ + res = 0; + } + }else{ + assert( oc==OP_SeekLt || oc==OP_SeekLe ); + if( res>0 || (res==0 && oc==OP_SeekLt) ){ + rc = sqlite3BtreePrevious(pC->pCursor, &res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + pC->rowidIsValid = 0; + }else{ + /* res might be negative because the table is empty. Check to + ** see if this is the case. + */ + res = sqlite3BtreeEof(pC->pCursor); + } + } + assert( pOp->p2>0 ); + if( res ){ pc = pOp->p2 - 1; } break; } /* Opcode: Seek P1 P2 * * * +** Synopsis: intkey=r[P2] ** ** P1 is an open table cursor and P2 is a rowid integer. Arrange ** for P1 to move so that it points to the rowid given by P2. @@ -69793,26 +70074,24 @@ case OP_SeekGt: { /* jump, in3 */ ** occur, no unnecessary I/O happens. */ case OP_Seek: { /* in2 */ -#if 0 /* local variables moved into u.bd */ VdbeCursor *pC; -#endif /* local variables moved into u.bd */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bd.pC = p->apCsr[pOp->p1]; - assert( u.bd.pC!=0 ); - if( ALWAYS(u.bd.pC->pCursor!=0) ){ - assert( u.bd.pC->isTable ); - u.bd.pC->nullRow = 0; - pIn2 = &aMem[pOp->p2]; - u.bd.pC->movetoTarget = sqlite3VdbeIntValue(pIn2); - u.bd.pC->rowidIsValid = 0; - u.bd.pC->deferredMoveto = 1; - } + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->pCursor!=0 ); + assert( pC->isTable ); + pC->nullRow = 0; + pIn2 = &aMem[pOp->p2]; + pC->movetoTarget = sqlite3VdbeIntValue(pIn2); + pC->rowidIsValid = 0; + pC->deferredMoveto = 1; break; } /* Opcode: Found P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** ** If P4==0 then register P3 holds a blob constructed by MakeRecord. If ** P4>0 then register P3 is the first of P4 registers that form an unpacked @@ -69821,8 +70100,11 @@ case OP_Seek: { /* in2 */ ** Cursor P1 is on an index btree. If the record identified by P3 and P4 ** is a prefix of any entry in P1 then a jump is made to P2 and ** P1 is left pointing at the matching entry. +** +** See also: NotFound, NoConflict, NotExists. SeekGe */ /* Opcode: NotFound P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] ** ** If P4==0 then register P3 holds a blob constructed by MakeRecord. If ** P4>0 then register P3 is the first of P4 registers that form an unpacked @@ -69834,216 +70116,154 @@ case OP_Seek: { /* in2 */ ** falls through to the next instruction and P1 is left pointing at the ** matching entry. ** -** See also: Found, NotExists, IsUnique +** See also: Found, NotExists, NoConflict */ +/* Opcode: NoConflict P1 P2 P3 P4 * +** Synopsis: key=r[P3@P4] +** +** If P4==0 then register P3 holds a blob constructed by MakeRecord. If +** P4>0 then register P3 is the first of P4 registers that form an unpacked +** record. +** +** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** contains any NULL value, jump immediately to P2. If all terms of the +** record are not-NULL then a check is done to determine if any row in the +** P1 index btree has a matching key prefix. If there are no matches, jump +** immediately to P2. If there is a match, fall through and leave the P1 +** cursor pointing to the matching row. +** +** This opcode is similar to OP_NotFound with the exceptions that the +** branch is always taken if any part of the search key input is NULL. +** +** See also: NotFound, Found, NotExists +*/ +case OP_NoConflict: /* jump, in3 */ case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ -#if 0 /* local variables moved into u.be */ int alreadyExists; + int ii; VdbeCursor *pC; int res; char *pFree; UnpackedRecord *pIdxKey; UnpackedRecord r; - char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; -#endif /* local variables moved into u.be */ + char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*4 + 7]; #ifdef SQLITE_TEST - sqlite3_found_count++; + if( pOp->opcode!=OP_NoConflict ) sqlite3_found_count++; #endif - u.be.alreadyExists = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p4type==P4_INT32 ); - u.be.pC = p->apCsr[pOp->p1]; - assert( u.be.pC!=0 ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); pIn3 = &aMem[pOp->p3]; - if( ALWAYS(u.be.pC->pCursor!=0) ){ - - assert( u.be.pC->isTable==0 ); - if( pOp->p4.i>0 ){ - u.be.r.pKeyInfo = u.be.pC->pKeyInfo; - u.be.r.nField = (u16)pOp->p4.i; - u.be.r.aMem = pIn3; + assert( pC->pCursor!=0 ); + assert( pC->isTable==0 ); + pFree = 0; /* Not needed. Only used to suppress a compiler warning. */ + if( pOp->p4.i>0 ){ + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)pOp->p4.i; + r.aMem = pIn3; #ifdef SQLITE_DEBUG - { int i; for(i=0; ip3+i, &r.aMem[i]); + } + } #endif - u.be.r.flags = UNPACKED_PREFIX_MATCH; - u.be.pIdxKey = &u.be.r; - }else{ - u.be.pIdxKey = sqlite3VdbeAllocUnpackedRecord( - u.be.pC->pKeyInfo, u.be.aTempRec, sizeof(u.be.aTempRec), &u.be.pFree - ); - if( u.be.pIdxKey==0 ) goto no_mem; - assert( pIn3->flags & MEM_Blob ); - assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ - sqlite3VdbeRecordUnpack(u.be.pC->pKeyInfo, pIn3->n, pIn3->z, u.be.pIdxKey); - u.be.pIdxKey->flags |= UNPACKED_PREFIX_MATCH; - } - rc = sqlite3BtreeMovetoUnpacked(u.be.pC->pCursor, u.be.pIdxKey, 0, 0, &u.be.res); - if( pOp->p4.i==0 ){ - sqlite3DbFree(db, u.be.pFree); - } - if( rc!=SQLITE_OK ){ - break; - } - u.be.alreadyExists = (u.be.res==0); - u.be.pC->deferredMoveto = 0; - u.be.pC->cacheStatus = CACHE_STALE; - } - if( pOp->opcode==OP_Found ){ - if( u.be.alreadyExists ) pc = pOp->p2 - 1; + r.flags = UNPACKED_PREFIX_MATCH; + pIdxKey = &r; }else{ - if( !u.be.alreadyExists ) pc = pOp->p2 - 1; + pIdxKey = sqlite3VdbeAllocUnpackedRecord( + pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree + ); + if( pIdxKey==0 ) goto no_mem; + assert( pIn3->flags & MEM_Blob ); + assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ + sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); + pIdxKey->flags |= UNPACKED_PREFIX_MATCH; } - break; -} - -/* Opcode: IsUnique P1 P2 P3 P4 * -** -** Cursor P1 is open on an index b-tree - that is to say, a btree which -** no data and where the key are records generated by OP_MakeRecord with -** the list field being the integer ROWID of the entry that the index -** entry refers to. -** -** The P3 register contains an integer record number. Call this record -** number R. Register P4 is the first in a set of N contiguous registers -** that make up an unpacked index key that can be used with cursor P1. -** The value of N can be inferred from the cursor. N includes the rowid -** value appended to the end of the index record. This rowid value may -** or may not be the same as R. -** -** If any of the N registers beginning with register P4 contains a NULL -** value, jump immediately to P2. -** -** Otherwise, this instruction checks if cursor P1 contains an entry -** where the first (N-1) fields match but the rowid value at the end -** of the index entry is not R. If there is no such entry, control jumps -** to instruction P2. Otherwise, the rowid of the conflicting index -** entry is copied to register P3 and control falls through to the next -** instruction. -** -** See also: NotFound, NotExists, Found -*/ -case OP_IsUnique: { /* jump, in3 */ -#if 0 /* local variables moved into u.bf */ - u16 ii; - VdbeCursor *pCx; - BtCursor *pCrsr; - u16 nField; - Mem *aMx; - UnpackedRecord r; /* B-Tree index search key */ - i64 R; /* Rowid stored in register P3 */ -#endif /* local variables moved into u.bf */ - - pIn3 = &aMem[pOp->p3]; - u.bf.aMx = &aMem[pOp->p4.i]; - /* Assert that the values of parameters P1 and P4 are in range. */ - assert( pOp->p4type==P4_INT32 ); - assert( pOp->p4.i>0 && pOp->p4.i<=(p->nMem-p->nCursor) ); - assert( pOp->p1>=0 && pOp->p1nCursor ); - - /* Find the index cursor. */ - u.bf.pCx = p->apCsr[pOp->p1]; - assert( u.bf.pCx->deferredMoveto==0 ); - u.bf.pCx->seekResult = 0; - u.bf.pCx->cacheStatus = CACHE_STALE; - u.bf.pCrsr = u.bf.pCx->pCursor; - - /* If any of the values are NULL, take the jump. */ - u.bf.nField = u.bf.pCx->pKeyInfo->nField; - for(u.bf.ii=0; u.bf.iip2 - 1; - u.bf.pCrsr = 0; - break; + if( pOp->opcode==OP_NoConflict ){ + /* For the OP_NoConflict opcode, take the jump if any of the + ** input fields are NULL, since any key with a NULL will not + ** conflict */ + for(ii=0; iip2 - 1; + break; + } } } - assert( (u.bf.aMx[u.bf.nField].flags & MEM_Null)==0 ); - - if( u.bf.pCrsr!=0 ){ - /* Populate the index search key. */ - u.bf.r.pKeyInfo = u.bf.pCx->pKeyInfo; - u.bf.r.nField = u.bf.nField + 1; - u.bf.r.flags = UNPACKED_PREFIX_SEARCH; - u.bf.r.aMem = u.bf.aMx; -#ifdef SQLITE_DEBUG - { int i; for(i=0; iu.i; - - /* Search the B-Tree index. If no conflicting record is found, jump - ** to P2. Otherwise, copy the rowid of the conflicting record to - ** register P3 and fall through to the next instruction. */ - rc = sqlite3BtreeMovetoUnpacked(u.bf.pCrsr, &u.bf.r, 0, 0, &u.bf.pCx->seekResult); - if( (u.bf.r.flags & UNPACKED_PREFIX_SEARCH) || u.bf.r.rowid==u.bf.R ){ - pc = pOp->p2 - 1; - }else{ - pIn3->u.i = u.bf.r.rowid; - } + rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res); + if( pOp->p4.i==0 ){ + sqlite3DbFree(db, pFree); + } + if( rc!=SQLITE_OK ){ + break; + } + pC->seekResult = res; + alreadyExists = (res==0); + pC->nullRow = 1-alreadyExists; + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; + if( pOp->opcode==OP_Found ){ + if( alreadyExists ) pc = pOp->p2 - 1; + }else{ + if( !alreadyExists ) pc = pOp->p2 - 1; } break; } /* Opcode: NotExists P1 P2 P3 * * +** Synopsis: intkey=r[P3] ** -** Use the content of register P3 as an integer key. If a record -** with that key does not exist in table of P1, then jump to P2. -** If the record does exist, then fall through. The cursor is left -** pointing to the record if it exists. +** P1 is the index of a cursor open on an SQL table btree (with integer +** keys). P3 is an integer rowid. If P1 does not contain a record with +** rowid P3 then jump immediately to P2. If P1 does contain a record +** with rowid P3 then leave the cursor pointing at that record and fall +** through to the next instruction. ** -** The difference between this operation and NotFound is that this -** operation assumes the key is an integer and that P1 is a table whereas -** NotFound assumes key is a blob constructed from MakeRecord and -** P1 is an index. +** The OP_NotFound opcode performs the same operation on index btrees +** (with arbitrary multi-value keys). ** -** See also: Found, NotFound, IsUnique +** See also: Found, NotFound, NoConflict */ case OP_NotExists: { /* jump, in3 */ -#if 0 /* local variables moved into u.bg */ VdbeCursor *pC; BtCursor *pCrsr; int res; u64 iKey; -#endif /* local variables moved into u.bg */ pIn3 = &aMem[pOp->p3]; assert( pIn3->flags & MEM_Int ); assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bg.pC = p->apCsr[pOp->p1]; - assert( u.bg.pC!=0 ); - assert( u.bg.pC->isTable ); - assert( u.bg.pC->pseudoTableReg==0 ); - u.bg.pCrsr = u.bg.pC->pCursor; - if( ALWAYS(u.bg.pCrsr!=0) ){ - u.bg.res = 0; - u.bg.iKey = pIn3->u.i; - rc = sqlite3BtreeMovetoUnpacked(u.bg.pCrsr, 0, u.bg.iKey, 0, &u.bg.res); - u.bg.pC->lastRowid = pIn3->u.i; - u.bg.pC->rowidIsValid = u.bg.res==0 ?1:0; - u.bg.pC->nullRow = 0; - u.bg.pC->cacheStatus = CACHE_STALE; - u.bg.pC->deferredMoveto = 0; - if( u.bg.res!=0 ){ - pc = pOp->p2 - 1; - assert( u.bg.pC->rowidIsValid==0 ); - } - u.bg.pC->seekResult = u.bg.res; - }else{ - /* This happens when an attempt to open a read cursor on the - ** sqlite_master table returns SQLITE_EMPTY. - */ + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->isTable ); + assert( pC->pseudoTableReg==0 ); + pCrsr = pC->pCursor; + assert( pCrsr!=0 ); + res = 0; + iKey = pIn3->u.i; + rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); + pC->lastRowid = pIn3->u.i; + pC->rowidIsValid = res==0 ?1:0; + pC->nullRow = 0; + pC->cacheStatus = CACHE_STALE; + pC->deferredMoveto = 0; + if( res!=0 ){ pc = pOp->p2 - 1; - assert( u.bg.pC->rowidIsValid==0 ); - u.bg.pC->seekResult = 0; + assert( pC->rowidIsValid==0 ); } + pC->seekResult = res; break; } /* Opcode: Sequence P1 P2 * * * +** Synopsis: r[P2]=rowid ** ** Find the next available sequence number for cursor P1. ** Write the sequence number into register P2. @@ -70059,6 +70279,7 @@ case OP_Sequence: { /* out2-prerelease */ /* Opcode: NewRowid P1 P2 P3 * * +** Synopsis: r[P2]=rowid ** ** Get a new integer record number (a.k.a "rowid") used as the key to a table. ** The record number is not previously used as a key in the database @@ -70073,21 +70294,19 @@ case OP_Sequence: { /* out2-prerelease */ ** AUTOINCREMENT feature. */ case OP_NewRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bh */ i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ int cnt; /* Counter to limit the number of searches */ Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ VdbeFrame *pFrame; /* Root frame of VDBE */ -#endif /* local variables moved into u.bh */ - u.bh.v = 0; - u.bh.res = 0; + v = 0; + res = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bh.pC = p->apCsr[pOp->p1]; - assert( u.bh.pC!=0 ); - if( NEVER(u.bh.pC->pCursor==0) ){ + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + if( NEVER(pC->pCursor==0) ){ /* The zero initialization above is all that is needed */ }else{ /* The next rowid or record number (different terms for the same @@ -70103,7 +70322,7 @@ case OP_NewRowid: { /* out2-prerelease */ ** succeeded. If the random rowid does exist, we select a new one ** and try again, up to 100 times. */ - assert( u.bh.pC->isTable ); + assert( pC->isTable ); #ifdef SQLITE_32BIT_ROWID # define MAX_ROWID 0x7fffffff @@ -70115,23 +70334,23 @@ case OP_NewRowid: { /* out2-prerelease */ # define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) #endif - if( !u.bh.pC->useRandomRowid ){ - u.bh.v = sqlite3BtreeGetCachedRowid(u.bh.pC->pCursor); - if( u.bh.v==0 ){ - rc = sqlite3BtreeLast(u.bh.pC->pCursor, &u.bh.res); + if( !pC->useRandomRowid ){ + v = sqlite3BtreeGetCachedRowid(pC->pCursor); + if( v==0 ){ + rc = sqlite3BtreeLast(pC->pCursor, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - if( u.bh.res ){ - u.bh.v = 1; /* IMP: R-61914-48074 */ + if( res ){ + v = 1; /* IMP: R-61914-48074 */ }else{ - assert( sqlite3BtreeCursorIsValid(u.bh.pC->pCursor) ); - rc = sqlite3BtreeKeySize(u.bh.pC->pCursor, &u.bh.v); + assert( sqlite3BtreeCursorIsValid(pC->pCursor) ); + rc = sqlite3BtreeKeySize(pC->pCursor, &v); assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ - if( u.bh.v>=MAX_ROWID ){ - u.bh.pC->useRandomRowid = 1; + if( v>=MAX_ROWID ){ + pC->useRandomRowid = 1; }else{ - u.bh.v++; /* IMP: R-29538-34987 */ + v++; /* IMP: R-29538-34987 */ } } } @@ -70141,35 +70360,35 @@ case OP_NewRowid: { /* out2-prerelease */ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3>0 ); if( p->pFrame ){ - for(u.bh.pFrame=p->pFrame; u.bh.pFrame->pParent; u.bh.pFrame=u.bh.pFrame->pParent); + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=u.bh.pFrame->nMem ); - u.bh.pMem = &u.bh.pFrame->aMem[pOp->p3]; + assert( pOp->p3<=pFrame->nMem ); + pMem = &pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=(p->nMem-p->nCursor) ); - u.bh.pMem = &aMem[pOp->p3]; - memAboutToChange(p, u.bh.pMem); + pMem = &aMem[pOp->p3]; + memAboutToChange(p, pMem); } - assert( memIsValid(u.bh.pMem) ); + assert( memIsValid(pMem) ); - REGISTER_TRACE(pOp->p3, u.bh.pMem); - sqlite3VdbeMemIntegerify(u.bh.pMem); - assert( (u.bh.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( u.bh.pMem->u.i==MAX_ROWID || u.bh.pC->useRandomRowid ){ + REGISTER_TRACE(pOp->p3, pMem); + sqlite3VdbeMemIntegerify(pMem); + assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ + if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ rc = SQLITE_FULL; /* IMP: R-12275-61338 */ goto abort_due_to_error; } - if( u.bh.vu.i+1 ){ - u.bh.v = u.bh.pMem->u.i + 1; + if( vu.i+1 ){ + v = pMem->u.i + 1; } - u.bh.pMem->u.i = u.bh.v; + pMem->u.i = v; } #endif - sqlite3BtreeSetCachedRowid(u.bh.pC->pCursor, u.bh.vpCursor, vuseRandomRowid ){ + if( pC->useRandomRowid ){ /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the ** largest possible integer (9223372036854775807) then the database ** engine starts picking positive candidate ROWIDs at random until @@ -70177,39 +70396,40 @@ case OP_NewRowid: { /* out2-prerelease */ assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is ** an AUTOINCREMENT table. */ /* on the first attempt, simply do one more than previous */ - u.bh.v = lastRowid; - u.bh.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ - u.bh.v++; /* ensure non-zero */ - u.bh.cnt = 0; - while( ((rc = sqlite3BtreeMovetoUnpacked(u.bh.pC->pCursor, 0, (u64)u.bh.v, - 0, &u.bh.res))==SQLITE_OK) - && (u.bh.res==0) - && (++u.bh.cnt<100)){ + v = lastRowid; + v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ + v++; /* ensure non-zero */ + cnt = 0; + while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v, + 0, &res))==SQLITE_OK) + && (res==0) + && (++cnt<100)){ /* collision - try another random rowid */ - sqlite3_randomness(sizeof(u.bh.v), &u.bh.v); - if( u.bh.cnt<5 ){ + sqlite3_randomness(sizeof(v), &v); + if( cnt<5 ){ /* try "small" random rowids for the initial attempts */ - u.bh.v &= 0xffffff; + v &= 0xffffff; }else{ - u.bh.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ + v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ } - u.bh.v++; /* ensure non-zero */ + v++; /* ensure non-zero */ } - if( rc==SQLITE_OK && u.bh.res==0 ){ + if( rc==SQLITE_OK && res==0 ){ rc = SQLITE_FULL; /* IMP: R-38219-53002 */ goto abort_due_to_error; } - assert( u.bh.v>0 ); /* EV: R-40812-03570 */ + assert( v>0 ); /* EV: R-40812-03570 */ } - u.bh.pC->rowidIsValid = 0; - u.bh.pC->deferredMoveto = 0; - u.bh.pC->cacheStatus = CACHE_STALE; + pC->rowidIsValid = 0; + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; } - pOut->u.i = u.bh.v; + pOut->u.i = v; break; } /* Opcode: Insert P1 P2 P3 P4 P5 +** Synopsis: intkey=r[P3] data=r[P2] ** ** Write an entry into the table of cursor P1. A new entry is ** created if it doesn't already exist or the data for an existing @@ -70249,13 +70469,13 @@ case OP_NewRowid: { /* out2-prerelease */ ** for indices is OP_IdxInsert. */ /* Opcode: InsertInt P1 P2 P3 P4 P5 +** Synopsis: intkey=P3 data=r[P2] ** ** This works exactly like OP_Insert except that the key is the ** integer value P3, not the value of the integer stored in register P3. */ case OP_Insert: case OP_InsertInt: { -#if 0 /* local variables moved into u.bi */ Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ i64 iKey; /* The integer ROWID or key for the record to be inserted */ @@ -70265,60 +70485,59 @@ case OP_InsertInt: { const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ -#endif /* local variables moved into u.bi */ - u.bi.pData = &aMem[pOp->p2]; + pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1nCursor ); - assert( memIsValid(u.bi.pData) ); - u.bi.pC = p->apCsr[pOp->p1]; - assert( u.bi.pC!=0 ); - assert( u.bi.pC->pCursor!=0 ); - assert( u.bi.pC->pseudoTableReg==0 ); - assert( u.bi.pC->isTable ); - REGISTER_TRACE(pOp->p2, u.bi.pData); + assert( memIsValid(pData) ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->pCursor!=0 ); + assert( pC->pseudoTableReg==0 ); + assert( pC->isTable ); + REGISTER_TRACE(pOp->p2, pData); if( pOp->opcode==OP_Insert ){ - u.bi.pKey = &aMem[pOp->p3]; - assert( u.bi.pKey->flags & MEM_Int ); - assert( memIsValid(u.bi.pKey) ); - REGISTER_TRACE(pOp->p3, u.bi.pKey); - u.bi.iKey = u.bi.pKey->u.i; + pKey = &aMem[pOp->p3]; + assert( pKey->flags & MEM_Int ); + assert( memIsValid(pKey) ); + REGISTER_TRACE(pOp->p3, pKey); + iKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); - u.bi.iKey = pOp->p3; + iKey = pOp->p3; } if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = u.bi.iKey; - if( u.bi.pData->flags & MEM_Null ){ - u.bi.pData->z = 0; - u.bi.pData->n = 0; + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = iKey; + if( pData->flags & MEM_Null ){ + pData->z = 0; + pData->n = 0; }else{ - assert( u.bi.pData->flags & (MEM_Blob|MEM_Str) ); + assert( pData->flags & (MEM_Blob|MEM_Str) ); } - u.bi.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bi.pC->seekResult : 0); - if( u.bi.pData->flags & MEM_Zero ){ - u.bi.nZero = u.bi.pData->u.nZero; + seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); + if( pData->flags & MEM_Zero ){ + nZero = pData->u.nZero; }else{ - u.bi.nZero = 0; + nZero = 0; } - sqlite3BtreeSetCachedRowid(u.bi.pC->pCursor, 0); - rc = sqlite3BtreeInsert(u.bi.pC->pCursor, 0, u.bi.iKey, - u.bi.pData->z, u.bi.pData->n, u.bi.nZero, - pOp->p5 & OPFLAG_APPEND, u.bi.seekResult + sqlite3BtreeSetCachedRowid(pC->pCursor, 0); + rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, + pData->z, pData->n, nZero, + (pOp->p5 & OPFLAG_APPEND)!=0, seekResult ); - u.bi.pC->rowidIsValid = 0; - u.bi.pC->deferredMoveto = 0; - u.bi.pC->cacheStatus = CACHE_STALE; + pC->rowidIsValid = 0; + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - u.bi.zDb = db->aDb[u.bi.pC->iDb].zName; - u.bi.zTbl = pOp->p4.z; - u.bi.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); - assert( u.bi.pC->isTable ); - db->xUpdateCallback(db->pUpdateArg, u.bi.op, u.bi.zDb, u.bi.zTbl, u.bi.iKey); - assert( u.bi.pC->iDb>=0 ); + zDb = db->aDb[pC->iDb].zName; + zTbl = pOp->p4.z; + op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); + assert( pC->isTable ); + db->xUpdateCallback(db->pUpdateArg, op, zDb, zTbl, iKey); + assert( pC->iDb>=0 ); } break; } @@ -70344,47 +70563,35 @@ case OP_InsertInt: { ** using OP_NotFound prior to invoking this opcode. */ case OP_Delete: { -#if 0 /* local variables moved into u.bj */ i64 iKey; VdbeCursor *pC; -#endif /* local variables moved into u.bj */ - u.bj.iKey = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bj.pC = p->apCsr[pOp->p1]; - assert( u.bj.pC!=0 ); - assert( u.bj.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ - - /* If the update-hook will be invoked, set u.bj.iKey to the rowid of the - ** row being deleted. - */ - if( db->xUpdateCallback && pOp->p4.z ){ - assert( u.bj.pC->isTable ); - assert( u.bj.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ - u.bj.iKey = u.bj.pC->lastRowid; - } + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ + iKey = pC->lastRowid; /* Only used for the update hook */ /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or ** OP_Column on the same table without any intervening operations that - ** might move or invalidate the cursor. Hence cursor u.bj.pC is always pointing + ** might move or invalidate the cursor. Hence cursor pC is always pointing ** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation ** below is always a no-op and cannot fail. We will run it anyhow, though, ** to guard against future changes to the code generator. **/ - assert( u.bj.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bj.pC); + assert( pC->deferredMoveto==0 ); + rc = sqlite3VdbeCursorMoveto(pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - sqlite3BtreeSetCachedRowid(u.bj.pC->pCursor, 0); - rc = sqlite3BtreeDelete(u.bj.pC->pCursor); - u.bj.pC->cacheStatus = CACHE_STALE; + sqlite3BtreeSetCachedRowid(pC->pCursor, 0); + rc = sqlite3BtreeDelete(pC->pCursor); + pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ - if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - const char *zDb = db->aDb[u.bj.pC->iDb].zName; - const char *zTbl = pOp->p4.z; - db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bj.iKey); - assert( u.bj.pC->iDb>=0 ); + if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z && pC->isTable ){ + db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, + db->aDb[pC->iDb].zName, pOp->p4.z, iKey); + assert( pC->iDb>=0 ); } if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; break; @@ -70402,46 +70609,55 @@ case OP_ResetCount: { break; } -/* Opcode: SorterCompare P1 P2 P3 +/* Opcode: SorterCompare P1 P2 P3 P4 +** Synopsis: if key(P1)!=rtrim(r[P3],P4) goto P2 ** -** P1 is a sorter cursor. This instruction compares the record blob in -** register P3 with the entry that the sorter cursor currently points to. -** If, excluding the rowid fields at the end, the two records are a match, -** fall through to the next instruction. Otherwise, jump to instruction P2. +** P1 is a sorter cursor. This instruction compares a prefix of the +** the record blob in register P3 against a prefix of the entry that +** the sorter cursor currently points to. The final P4 fields of both +** the P3 and sorter record are ignored. +** +** If either P3 or the sorter contains a NULL in one of their significant +** fields (not counting the P4 fields at the end which are ignored) then +** the comparison is assumed to be equal. +** +** Fall through to next instruction if the two records compare equal to +** each other. Jump to P2 if they are different. */ case OP_SorterCompare: { -#if 0 /* local variables moved into u.bk */ VdbeCursor *pC; int res; -#endif /* local variables moved into u.bk */ + int nIgnore; - u.bk.pC = p->apCsr[pOp->p1]; - assert( isSorter(u.bk.pC) ); + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + assert( pOp->p4type==P4_INT32 ); pIn3 = &aMem[pOp->p3]; - rc = sqlite3VdbeSorterCompare(u.bk.pC, pIn3, &u.bk.res); - if( u.bk.res ){ + nIgnore = pOp->p4.i; + rc = sqlite3VdbeSorterCompare(pC, pIn3, nIgnore, &res); + if( res ){ pc = pOp->p2-1; } break; }; /* Opcode: SorterData P1 P2 * * * +** Synopsis: r[P2]=data ** ** Write into register P2 the current sorter data for sorter cursor P1. */ case OP_SorterData: { -#if 0 /* local variables moved into u.bl */ VdbeCursor *pC; -#endif /* local variables moved into u.bl */ pOut = &aMem[pOp->p2]; - u.bl.pC = p->apCsr[pOp->p1]; - assert( u.bl.pC->isSorter ); - rc = sqlite3VdbeSorterRowkey(u.bl.pC, pOut); + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + rc = sqlite3VdbeSorterRowkey(pC, pOut); break; } /* Opcode: RowData P1 P2 * * * +** Synopsis: r[P2]=data ** ** Write into register P2 the complete row data for cursor P1. ** There is no interpretation of the data. @@ -70452,6 +70668,7 @@ case OP_SorterData: { ** of a real table, not a pseudo-table. */ /* Opcode: RowKey P1 P2 * * * +** Synopsis: r[P2]=key ** ** Write into register P2 the complete row key for cursor P1. ** There is no interpretation of the data. @@ -70463,69 +70680,69 @@ case OP_SorterData: { */ case OP_RowKey: case OP_RowData: { -#if 0 /* local variables moved into u.bm */ VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; -#endif /* local variables moved into u.bm */ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bm.pC = p->apCsr[pOp->p1]; - assert( u.bm.pC->isSorter==0 ); - assert( u.bm.pC->isTable || pOp->opcode!=OP_RowData ); - assert( u.bm.pC->isIndex || pOp->opcode==OP_RowData ); - assert( u.bm.pC!=0 ); - assert( u.bm.pC->nullRow==0 ); - assert( u.bm.pC->pseudoTableReg==0 ); - assert( u.bm.pC->pCursor!=0 ); - u.bm.pCrsr = u.bm.pC->pCursor; - assert( sqlite3BtreeCursorIsValid(u.bm.pCrsr) ); + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC)==0 ); + assert( pC->isTable || pOp->opcode!=OP_RowData ); + assert( pC->isTable==0 || pOp->opcode==OP_RowData ); + assert( pC!=0 ); + assert( pC->nullRow==0 ); + assert( pC->pseudoTableReg==0 ); + assert( pC->pCursor!=0 ); + pCrsr = pC->pCursor; + assert( sqlite3BtreeCursorIsValid(pCrsr) ); /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or ** OP_Rewind/Op_Next with no intervening instructions that might invalidate ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always ** a no-op and can never fail. But we leave it in place as a safety. */ - assert( u.bm.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bm.pC); + assert( pC->deferredMoveto==0 ); + rc = sqlite3VdbeCursorMoveto(pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - if( u.bm.pC->isIndex ){ - assert( !u.bm.pC->isTable ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(u.bm.pCrsr, &u.bm.n64); + if( pC->isTable==0 ){ + assert( !pC->isTable ); + VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &n64); assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - if( u.bm.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + if( n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } - u.bm.n = (u32)u.bm.n64; + n = (u32)n64; }else{ - VVA_ONLY(rc =) sqlite3BtreeDataSize(u.bm.pCrsr, &u.bm.n); + VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &n); assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - if( u.bm.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } } - if( sqlite3VdbeMemGrow(pOut, u.bm.n, 0) ){ + if( sqlite3VdbeMemGrow(pOut, n, 0) ){ goto no_mem; } - pOut->n = u.bm.n; + pOut->n = n; MemSetTypeFlag(pOut, MEM_Blob); - if( u.bm.pC->isIndex ){ - rc = sqlite3BtreeKey(u.bm.pCrsr, 0, u.bm.n, pOut->z); + if( pC->isTable==0 ){ + rc = sqlite3BtreeKey(pCrsr, 0, n, pOut->z); }else{ - rc = sqlite3BtreeData(u.bm.pCrsr, 0, u.bm.n, pOut->z); + rc = sqlite3BtreeData(pCrsr, 0, n, pOut->z); } pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ UPDATE_MAX_BLOBSIZE(pOut); + REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: Rowid P1 P2 * * * +** Synopsis: r[P2]=rowid ** ** Store in register P2 an integer which is the key of the table entry that ** P1 is currently point to. @@ -70535,42 +70752,40 @@ case OP_RowData: { ** one opcode now works for both table types. */ case OP_Rowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bn */ VdbeCursor *pC; i64 v; sqlite3_vtab *pVtab; const sqlite3_module *pModule; -#endif /* local variables moved into u.bn */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bn.pC = p->apCsr[pOp->p1]; - assert( u.bn.pC!=0 ); - assert( u.bn.pC->pseudoTableReg==0 || u.bn.pC->nullRow ); - if( u.bn.pC->nullRow ){ + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->pseudoTableReg==0 || pC->nullRow ); + if( pC->nullRow ){ pOut->flags = MEM_Null; break; - }else if( u.bn.pC->deferredMoveto ){ - u.bn.v = u.bn.pC->movetoTarget; + }else if( pC->deferredMoveto ){ + v = pC->movetoTarget; #ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( u.bn.pC->pVtabCursor ){ - u.bn.pVtab = u.bn.pC->pVtabCursor->pVtab; - u.bn.pModule = u.bn.pVtab->pModule; - assert( u.bn.pModule->xRowid ); - rc = u.bn.pModule->xRowid(u.bn.pC->pVtabCursor, &u.bn.v); - sqlite3VtabImportErrmsg(p, u.bn.pVtab); + }else if( pC->pVtabCursor ){ + pVtab = pC->pVtabCursor->pVtab; + pModule = pVtab->pModule; + assert( pModule->xRowid ); + rc = pModule->xRowid(pC->pVtabCursor, &v); + sqlite3VtabImportErrmsg(p, pVtab); #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ - assert( u.bn.pC->pCursor!=0 ); - rc = sqlite3VdbeCursorMoveto(u.bn.pC); + assert( pC->pCursor!=0 ); + rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; - if( u.bn.pC->rowidIsValid ){ - u.bn.v = u.bn.pC->lastRowid; + if( pC->rowidIsValid ){ + v = pC->lastRowid; }else{ - rc = sqlite3BtreeKeySize(u.bn.pC->pCursor, &u.bn.v); + rc = sqlite3BtreeKeySize(pC->pCursor, &v); assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */ } } - pOut->u.i = u.bn.v; + pOut->u.i = v; break; } @@ -70581,18 +70796,16 @@ case OP_Rowid: { /* out2-prerelease */ ** write a NULL. */ case OP_NullRow: { -#if 0 /* local variables moved into u.bo */ VdbeCursor *pC; -#endif /* local variables moved into u.bo */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bo.pC = p->apCsr[pOp->p1]; - assert( u.bo.pC!=0 ); - u.bo.pC->nullRow = 1; - u.bo.pC->rowidIsValid = 0; - assert( u.bo.pC->pCursor || u.bo.pC->pVtabCursor ); - if( u.bo.pC->pCursor ){ - sqlite3BtreeClearCursor(u.bo.pC->pCursor); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + pC->nullRow = 1; + pC->rowidIsValid = 0; + pC->cacheStatus = CACHE_STALE; + if( pC->pCursor ){ + sqlite3BtreeClearCursor(pC->pCursor); } break; } @@ -70606,25 +70819,22 @@ case OP_NullRow: { ** to the following instruction. */ case OP_Last: { /* jump */ -#if 0 /* local variables moved into u.bp */ VdbeCursor *pC; BtCursor *pCrsr; int res; -#endif /* local variables moved into u.bp */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bp.pC = p->apCsr[pOp->p1]; - assert( u.bp.pC!=0 ); - u.bp.pCrsr = u.bp.pC->pCursor; - u.bp.res = 0; - if( ALWAYS(u.bp.pCrsr!=0) ){ - rc = sqlite3BtreeLast(u.bp.pCrsr, &u.bp.res); - } - u.bp.pC->nullRow = (u8)u.bp.res; - u.bp.pC->deferredMoveto = 0; - u.bp.pC->rowidIsValid = 0; - u.bp.pC->cacheStatus = CACHE_STALE; - if( pOp->p2>0 && u.bp.res ){ + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + pCrsr = pC->pCursor; + res = 0; + assert( pCrsr!=0 ); + rc = sqlite3BtreeLast(pCrsr, &res); + pC->nullRow = (u8)res; + pC->deferredMoveto = 0; + pC->rowidIsValid = 0; + pC->cacheStatus = CACHE_STALE; + if( pOp->p2>0 && res ){ pc = pOp->p2 - 1; } break; @@ -70661,44 +70871,42 @@ case OP_Sort: { /* jump */ ** to the following instruction. */ case OP_Rewind: { /* jump */ -#if 0 /* local variables moved into u.bq */ VdbeCursor *pC; BtCursor *pCrsr; int res; -#endif /* local variables moved into u.bq */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bq.pC = p->apCsr[pOp->p1]; - assert( u.bq.pC!=0 ); - assert( u.bq.pC->isSorter==(pOp->opcode==OP_SorterSort) ); - u.bq.res = 1; - if( isSorter(u.bq.pC) ){ - rc = sqlite3VdbeSorterRewind(db, u.bq.pC, &u.bq.res); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); + res = 1; + if( isSorter(pC) ){ + rc = sqlite3VdbeSorterRewind(db, pC, &res); }else{ - u.bq.pCrsr = u.bq.pC->pCursor; - assert( u.bq.pCrsr ); - rc = sqlite3BtreeFirst(u.bq.pCrsr, &u.bq.res); - u.bq.pC->atFirst = u.bq.res==0 ?1:0; - u.bq.pC->deferredMoveto = 0; - u.bq.pC->cacheStatus = CACHE_STALE; - u.bq.pC->rowidIsValid = 0; + pCrsr = pC->pCursor; + assert( pCrsr ); + rc = sqlite3BtreeFirst(pCrsr, &res); + pC->deferredMoveto = 0; + pC->cacheStatus = CACHE_STALE; + pC->rowidIsValid = 0; } - u.bq.pC->nullRow = (u8)u.bq.res; + pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2nOp ); - if( u.bq.res ){ + if( res ){ pc = pOp->p2 - 1; } break; } -/* Opcode: Next P1 P2 * P4 P5 +/* Opcode: Next P1 P2 * * P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through ** to the following instruction. But if the cursor advance was successful, ** jump immediately to P2. ** -** The P1 cursor must be for a real table, not a pseudo-table. +** The P1 cursor must be for a real table, not a pseudo-table. P1 must have +** been opened prior to this opcode or the program will segfault. ** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreeNext(). @@ -70706,7 +70914,12 @@ case OP_Rewind: { /* jump */ ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. ** -** See also: Prev +** See also: Prev, NextIfOpen +*/ +/* Opcode: NextIfOpen P1 P2 * * P5 +** +** This opcode works just like OP_Next except that if cursor P1 is not +** open it behaves a no-op. */ /* Opcode: Prev P1 P2 * * P5 ** @@ -70715,7 +70928,8 @@ case OP_Rewind: { /* jump */ ** to the following instruction. But if the cursor backup was successful, ** jump immediately to P2. ** -** The P1 cursor must be for a real table, not a pseudo-table. +** The P1 cursor must be for a real table, not a pseudo-table. If P1 is +** not open then the behavior is undefined. ** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreePrevious(). @@ -70723,46 +70937,54 @@ case OP_Rewind: { /* jump */ ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. */ -case OP_SorterNext: /* jump */ -case OP_Prev: /* jump */ -case OP_Next: { /* jump */ -#if 0 /* local variables moved into u.br */ +/* Opcode: PrevIfOpen P1 P2 * * P5 +** +** This opcode works just like OP_Prev except that if cursor P1 is not +** open it behaves a no-op. +*/ +case OP_SorterNext: { /* jump */ VdbeCursor *pC; int res; -#endif /* local variables moved into u.br */ + pC = p->apCsr[pOp->p1]; + assert( isSorter(pC) ); + rc = sqlite3VdbeSorterNext(db, pC, &res); + goto next_tail; +case OP_PrevIfOpen: /* jump */ +case OP_NextIfOpen: /* jump */ + if( p->apCsr[pOp->p1]==0 ) break; + /* Fall through */ +case OP_Prev: /* jump */ +case OP_Next: /* jump */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p5aCounter) ); - u.br.pC = p->apCsr[pOp->p1]; - if( u.br.pC==0 ){ - break; /* See ticket #2273 */ - } - assert( u.br.pC->isSorter==(pOp->opcode==OP_SorterNext) ); - if( isSorter(u.br.pC) ){ - assert( pOp->opcode==OP_SorterNext ); - rc = sqlite3VdbeSorterNext(db, u.br.pC, &u.br.res); - }else{ - /* u.br.res = 1; // Always initialized by the xAdvance() call */ - assert( u.br.pC->deferredMoveto==0 ); - assert( u.br.pC->pCursor ); - assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); - assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); - rc = pOp->p4.xAdvance(u.br.pC->pCursor, &u.br.res); - } - u.br.pC->nullRow = (u8)u.br.res; - u.br.pC->cacheStatus = CACHE_STALE; - if( u.br.res==0 ){ + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->deferredMoveto==0 ); + assert( pC->pCursor ); + assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); + assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); + assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext ); + assert( pOp->opcode!=OP_PrevIfOpen || pOp->p4.xAdvance==sqlite3BtreePrevious); + rc = pOp->p4.xAdvance(pC->pCursor, &res); +next_tail: + pC->cacheStatus = CACHE_STALE; + if( res==0 ){ + pC->nullRow = 0; pc = pOp->p2 - 1; p->aCounter[pOp->p5]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif + }else{ + pC->nullRow = 1; } - u.br.pC->rowidIsValid = 0; + pC->rowidIsValid = 0; goto check_for_interrupt; } /* Opcode: IdxInsert P1 P2 P3 * P5 +** Synopsis: key=r[P2] ** ** Register P2 holds an SQL index key made using the ** MakeRecord instructions. This opcode writes that key @@ -70776,79 +70998,77 @@ case OP_Next: { /* jump */ */ case OP_SorterInsert: /* in2 */ case OP_IdxInsert: { /* in2 */ -#if 0 /* local variables moved into u.bs */ VdbeCursor *pC; BtCursor *pCrsr; int nKey; const char *zKey; -#endif /* local variables moved into u.bs */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bs.pC = p->apCsr[pOp->p1]; - assert( u.bs.pC!=0 ); - assert( u.bs.pC->isSorter==(pOp->opcode==OP_SorterInsert) ); + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) ); pIn2 = &aMem[pOp->p2]; assert( pIn2->flags & MEM_Blob ); - u.bs.pCrsr = u.bs.pC->pCursor; - if( ALWAYS(u.bs.pCrsr!=0) ){ - assert( u.bs.pC->isTable==0 ); - rc = ExpandBlob(pIn2); - if( rc==SQLITE_OK ){ - if( isSorter(u.bs.pC) ){ - rc = sqlite3VdbeSorterWrite(db, u.bs.pC, pIn2); - }else{ - u.bs.nKey = pIn2->n; - u.bs.zKey = pIn2->z; - rc = sqlite3BtreeInsert(u.bs.pCrsr, u.bs.zKey, u.bs.nKey, "", 0, 0, pOp->p3, - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bs.pC->seekResult : 0) - ); - assert( u.bs.pC->deferredMoveto==0 ); - u.bs.pC->cacheStatus = CACHE_STALE; - } + pCrsr = pC->pCursor; + if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; + assert( pCrsr!=0 ); + assert( pC->isTable==0 ); + rc = ExpandBlob(pIn2); + if( rc==SQLITE_OK ){ + if( isSorter(pC) ){ + rc = sqlite3VdbeSorterWrite(db, pC, pIn2); + }else{ + nKey = pIn2->n; + zKey = pIn2->z; + rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3, + ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) + ); + assert( pC->deferredMoveto==0 ); + pC->cacheStatus = CACHE_STALE; } } break; } /* Opcode: IdxDelete P1 P2 P3 * * +** Synopsis: key=r[P2@P3] ** ** The content of P3 registers starting at register P2 form ** an unpacked index key. This opcode removes that entry from the ** index opened by cursor P1. */ case OP_IdxDelete: { -#if 0 /* local variables moved into u.bt */ VdbeCursor *pC; BtCursor *pCrsr; int res; UnpackedRecord r; -#endif /* local variables moved into u.bt */ assert( pOp->p3>0 ); assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem-p->nCursor)+1 ); assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bt.pC = p->apCsr[pOp->p1]; - assert( u.bt.pC!=0 ); - u.bt.pCrsr = u.bt.pC->pCursor; - if( ALWAYS(u.bt.pCrsr!=0) ){ - u.bt.r.pKeyInfo = u.bt.pC->pKeyInfo; - u.bt.r.nField = (u16)pOp->p3; - u.bt.r.flags = 0; - u.bt.r.aMem = &aMem[pOp->p2]; + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + pCrsr = pC->pCursor; + assert( pCrsr!=0 ); + assert( pOp->p5==0 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)pOp->p3; + r.flags = UNPACKED_PREFIX_MATCH; + r.aMem = &aMem[pOp->p2]; #ifdef SQLITE_DEBUG - { int i; for(i=0; ideferredMoveto==0 ); - u.bt.pC->cacheStatus = CACHE_STALE; + rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); + if( rc==SQLITE_OK && res==0 ){ + rc = sqlite3BtreeDelete(pCrsr); } + assert( pC->deferredMoveto==0 ); + pC->cacheStatus = CACHE_STALE; break; } /* Opcode: IdxRowid P1 P2 * * * +** Synopsis: r[P2]=rowid ** ** Write into register P2 an integer which is the last entry in the record at ** the end of the index key pointed to by cursor P1. This integer should be @@ -70857,35 +71077,34 @@ case OP_IdxDelete: { ** See also: Rowid, MakeRecord. */ case OP_IdxRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bu */ BtCursor *pCrsr; VdbeCursor *pC; i64 rowid; -#endif /* local variables moved into u.bu */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bu.pC = p->apCsr[pOp->p1]; - assert( u.bu.pC!=0 ); - u.bu.pCrsr = u.bu.pC->pCursor; + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + pCrsr = pC->pCursor; + assert( pCrsr!=0 ); pOut->flags = MEM_Null; - if( ALWAYS(u.bu.pCrsr!=0) ){ - rc = sqlite3VdbeCursorMoveto(u.bu.pC); - if( NEVER(rc) ) goto abort_due_to_error; - assert( u.bu.pC->deferredMoveto==0 ); - assert( u.bu.pC->isTable==0 ); - if( !u.bu.pC->nullRow ){ - rc = sqlite3VdbeIdxRowid(db, u.bu.pCrsr, &u.bu.rowid); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - pOut->u.i = u.bu.rowid; - pOut->flags = MEM_Int; + rc = sqlite3VdbeCursorMoveto(pC); + if( NEVER(rc) ) goto abort_due_to_error; + assert( pC->deferredMoveto==0 ); + assert( pC->isTable==0 ); + if( !pC->nullRow ){ + rowid = 0; /* Not needed. Only used to silence a warning. */ + rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; } + pOut->u.i = rowid; + pOut->flags = MEM_Int; } break; } /* Opcode: IdxGE P1 P2 P3 P4 P5 +** Synopsis: key=r[P3@P4] ** ** The P4 register values beginning with P3 form an unpacked index ** key that omits the ROWID. Compare this key value against the index @@ -70900,6 +71119,7 @@ case OP_IdxRowid: { /* out2-prerelease */ ** the result is false whereas it would be true with IdxGT. */ /* Opcode: IdxLT P1 P2 P3 P4 P5 +** Synopsis: key=r[P3@P4] ** ** The P4 register values beginning with P3 form an unpacked index ** key that omits the ROWID. Compare this key value against the index @@ -70913,41 +71133,39 @@ case OP_IdxRowid: { /* out2-prerelease */ */ case OP_IdxLT: /* jump */ case OP_IdxGE: { /* jump */ -#if 0 /* local variables moved into u.bv */ VdbeCursor *pC; int res; UnpackedRecord r; -#endif /* local variables moved into u.bv */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bv.pC = p->apCsr[pOp->p1]; - assert( u.bv.pC!=0 ); - assert( u.bv.pC->isOrdered ); - if( ALWAYS(u.bv.pC->pCursor!=0) ){ - assert( u.bv.pC->deferredMoveto==0 ); - assert( pOp->p5==0 || pOp->p5==1 ); - assert( pOp->p4type==P4_INT32 ); - u.bv.r.pKeyInfo = u.bv.pC->pKeyInfo; - u.bv.r.nField = (u16)pOp->p4.i; - if( pOp->p5 ){ - u.bv.r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; - }else{ - u.bv.r.flags = UNPACKED_PREFIX_MATCH; - } - u.bv.r.aMem = &aMem[pOp->p3]; + pC = p->apCsr[pOp->p1]; + assert( pC!=0 ); + assert( pC->isOrdered ); + assert( pC->pCursor!=0); + assert( pC->deferredMoveto==0 ); + assert( pOp->p5==0 || pOp->p5==1 ); + assert( pOp->p4type==P4_INT32 ); + r.pKeyInfo = pC->pKeyInfo; + r.nField = (u16)pOp->p4.i; + if( pOp->p5 ){ + r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; + }else{ + r.flags = UNPACKED_PREFIX_MATCH; + } + r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG - { int i; for(i=0; iopcode==OP_IdxLT ){ - u.bv.res = -u.bv.res; - }else{ - assert( pOp->opcode==OP_IdxGE ); - u.bv.res++; - } - if( u.bv.res>0 ){ - pc = pOp->p2 - 1 ; - } + res = 0; /* Not needed. Only used to silence a warning. */ + rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res); + if( pOp->opcode==OP_IdxLT ){ + res = -res; + }else{ + assert( pOp->opcode==OP_IdxGE ); + res++; + } + if( res>0 ){ + pc = pOp->p2 - 1 ; } break; } @@ -70973,43 +71191,42 @@ case OP_IdxGE: { /* jump */ ** See also: Clear */ case OP_Destroy: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bw */ int iMoved; int iCnt; Vdbe *pVdbe; int iDb; -#endif /* local variables moved into u.bw */ assert( p->readOnly==0 ); #ifndef SQLITE_OMIT_VIRTUALTABLE - u.bw.iCnt = 0; - for(u.bw.pVdbe=db->pVdbe; u.bw.pVdbe; u.bw.pVdbe = u.bw.pVdbe->pNext){ - if( u.bw.pVdbe->magic==VDBE_MAGIC_RUN && u.bw.pVdbe->bIsReader - && u.bw.pVdbe->inVtabMethod<2 && u.bw.pVdbe->pc>=0 + iCnt = 0; + for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){ + if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->bIsReader + && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 ){ - u.bw.iCnt++; + iCnt++; } } #else - u.bw.iCnt = db->nVdbeRead; + iCnt = db->nVdbeRead; #endif pOut->flags = MEM_Null; - if( u.bw.iCnt>1 ){ + if( iCnt>1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; }else{ - u.bw.iDb = pOp->p3; - assert( u.bw.iCnt==1 ); - assert( (p->btreeMask & (((yDbMask)1)<aDb[u.bw.iDb].pBt, pOp->p1, &u.bw.iMoved); + iDb = pOp->p3; + assert( iCnt==1 ); + assert( (p->btreeMask & (((yDbMask)1)<aDb[iDb].pBt, pOp->p1, &iMoved); pOut->flags = MEM_Int; - pOut->u.i = u.bw.iMoved; + pOut->u.i = iMoved; #ifndef SQLITE_OMIT_AUTOVACUUM - if( rc==SQLITE_OK && u.bw.iMoved!=0 ){ - sqlite3RootPageMoved(db, u.bw.iDb, u.bw.iMoved, pOp->p1); + if( rc==SQLITE_OK && iMoved!=0 ){ + sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1); /* All OP_Destroy operations occur on the same btree */ - assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.bw.iDb+1 ); - resetSchemaOnFault = u.bw.iDb+1; + assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 ); + resetSchemaOnFault = iDb+1; } #endif } @@ -71035,29 +71252,28 @@ case OP_Destroy: { /* out2-prerelease */ ** See also: Destroy */ case OP_Clear: { -#if 0 /* local variables moved into u.bx */ int nChange; -#endif /* local variables moved into u.bx */ - - u.bx.nChange = 0; + + nChange = 0; assert( p->readOnly==0 ); assert( pOp->p1!=1 ); assert( (p->btreeMask & (((yDbMask)1)<p2))!=0 ); rc = sqlite3BtreeClearTable( - db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bx.nChange : 0) + db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) ); if( pOp->p3 ){ - p->nChange += u.bx.nChange; + p->nChange += nChange; if( pOp->p3>0 ){ assert( memIsValid(&aMem[pOp->p3]) ); memAboutToChange(p, &aMem[pOp->p3]); - aMem[pOp->p3].u.i += u.bx.nChange; + aMem[pOp->p3].u.i += nChange; } } break; } /* Opcode: CreateTable P1 P2 * * * +** Synopsis: r[P2]=root iDb=P1 ** ** Allocate a new table in the main database file if P1==0 or in the ** auxiliary database file if P1==1 or in an attached database if @@ -71071,6 +71287,7 @@ case OP_Clear: { ** See also: CreateIndex */ /* Opcode: CreateIndex P1 P2 * * * +** Synopsis: r[P2]=root iDb=P1 ** ** Allocate a new index in the main database file if P1==0 or in the ** auxiliary database file if P1==1 or in an attached database if @@ -71081,26 +71298,24 @@ case OP_Clear: { */ case OP_CreateIndex: /* out2-prerelease */ case OP_CreateTable: { /* out2-prerelease */ -#if 0 /* local variables moved into u.by */ int pgno; int flags; Db *pDb; -#endif /* local variables moved into u.by */ - u.by.pgno = 0; + pgno = 0; assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); assert( p->readOnly==0 ); - u.by.pDb = &db->aDb[pOp->p1]; - assert( u.by.pDb->pBt!=0 ); + pDb = &db->aDb[pOp->p1]; + assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ - /* u.by.flags = BTREE_INTKEY; */ - u.by.flags = BTREE_INTKEY; + /* flags = BTREE_INTKEY; */ + flags = BTREE_INTKEY; }else{ - u.by.flags = BTREE_BLOBKEY; + flags = BTREE_BLOBKEY; } - rc = sqlite3BtreeCreateTable(u.by.pDb->pBt, &u.by.pgno, u.by.flags); - pOut->u.i = u.by.pgno; + rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags); + pOut->u.i = pgno; break; } @@ -71113,44 +71328,42 @@ case OP_CreateTable: { /* out2-prerelease */ ** then runs the new virtual machine. It is thus a re-entrant opcode. */ case OP_ParseSchema: { -#if 0 /* local variables moved into u.bz */ int iDb; const char *zMaster; char *zSql; InitData initData; -#endif /* local variables moved into u.bz */ /* Any prepared statement that invokes this opcode will hold mutexes - ** on every btree. This is a prerequisite for invoking + ** on every btree. This is a prerequisite for invoking ** sqlite3InitCallback(). */ #ifdef SQLITE_DEBUG - for(u.bz.iDb=0; u.bz.iDbnDb; u.bz.iDb++){ - assert( u.bz.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.bz.iDb].pBt) ); + for(iDb=0; iDbnDb; iDb++){ + assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); } #endif - u.bz.iDb = pOp->p1; - assert( u.bz.iDb>=0 && u.bz.iDbnDb ); - assert( DbHasProperty(db, u.bz.iDb, DB_SchemaLoaded) ); + iDb = pOp->p1; + assert( iDb>=0 && iDbnDb ); + assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); /* Used to be a conditional */ { - u.bz.zMaster = SCHEMA_TABLE(u.bz.iDb); - u.bz.initData.db = db; - u.bz.initData.iDb = pOp->p1; - u.bz.initData.pzErrMsg = &p->zErrMsg; - u.bz.zSql = sqlite3MPrintf(db, + zMaster = SCHEMA_TABLE(iDb); + initData.db = db; + initData.iDb = pOp->p1; + initData.pzErrMsg = &p->zErrMsg; + zSql = sqlite3MPrintf(db, "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", - db->aDb[u.bz.iDb].zName, u.bz.zMaster, pOp->p4.z); - if( u.bz.zSql==0 ){ + db->aDb[iDb].zName, zMaster, pOp->p4.z); + if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ assert( db->init.busy==0 ); db->init.busy = 1; - u.bz.initData.rc = SQLITE_OK; + initData.rc = SQLITE_OK; assert( !db->mallocFailed ); - rc = sqlite3_exec(db, u.bz.zSql, sqlite3InitCallback, &u.bz.initData, 0); - if( rc==SQLITE_OK ) rc = u.bz.initData.rc; - sqlite3DbFree(db, u.bz.zSql); + rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); + if( rc==SQLITE_OK ) rc = initData.rc; + sqlite3DbFree(db, zSql); db->init.busy = 0; } } @@ -71158,7 +71371,7 @@ case OP_ParseSchema: { if( rc==SQLITE_NOMEM ){ goto no_mem; } - break; + break; } #if !defined(SQLITE_OMIT_ANALYZE) @@ -71234,42 +71447,40 @@ case OP_DropTrigger: { ** This opcode is used to implement the integrity_check pragma. */ case OP_IntegrityCk: { -#if 0 /* local variables moved into u.ca */ int nRoot; /* Number of tables to check. (Number of root pages.) */ int *aRoot; /* Array of rootpage numbers for tables to be checked */ int j; /* Loop counter */ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ -#endif /* local variables moved into u.ca */ assert( p->bIsReader ); - u.ca.nRoot = pOp->p2; - assert( u.ca.nRoot>0 ); - u.ca.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.ca.nRoot+1) ); - if( u.ca.aRoot==0 ) goto no_mem; + nRoot = pOp->p2; + assert( nRoot>0 ); + aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) ); + if( aRoot==0 ) goto no_mem; assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); - u.ca.pnErr = &aMem[pOp->p3]; - assert( (u.ca.pnErr->flags & MEM_Int)!=0 ); - assert( (u.ca.pnErr->flags & (MEM_Str|MEM_Blob))==0 ); + pnErr = &aMem[pOp->p3]; + assert( (pnErr->flags & MEM_Int)!=0 ); + assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1]; - for(u.ca.j=0; u.ca.jp5nDb ); assert( (p->btreeMask & (((yDbMask)1)<p5))!=0 ); - u.ca.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.ca.aRoot, u.ca.nRoot, - (int)u.ca.pnErr->u.i, &u.ca.nErr); - sqlite3DbFree(db, u.ca.aRoot); - u.ca.pnErr->u.i -= u.ca.nErr; + z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot, + (int)pnErr->u.i, &nErr); + sqlite3DbFree(db, aRoot); + pnErr->u.i -= nErr; sqlite3VdbeMemSetNull(pIn1); - if( u.ca.nErr==0 ){ - assert( u.ca.z==0 ); - }else if( u.ca.z==0 ){ + if( nErr==0 ){ + assert( z==0 ); + }else if( z==0 ){ goto no_mem; }else{ - sqlite3VdbeMemSetStr(pIn1, u.ca.z, -1, SQLITE_UTF8, sqlite3_free); + sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free); } UPDATE_MAX_BLOBSIZE(pIn1); sqlite3VdbeChangeEncoding(pIn1, encoding); @@ -71278,6 +71489,7 @@ case OP_IntegrityCk: { #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* Opcode: RowSetAdd P1 P2 * * * +** Synopsis: rowset(P1)=r[P2] ** ** Insert the integer value held by register P2 into a boolean index ** held in register P1. @@ -71297,31 +71509,31 @@ case OP_RowSetAdd: { /* in1, in2 */ } /* Opcode: RowSetRead P1 P2 P3 * * +** Synopsis: r[P3]=rowset(P1) ** ** Extract the smallest value from boolean index P1 and put that value into ** register P3. Or, if boolean index P1 is initially empty, leave P3 ** unchanged and jump to instruction P2. */ case OP_RowSetRead: { /* jump, in1, out3 */ -#if 0 /* local variables moved into u.cb */ i64 val; -#endif /* local variables moved into u.cb */ pIn1 = &aMem[pOp->p1]; - if( (pIn1->flags & MEM_RowSet)==0 - || sqlite3RowSetNext(pIn1->u.pRowSet, &u.cb.val)==0 + if( (pIn1->flags & MEM_RowSet)==0 + || sqlite3RowSetNext(pIn1->u.pRowSet, &val)==0 ){ /* The boolean index is empty */ sqlite3VdbeMemSetNull(pIn1); pc = pOp->p2 - 1; }else{ /* A value was pulled from the index */ - sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.cb.val); + sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); } goto check_for_interrupt; } /* Opcode: RowSetTest P1 P2 P3 P4 +** Synopsis: if r[P3] in rowset(P1) goto P2 ** ** Register P3 is assumed to hold a 64-bit integer value. If register P1 ** contains a RowSet object and that RowSet object contains @@ -71345,14 +71557,12 @@ case OP_RowSetRead: { /* jump, in1, out3 */ ** inserted as part of some other set). */ case OP_RowSetTest: { /* jump, in1, in3 */ -#if 0 /* local variables moved into u.cc */ int iSet; int exists; -#endif /* local variables moved into u.cc */ pIn1 = &aMem[pOp->p1]; pIn3 = &aMem[pOp->p3]; - u.cc.iSet = pOp->p4.i; + iSet = pOp->p4.i; assert( pIn3->flags&MEM_Int ); /* If there is anything other than a rowset object in memory cell P1, @@ -71364,17 +71574,17 @@ case OP_RowSetTest: { /* jump, in1, in3 */ } assert( pOp->p4type==P4_INT32 ); - assert( u.cc.iSet==-1 || u.cc.iSet>=0 ); - if( u.cc.iSet ){ - u.cc.exists = sqlite3RowSetTest(pIn1->u.pRowSet, - (u8)(u.cc.iSet>=0 ? u.cc.iSet & 0xf : 0xff), + assert( iSet==-1 || iSet>=0 ); + if( iSet ){ + exists = sqlite3RowSetTest(pIn1->u.pRowSet, + (u8)(iSet>=0 ? iSet & 0xf : 0xff), pIn3->u.i); - if( u.cc.exists ){ + if( exists ){ pc = pOp->p2 - 1; break; } } - if( u.cc.iSet>=0 ){ + if( iSet>=0 ){ sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i); } break; @@ -71397,7 +71607,6 @@ case OP_RowSetTest: { /* jump, in1, in3 */ ** P4 is a pointer to the VM containing the trigger program. */ case OP_Program: { /* jump */ -#if 0 /* local variables moved into u.cd */ int nMem; /* Number of memory registers for sub-program */ int nByte; /* Bytes of runtime space required for sub-program */ Mem *pRt; /* Register to allocate runtime space */ @@ -71406,27 +71615,26 @@ case OP_Program: { /* jump */ VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ -#endif /* local variables moved into u.cd */ - u.cd.pProgram = pOp->p4.pProgram; - u.cd.pRt = &aMem[pOp->p3]; - assert( u.cd.pProgram->nOp>0 ); - - /* If the p5 flag is clear, then recursive invocation of triggers is + pProgram = pOp->p4.pProgram; + pRt = &aMem[pOp->p3]; + assert( pProgram->nOp>0 ); + + /* If the p5 flag is clear, then recursive invocation of triggers is ** disabled for backwards compatibility (p5 is set if this sub-program ** is really a trigger, not a foreign key action, and the flag set ** and cleared by the "PRAGMA recursive_triggers" command is clear). - ** - ** It is recursive invocation of triggers, at the SQL level, that is - ** disabled. In some cases a single trigger may generate more than one - ** SubProgram (if the trigger may be executed with more than one different + ** + ** It is recursive invocation of triggers, at the SQL level, that is + ** disabled. In some cases a single trigger may generate more than one + ** SubProgram (if the trigger may be executed with more than one different ** ON CONFLICT algorithm). SubProgram structures associated with a - ** single trigger all have the same value for the SubProgram.token + ** single trigger all have the same value for the SubProgram.token ** variable. */ if( pOp->p5 ){ - u.cd.t = u.cd.pProgram->token; - for(u.cd.pFrame=p->pFrame; u.cd.pFrame && u.cd.pFrame->token!=u.cd.t; u.cd.pFrame=u.cd.pFrame->pParent); - if( u.cd.pFrame ) break; + t = pProgram->token; + for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent); + if( pFrame ) break; } if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ @@ -71435,69 +71643,69 @@ case OP_Program: { /* jump */ break; } - /* Register u.cd.pRt is used to store the memory required to save the state + /* Register pRt is used to store the memory required to save the state ** of the current program, and the memory required at runtime to execute - ** the trigger program. If this trigger has been fired before, then u.cd.pRt + ** the trigger program. If this trigger has been fired before, then pRt ** is already allocated. Otherwise, it must be initialized. */ - if( (u.cd.pRt->flags&MEM_Frame)==0 ){ - /* SubProgram.nMem is set to the number of memory cells used by the + if( (pRt->flags&MEM_Frame)==0 ){ + /* SubProgram.nMem is set to the number of memory cells used by the ** program stored in SubProgram.aOp. As well as these, one memory ** cell is required for each cursor used by the program. Set local - ** variable u.cd.nMem (and later, VdbeFrame.nChildMem) to this value. + ** variable nMem (and later, VdbeFrame.nChildMem) to this value. */ - u.cd.nMem = u.cd.pProgram->nMem + u.cd.pProgram->nCsr; - u.cd.nByte = ROUND8(sizeof(VdbeFrame)) - + u.cd.nMem * sizeof(Mem) - + u.cd.pProgram->nCsr * sizeof(VdbeCursor *) - + u.cd.pProgram->nOnce * sizeof(u8); - u.cd.pFrame = sqlite3DbMallocZero(db, u.cd.nByte); - if( !u.cd.pFrame ){ + nMem = pProgram->nMem + pProgram->nCsr; + nByte = ROUND8(sizeof(VdbeFrame)) + + nMem * sizeof(Mem) + + pProgram->nCsr * sizeof(VdbeCursor *) + + pProgram->nOnce * sizeof(u8); + pFrame = sqlite3DbMallocZero(db, nByte); + if( !pFrame ){ goto no_mem; } - sqlite3VdbeMemRelease(u.cd.pRt); - u.cd.pRt->flags = MEM_Frame; - u.cd.pRt->u.pFrame = u.cd.pFrame; + sqlite3VdbeMemRelease(pRt); + pRt->flags = MEM_Frame; + pRt->u.pFrame = pFrame; - u.cd.pFrame->v = p; - u.cd.pFrame->nChildMem = u.cd.nMem; - u.cd.pFrame->nChildCsr = u.cd.pProgram->nCsr; - u.cd.pFrame->pc = pc; - u.cd.pFrame->aMem = p->aMem; - u.cd.pFrame->nMem = p->nMem; - u.cd.pFrame->apCsr = p->apCsr; - u.cd.pFrame->nCursor = p->nCursor; - u.cd.pFrame->aOp = p->aOp; - u.cd.pFrame->nOp = p->nOp; - u.cd.pFrame->token = u.cd.pProgram->token; - u.cd.pFrame->aOnceFlag = p->aOnceFlag; - u.cd.pFrame->nOnceFlag = p->nOnceFlag; + pFrame->v = p; + pFrame->nChildMem = nMem; + pFrame->nChildCsr = pProgram->nCsr; + pFrame->pc = pc; + pFrame->aMem = p->aMem; + pFrame->nMem = p->nMem; + pFrame->apCsr = p->apCsr; + pFrame->nCursor = p->nCursor; + pFrame->aOp = p->aOp; + pFrame->nOp = p->nOp; + pFrame->token = pProgram->token; + pFrame->aOnceFlag = p->aOnceFlag; + pFrame->nOnceFlag = p->nOnceFlag; - u.cd.pEnd = &VdbeFrameMem(u.cd.pFrame)[u.cd.pFrame->nChildMem]; - for(u.cd.pMem=VdbeFrameMem(u.cd.pFrame); u.cd.pMem!=u.cd.pEnd; u.cd.pMem++){ - u.cd.pMem->flags = MEM_Invalid; - u.cd.pMem->db = db; + pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem]; + for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){ + pMem->flags = MEM_Invalid; + pMem->db = db; } }else{ - u.cd.pFrame = u.cd.pRt->u.pFrame; - assert( u.cd.pProgram->nMem+u.cd.pProgram->nCsr==u.cd.pFrame->nChildMem ); - assert( u.cd.pProgram->nCsr==u.cd.pFrame->nChildCsr ); - assert( pc==u.cd.pFrame->pc ); + pFrame = pRt->u.pFrame; + assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem ); + assert( pProgram->nCsr==pFrame->nChildCsr ); + assert( pc==pFrame->pc ); } p->nFrame++; - u.cd.pFrame->pParent = p->pFrame; - u.cd.pFrame->lastRowid = lastRowid; - u.cd.pFrame->nChange = p->nChange; + pFrame->pParent = p->pFrame; + pFrame->lastRowid = lastRowid; + pFrame->nChange = p->nChange; p->nChange = 0; - p->pFrame = u.cd.pFrame; - p->aMem = aMem = &VdbeFrameMem(u.cd.pFrame)[-1]; - p->nMem = u.cd.pFrame->nChildMem; - p->nCursor = (u16)u.cd.pFrame->nChildCsr; + p->pFrame = pFrame; + p->aMem = aMem = &VdbeFrameMem(pFrame)[-1]; + p->nMem = pFrame->nChildMem; + p->nCursor = (u16)pFrame->nChildCsr; p->apCsr = (VdbeCursor **)&aMem[p->nMem+1]; - p->aOp = aOp = u.cd.pProgram->aOp; - p->nOp = u.cd.pProgram->nOp; + p->aOp = aOp = pProgram->aOp; + p->nOp = pProgram->nOp; p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor]; - p->nOnceFlag = u.cd.pProgram->nOnce; + p->nOnceFlag = pProgram->nOnce; pc = -1; memset(p->aOnceFlag, 0, p->nOnceFlag); @@ -71517,13 +71725,11 @@ case OP_Program: { /* jump */ ** calling OP_Program instruction. */ case OP_Param: { /* out2-prerelease */ -#if 0 /* local variables moved into u.ce */ VdbeFrame *pFrame; Mem *pIn; -#endif /* local variables moved into u.ce */ - u.ce.pFrame = p->pFrame; - u.ce.pIn = &u.ce.pFrame->aMem[pOp->p1 + u.ce.pFrame->aOp[u.ce.pFrame->pc].p1]; - sqlite3VdbeMemShallowCopy(pOut, u.ce.pIn, MEM_Ephem); + pFrame = p->pFrame; + pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; + sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); break; } @@ -71531,6 +71737,7 @@ case OP_Param: { /* out2-prerelease */ #ifndef SQLITE_OMIT_FOREIGN_KEY /* Opcode: FkCounter P1 P2 * * * +** Synopsis: fkctr[P1]+=P2 ** ** Increment a "constraint counter" by P2 (P2 may be negative or positive). ** If P1 is non-zero, the database constraint counter is incremented @@ -71549,6 +71756,7 @@ case OP_FkCounter: { } /* Opcode: FkIfZero P1 P2 * * * +** Synopsis: if fkctr[P1]==0 goto P2 ** ** This opcode tests if a foreign key constraint-counter is currently zero. ** If so, jump to instruction P2. Otherwise, fall through to the next @@ -71571,6 +71779,7 @@ case OP_FkIfZero: { /* jump */ #ifndef SQLITE_OMIT_AUTOINCREMENT /* Opcode: MemMax P1 P2 * * * +** Synopsis: r[P1]=max(r[P1],r[P2]) ** ** P1 is a register in the root frame of this VM (the root frame is ** different from the current frame if this instruction is being executed @@ -71581,28 +71790,26 @@ case OP_FkIfZero: { /* jump */ ** an integer. */ case OP_MemMax: { /* in2 */ -#if 0 /* local variables moved into u.cf */ - Mem *pIn1; VdbeFrame *pFrame; -#endif /* local variables moved into u.cf */ if( p->pFrame ){ - for(u.cf.pFrame=p->pFrame; u.cf.pFrame->pParent; u.cf.pFrame=u.cf.pFrame->pParent); - u.cf.pIn1 = &u.cf.pFrame->aMem[pOp->p1]; + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + pIn1 = &pFrame->aMem[pOp->p1]; }else{ - u.cf.pIn1 = &aMem[pOp->p1]; + pIn1 = &aMem[pOp->p1]; } - assert( memIsValid(u.cf.pIn1) ); - sqlite3VdbeMemIntegerify(u.cf.pIn1); + assert( memIsValid(pIn1) ); + sqlite3VdbeMemIntegerify(pIn1); pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); - if( u.cf.pIn1->u.iu.i){ - u.cf.pIn1->u.i = pIn2->u.i; + if( pIn1->u.iu.i){ + pIn1->u.i = pIn2->u.i; } break; } #endif /* SQLITE_OMIT_AUTOINCREMENT */ /* Opcode: IfPos P1 P2 * * * +** Synopsis: if r[P1]>0 goto P2 ** ** If the value of register P1 is 1 or greater, jump to P2. ** @@ -71619,6 +71826,7 @@ case OP_IfPos: { /* jump, in1 */ } /* Opcode: IfNeg P1 P2 * * * +** Synopsis: if r[P1]<0 goto P2 ** ** If the value of register P1 is less than zero, jump to P2. ** @@ -71635,6 +71843,7 @@ case OP_IfNeg: { /* jump, in1 */ } /* Opcode: IfZero P1 P2 P3 * * +** Synopsis: r[P1]+=P3, if r[P1]==0 goto P2 ** ** The register P1 must contain an integer. Add literal P3 to the ** value in register P1. If the result is exactly 0, jump to P2. @@ -71653,6 +71862,7 @@ case OP_IfZero: { /* jump, in1 */ } /* Opcode: AggStep * P2 P3 P4 P5 +** Synopsis: accum=r[P3] step(r[P2@P5]) ** ** Execute the step function for an aggregate. The ** function has P5 arguments. P4 is a pointer to the FuncDef @@ -71663,61 +71873,60 @@ case OP_IfZero: { /* jump, in1 */ ** successors. */ case OP_AggStep: { -#if 0 /* local variables moved into u.cg */ int n; int i; Mem *pMem; Mem *pRec; sqlite3_context ctx; sqlite3_value **apVal; -#endif /* local variables moved into u.cg */ - u.cg.n = pOp->p5; - assert( u.cg.n>=0 ); - u.cg.pRec = &aMem[pOp->p2]; - u.cg.apVal = p->apArg; - assert( u.cg.apVal || u.cg.n==0 ); - for(u.cg.i=0; u.cg.ip5; + assert( n>=0 ); + pRec = &aMem[pOp->p2]; + apVal = p->apArg; + assert( apVal || n==0 ); + for(i=0; ip4.pFunc; + ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); - u.cg.ctx.pMem = u.cg.pMem = &aMem[pOp->p3]; - u.cg.pMem->n++; - u.cg.ctx.s.flags = MEM_Null; - u.cg.ctx.s.z = 0; - u.cg.ctx.s.zMalloc = 0; - u.cg.ctx.s.xDel = 0; - u.cg.ctx.s.db = db; - u.cg.ctx.isError = 0; - u.cg.ctx.pColl = 0; - u.cg.ctx.skipFlag = 0; - if( u.cg.ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ + ctx.pMem = pMem = &aMem[pOp->p3]; + pMem->n++; + ctx.s.flags = MEM_Null; + ctx.s.z = 0; + ctx.s.zMalloc = 0; + ctx.s.xDel = 0; + ctx.s.db = db; + ctx.isError = 0; + ctx.pColl = 0; + ctx.skipFlag = 0; + if( ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>p->aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); - u.cg.ctx.pColl = pOp[-1].p4.pColl; + ctx.pColl = pOp[-1].p4.pColl; } - (u.cg.ctx.pFunc->xStep)(&u.cg.ctx, u.cg.n, u.cg.apVal); /* IMP: R-24505-23230 */ - if( u.cg.ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cg.ctx.s)); - rc = u.cg.ctx.isError; + (ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */ + if( ctx.isError ){ + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); + rc = ctx.isError; } - if( u.cg.ctx.skipFlag ){ + if( ctx.skipFlag ){ assert( pOp[-1].opcode==OP_CollSeq ); - u.cg.i = pOp[-1].p1; - if( u.cg.i ) sqlite3VdbeMemSetInt64(&aMem[u.cg.i], 1); + i = pOp[-1].p1; + if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1); } - sqlite3VdbeMemRelease(&u.cg.ctx.s); + sqlite3VdbeMemRelease(&ctx.s); break; } /* Opcode: AggFinal P1 P2 * P4 * +** Synopsis: accum=r[P1] N=P2 ** ** Execute the finalizer function for an aggregate. P1 is ** the memory location that is the accumulator for the aggregate. @@ -71730,19 +71939,17 @@ case OP_AggStep: { ** the step function was not previously called. */ case OP_AggFinal: { -#if 0 /* local variables moved into u.ch */ Mem *pMem; -#endif /* local variables moved into u.ch */ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) ); - u.ch.pMem = &aMem[pOp->p1]; - assert( (u.ch.pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); - rc = sqlite3VdbeMemFinalize(u.ch.pMem, pOp->p4.pFunc); + pMem = &aMem[pOp->p1]; + assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); + rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); if( rc ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.ch.pMem)); + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem)); } - sqlite3VdbeChangeEncoding(u.ch.pMem, encoding); - UPDATE_MAX_BLOBSIZE(u.ch.pMem); - if( sqlite3VdbeMemTooBig(u.ch.pMem) ){ + sqlite3VdbeChangeEncoding(pMem, encoding); + UPDATE_MAX_BLOBSIZE(pMem); + if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } break; @@ -71761,27 +71968,25 @@ case OP_AggFinal: { ** mem[P3+2] are initialized to -1. */ case OP_Checkpoint: { -#if 0 /* local variables moved into u.ci */ int i; /* Loop counter */ int aRes[3]; /* Results */ Mem *pMem; /* Write results here */ -#endif /* local variables moved into u.ci */ assert( p->readOnly==0 ); - u.ci.aRes[0] = 0; - u.ci.aRes[1] = u.ci.aRes[2] = -1; + aRes[0] = 0; + aRes[1] = aRes[2] = -1; assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE || pOp->p2==SQLITE_CHECKPOINT_FULL || pOp->p2==SQLITE_CHECKPOINT_RESTART ); - rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.ci.aRes[1], &u.ci.aRes[2]); + rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]); if( rc==SQLITE_BUSY ){ rc = SQLITE_OK; - u.ci.aRes[0] = 1; - } - for(u.ci.i=0, u.ci.pMem = &aMem[pOp->p3]; u.ci.i<3; u.ci.i++, u.ci.pMem++){ - sqlite3VdbeMemSetInt64(u.ci.pMem, (i64)u.ci.aRes[u.ci.i]); + aRes[0] = 1; } + for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){ + sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]); + } break; }; #endif @@ -71799,7 +72004,6 @@ case OP_Checkpoint: { ** Write a string containing the final journal-mode to register P2. */ case OP_JournalMode: { /* out2-prerelease */ -#if 0 /* local variables moved into u.cj */ Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ @@ -71807,86 +72011,85 @@ case OP_JournalMode: { /* out2-prerelease */ #ifndef SQLITE_OMIT_WAL const char *zFilename; /* Name of database file for pPager */ #endif -#endif /* local variables moved into u.cj */ - u.cj.eNew = pOp->p3; - assert( u.cj.eNew==PAGER_JOURNALMODE_DELETE - || u.cj.eNew==PAGER_JOURNALMODE_TRUNCATE - || u.cj.eNew==PAGER_JOURNALMODE_PERSIST - || u.cj.eNew==PAGER_JOURNALMODE_OFF - || u.cj.eNew==PAGER_JOURNALMODE_MEMORY - || u.cj.eNew==PAGER_JOURNALMODE_WAL - || u.cj.eNew==PAGER_JOURNALMODE_QUERY + eNew = pOp->p3; + assert( eNew==PAGER_JOURNALMODE_DELETE + || eNew==PAGER_JOURNALMODE_TRUNCATE + || eNew==PAGER_JOURNALMODE_PERSIST + || eNew==PAGER_JOURNALMODE_OFF + || eNew==PAGER_JOURNALMODE_MEMORY + || eNew==PAGER_JOURNALMODE_WAL + || eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1nDb ); assert( p->readOnly==0 ); - u.cj.pBt = db->aDb[pOp->p1].pBt; - u.cj.pPager = sqlite3BtreePager(u.cj.pBt); - u.cj.eOld = sqlite3PagerGetJournalMode(u.cj.pPager); - if( u.cj.eNew==PAGER_JOURNALMODE_QUERY ) u.cj.eNew = u.cj.eOld; - if( !sqlite3PagerOkToChangeJournalMode(u.cj.pPager) ) u.cj.eNew = u.cj.eOld; + pBt = db->aDb[pOp->p1].pBt; + pPager = sqlite3BtreePager(pBt); + eOld = sqlite3PagerGetJournalMode(pPager); + if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; + if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; #ifndef SQLITE_OMIT_WAL - u.cj.zFilename = sqlite3PagerFilename(u.cj.pPager, 1); + zFilename = sqlite3PagerFilename(pPager, 1); /* Do not allow a transition to journal_mode=WAL for a database - ** in temporary storage or if the VFS does not support shared memory + ** in temporary storage or if the VFS does not support shared memory */ - if( u.cj.eNew==PAGER_JOURNALMODE_WAL - && (sqlite3Strlen30(u.cj.zFilename)==0 /* Temp file */ - || !sqlite3PagerWalSupported(u.cj.pPager)) /* No shared-memory support */ + if( eNew==PAGER_JOURNALMODE_WAL + && (sqlite3Strlen30(zFilename)==0 /* Temp file */ + || !sqlite3PagerWalSupported(pPager)) /* No shared-memory support */ ){ - u.cj.eNew = u.cj.eOld; + eNew = eOld; } - if( (u.cj.eNew!=u.cj.eOld) - && (u.cj.eOld==PAGER_JOURNALMODE_WAL || u.cj.eNew==PAGER_JOURNALMODE_WAL) + if( (eNew!=eOld) + && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL) ){ if( !db->autoCommit || db->nVdbeRead>1 ){ rc = SQLITE_ERROR; - sqlite3SetString(&p->zErrMsg, db, + sqlite3SetString(&p->zErrMsg, db, "cannot change %s wal mode from within a transaction", - (u.cj.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") + (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") ); break; }else{ - - if( u.cj.eOld==PAGER_JOURNALMODE_WAL ){ + + if( eOld==PAGER_JOURNALMODE_WAL ){ /* If leaving WAL mode, close the log file. If successful, the call - ** to PagerCloseWal() checkpoints and deletes the write-ahead-log - ** file. An EXCLUSIVE lock may still be held on the database file - ** after a successful return. + ** to PagerCloseWal() checkpoints and deletes the write-ahead-log + ** file. An EXCLUSIVE lock may still be held on the database file + ** after a successful return. */ - rc = sqlite3PagerCloseWal(u.cj.pPager); + rc = sqlite3PagerCloseWal(pPager); if( rc==SQLITE_OK ){ - sqlite3PagerSetJournalMode(u.cj.pPager, u.cj.eNew); + sqlite3PagerSetJournalMode(pPager, eNew); } - }else if( u.cj.eOld==PAGER_JOURNALMODE_MEMORY ){ + }else if( eOld==PAGER_JOURNALMODE_MEMORY ){ /* Cannot transition directly from MEMORY to WAL. Use mode OFF ** as an intermediate */ - sqlite3PagerSetJournalMode(u.cj.pPager, PAGER_JOURNALMODE_OFF); + sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF); } - + /* Open a transaction on the database file. Regardless of the journal ** mode, this transaction always uses a rollback journal. */ - assert( sqlite3BtreeIsInTrans(u.cj.pBt)==0 ); + assert( sqlite3BtreeIsInTrans(pBt)==0 ); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeSetVersion(u.cj.pBt, (u.cj.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); + rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); } } } #endif /* ifndef SQLITE_OMIT_WAL */ if( rc ){ - u.cj.eNew = u.cj.eOld; + eNew = eOld; } - u.cj.eNew = sqlite3PagerSetJournalMode(u.cj.pPager, u.cj.eNew); + eNew = sqlite3PagerSetJournalMode(pPager, eNew); pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = (char *)sqlite3JournalModename(u.cj.eNew); + pOut->z = (char *)sqlite3JournalModename(eNew); pOut->n = sqlite3Strlen30(pOut->z); pOut->enc = SQLITE_UTF8; sqlite3VdbeChangeEncoding(pOut, encoding); @@ -71916,15 +72119,13 @@ case OP_Vacuum: { ** P2. Otherwise, fall through to the next instruction. */ case OP_IncrVacuum: { /* jump */ -#if 0 /* local variables moved into u.ck */ Btree *pBt; -#endif /* local variables moved into u.ck */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); assert( p->readOnly==0 ); - u.ck.pBt = db->aDb[pOp->p1].pBt; - rc = sqlite3BtreeIncrVacuum(u.ck.pBt); + pBt = db->aDb[pOp->p1].pBt; + rc = sqlite3BtreeIncrVacuum(pBt); if( rc==SQLITE_DONE ){ pc = pOp->p2 - 1; rc = SQLITE_OK; @@ -71953,6 +72154,7 @@ case OP_Expire: { #ifndef SQLITE_OMIT_SHARED_CACHE /* Opcode: TableLock P1 P2 P3 P4 * +** Synopsis: iDb=P1 root=P2 write=P3 ** ** Obtain a lock on a particular table. This instruction is only used when ** the shared-cache feature is enabled. @@ -71994,12 +72196,10 @@ case OP_TableLock: { ** code will be set to SQLITE_LOCKED. */ case OP_VBegin: { -#if 0 /* local variables moved into u.cl */ VTable *pVTab; -#endif /* local variables moved into u.cl */ - u.cl.pVTab = pOp->p4.pVtab; - rc = sqlite3VtabBegin(db, u.cl.pVTab); - if( u.cl.pVTab ) sqlite3VtabImportErrmsg(p, u.cl.pVTab->pVtab); + pVTab = pOp->p4.pVtab; + rc = sqlite3VtabBegin(db, pVTab); + if( pVTab ) sqlite3VtabImportErrmsg(p, pVTab->pVtab); break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -72038,33 +72238,30 @@ case OP_VDestroy: { ** table and stores that cursor in P1. */ case OP_VOpen: { -#if 0 /* local variables moved into u.cm */ VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; sqlite3_module *pModule; -#endif /* local variables moved into u.cm */ assert( p->bIsReader ); - u.cm.pCur = 0; - u.cm.pVtabCursor = 0; - u.cm.pVtab = pOp->p4.pVtab->pVtab; - u.cm.pModule = (sqlite3_module *)u.cm.pVtab->pModule; - assert(u.cm.pVtab && u.cm.pModule); - rc = u.cm.pModule->xOpen(u.cm.pVtab, &u.cm.pVtabCursor); - sqlite3VtabImportErrmsg(p, u.cm.pVtab); + pCur = 0; + pVtabCursor = 0; + pVtab = pOp->p4.pVtab->pVtab; + pModule = (sqlite3_module *)pVtab->pModule; + assert(pVtab && pModule); + rc = pModule->xOpen(pVtab, &pVtabCursor); + sqlite3VtabImportErrmsg(p, pVtab); if( SQLITE_OK==rc ){ /* Initialize sqlite3_vtab_cursor base class */ - u.cm.pVtabCursor->pVtab = u.cm.pVtab; + pVtabCursor->pVtab = pVtab; /* Initialize vdbe cursor object */ - u.cm.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); - if( u.cm.pCur ){ - u.cm.pCur->pVtabCursor = u.cm.pVtabCursor; - u.cm.pCur->pModule = u.cm.pVtabCursor->pVtab->pModule; + pCur = allocateCursor(p, pOp->p1, 0, -1, 0); + if( pCur ){ + pCur->pVtabCursor = pVtabCursor; }else{ db->mallocFailed = 1; - u.cm.pModule->xClose(u.cm.pVtabCursor); + pModule->xClose(pVtabCursor); } } break; @@ -72073,6 +72270,7 @@ case OP_VOpen: { #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VFilter P1 P2 P3 P4 * +** Synopsis: iPlan=r[P3] zPlan='P4' ** ** P1 is a cursor opened using VOpen. P2 is an address to jump to if ** the filtered result set is empty. @@ -72091,7 +72289,6 @@ case OP_VOpen: { ** A jump is made to P2 if the result set after filtering would be empty. */ case OP_VFilter: { /* jump */ -#if 0 /* local variables moved into u.cn */ int nArg; int iQuery; const sqlite3_module *pModule; @@ -72103,45 +72300,44 @@ case OP_VFilter: { /* jump */ int res; int i; Mem **apArg; -#endif /* local variables moved into u.cn */ - u.cn.pQuery = &aMem[pOp->p3]; - u.cn.pArgc = &u.cn.pQuery[1]; - u.cn.pCur = p->apCsr[pOp->p1]; - assert( memIsValid(u.cn.pQuery) ); - REGISTER_TRACE(pOp->p3, u.cn.pQuery); - assert( u.cn.pCur->pVtabCursor ); - u.cn.pVtabCursor = u.cn.pCur->pVtabCursor; - u.cn.pVtab = u.cn.pVtabCursor->pVtab; - u.cn.pModule = u.cn.pVtab->pModule; + pQuery = &aMem[pOp->p3]; + pArgc = &pQuery[1]; + pCur = p->apCsr[pOp->p1]; + assert( memIsValid(pQuery) ); + REGISTER_TRACE(pOp->p3, pQuery); + assert( pCur->pVtabCursor ); + pVtabCursor = pCur->pVtabCursor; + pVtab = pVtabCursor->pVtab; + pModule = pVtab->pModule; /* Grab the index number and argc parameters */ - assert( (u.cn.pQuery->flags&MEM_Int)!=0 && u.cn.pArgc->flags==MEM_Int ); - u.cn.nArg = (int)u.cn.pArgc->u.i; - u.cn.iQuery = (int)u.cn.pQuery->u.i; + assert( (pQuery->flags&MEM_Int)!=0 && pArgc->flags==MEM_Int ); + nArg = (int)pArgc->u.i; + iQuery = (int)pQuery->u.i; /* Invoke the xFilter method */ { - u.cn.res = 0; - u.cn.apArg = p->apArg; - for(u.cn.i = 0; u.cn.iapArg; + for(i = 0; iinVtabMethod = 1; - rc = u.cn.pModule->xFilter(u.cn.pVtabCursor, u.cn.iQuery, pOp->p4.z, u.cn.nArg, u.cn.apArg); + rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); p->inVtabMethod = 0; - sqlite3VtabImportErrmsg(p, u.cn.pVtab); + sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK ){ - u.cn.res = u.cn.pModule->xEof(u.cn.pVtabCursor); + res = pModule->xEof(pVtabCursor); } - if( u.cn.res ){ + if( res ){ pc = pOp->p2 - 1; } } - u.cn.pCur->nullRow = 0; + pCur->nullRow = 0; break; } @@ -72149,57 +72345,56 @@ case OP_VFilter: { /* jump */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VColumn P1 P2 P3 * * +** Synopsis: r[P3]=vcolumn(P2) ** ** Store the value of the P2-th column of ** the row of the virtual-table that the ** P1 cursor is pointing to into register P3. */ case OP_VColumn: { -#if 0 /* local variables moved into u.co */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; -#endif /* local variables moved into u.co */ VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); - u.co.pDest = &aMem[pOp->p3]; - memAboutToChange(p, u.co.pDest); + pDest = &aMem[pOp->p3]; + memAboutToChange(p, pDest); if( pCur->nullRow ){ - sqlite3VdbeMemSetNull(u.co.pDest); + sqlite3VdbeMemSetNull(pDest); break; } - u.co.pVtab = pCur->pVtabCursor->pVtab; - u.co.pModule = u.co.pVtab->pModule; - assert( u.co.pModule->xColumn ); - memset(&u.co.sContext, 0, sizeof(u.co.sContext)); + pVtab = pCur->pVtabCursor->pVtab; + pModule = pVtab->pModule; + assert( pModule->xColumn ); + memset(&sContext, 0, sizeof(sContext)); /* The output cell may already have a buffer allocated. Move - ** the current contents to u.co.sContext.s so in case the user-function - ** can use the already allocated buffer instead of allocating a + ** the current contents to sContext.s so in case the user-function + ** can use the already allocated buffer instead of allocating a ** new one. */ - sqlite3VdbeMemMove(&u.co.sContext.s, u.co.pDest); - MemSetTypeFlag(&u.co.sContext.s, MEM_Null); + sqlite3VdbeMemMove(&sContext.s, pDest); + MemSetTypeFlag(&sContext.s, MEM_Null); - rc = u.co.pModule->xColumn(pCur->pVtabCursor, &u.co.sContext, pOp->p2); - sqlite3VtabImportErrmsg(p, u.co.pVtab); - if( u.co.sContext.isError ){ - rc = u.co.sContext.isError; + rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2); + sqlite3VtabImportErrmsg(p, pVtab); + if( sContext.isError ){ + rc = sContext.isError; } /* Copy the result of the function to the P3 register. We ** do this regardless of whether or not an error occurred to ensure any - ** dynamic allocation in u.co.sContext.s (a Mem struct) is released. + ** dynamic allocation in sContext.s (a Mem struct) is released. */ - sqlite3VdbeChangeEncoding(&u.co.sContext.s, encoding); - sqlite3VdbeMemMove(u.co.pDest, &u.co.sContext.s); - REGISTER_TRACE(pOp->p3, u.co.pDest); - UPDATE_MAX_BLOBSIZE(u.co.pDest); + sqlite3VdbeChangeEncoding(&sContext.s, encoding); + sqlite3VdbeMemMove(pDest, &sContext.s); + REGISTER_TRACE(pOp->p3, pDest); + UPDATE_MAX_BLOBSIZE(pDest); - if( sqlite3VdbeMemTooBig(u.co.pDest) ){ + if( sqlite3VdbeMemTooBig(pDest) ){ goto too_big; } break; @@ -72214,38 +72409,36 @@ case OP_VColumn: { ** the end of its result set, then fall through to the next instruction. */ case OP_VNext: { /* jump */ -#if 0 /* local variables moved into u.cp */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res; VdbeCursor *pCur; -#endif /* local variables moved into u.cp */ - u.cp.res = 0; - u.cp.pCur = p->apCsr[pOp->p1]; - assert( u.cp.pCur->pVtabCursor ); - if( u.cp.pCur->nullRow ){ + res = 0; + pCur = p->apCsr[pOp->p1]; + assert( pCur->pVtabCursor ); + if( pCur->nullRow ){ break; } - u.cp.pVtab = u.cp.pCur->pVtabCursor->pVtab; - u.cp.pModule = u.cp.pVtab->pModule; - assert( u.cp.pModule->xNext ); + pVtab = pCur->pVtabCursor->pVtab; + pModule = pVtab->pModule; + assert( pModule->xNext ); /* Invoke the xNext() method of the module. There is no way for the ** underlying implementation to return an error if one occurs during - ** xNext(). Instead, if an error occurs, true is returned (indicating that + ** xNext(). Instead, if an error occurs, true is returned (indicating that ** data is available) and the error code returned when xColumn or ** some other method is next invoked on the save virtual table cursor. */ p->inVtabMethod = 1; - rc = u.cp.pModule->xNext(u.cp.pCur->pVtabCursor); + rc = pModule->xNext(pCur->pVtabCursor); p->inVtabMethod = 0; - sqlite3VtabImportErrmsg(p, u.cp.pVtab); + sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK ){ - u.cp.res = u.cp.pModule->xEof(u.cp.pCur->pVtabCursor); + res = pModule->xEof(pCur->pVtabCursor); } - if( !u.cp.res ){ + if( !res ){ /* If there is data, jump to P2 */ pc = pOp->p2 - 1; } @@ -72261,25 +72454,23 @@ case OP_VNext: { /* jump */ ** in register P1 is passed as the zName argument to the xRename method. */ case OP_VRename: { -#if 0 /* local variables moved into u.cq */ sqlite3_vtab *pVtab; Mem *pName; -#endif /* local variables moved into u.cq */ - u.cq.pVtab = pOp->p4.pVtab->pVtab; - u.cq.pName = &aMem[pOp->p1]; - assert( u.cq.pVtab->pModule->xRename ); - assert( memIsValid(u.cq.pName) ); + pVtab = pOp->p4.pVtab->pVtab; + pName = &aMem[pOp->p1]; + assert( pVtab->pModule->xRename ); + assert( memIsValid(pName) ); assert( p->readOnly==0 ); - REGISTER_TRACE(pOp->p1, u.cq.pName); - assert( u.cq.pName->flags & MEM_Str ); - testcase( u.cq.pName->enc==SQLITE_UTF8 ); - testcase( u.cq.pName->enc==SQLITE_UTF16BE ); - testcase( u.cq.pName->enc==SQLITE_UTF16LE ); - rc = sqlite3VdbeChangeEncoding(u.cq.pName, SQLITE_UTF8); + REGISTER_TRACE(pOp->p1, pName); + assert( pName->flags & MEM_Str ); + testcase( pName->enc==SQLITE_UTF8 ); + testcase( pName->enc==SQLITE_UTF16BE ); + testcase( pName->enc==SQLITE_UTF16LE ); + rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8); if( rc==SQLITE_OK ){ - rc = u.cq.pVtab->pModule->xRename(u.cq.pVtab, u.cq.pName->z); - sqlite3VtabImportErrmsg(p, u.cq.pVtab); + rc = pVtab->pModule->xRename(pVtab, pName->z); + sqlite3VtabImportErrmsg(p, pVtab); p->expired = 0; } break; @@ -72288,6 +72479,7 @@ case OP_VRename: { #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VUpdate P1 P2 P3 P4 * +** Synopsis: data=r[P3@P2] ** ** P4 is a pointer to a virtual table object, an sqlite3_vtab structure. ** This opcode invokes the corresponding xUpdate method. P2 values @@ -72311,7 +72503,6 @@ case OP_VRename: { ** is set to the value of the rowid for the row just inserted. */ case OP_VUpdate: { -#if 0 /* local variables moved into u.cr */ sqlite3_vtab *pVtab; sqlite3_module *pModule; int nArg; @@ -72319,34 +72510,33 @@ case OP_VUpdate: { sqlite_int64 rowid; Mem **apArg; Mem *pX; -#endif /* local variables moved into u.cr */ - assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback + assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace ); assert( p->readOnly==0 ); - u.cr.pVtab = pOp->p4.pVtab->pVtab; - u.cr.pModule = (sqlite3_module *)u.cr.pVtab->pModule; - u.cr.nArg = pOp->p2; + pVtab = pOp->p4.pVtab->pVtab; + pModule = (sqlite3_module *)pVtab->pModule; + nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); - if( ALWAYS(u.cr.pModule->xUpdate) ){ + if( ALWAYS(pModule->xUpdate) ){ u8 vtabOnConflict = db->vtabOnConflict; - u.cr.apArg = p->apArg; - u.cr.pX = &aMem[pOp->p3]; - for(u.cr.i=0; u.cr.iapArg; + pX = &aMem[pOp->p3]; + for(i=0; ivtabOnConflict = pOp->p5; - rc = u.cr.pModule->xUpdate(u.cr.pVtab, u.cr.nArg, u.cr.apArg, &u.cr.rowid); + rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); db->vtabOnConflict = vtabOnConflict; - sqlite3VtabImportErrmsg(p, u.cr.pVtab); + sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK && pOp->p1 ){ - assert( u.cr.nArg>1 && u.cr.apArg[0] && (u.cr.apArg[0]->flags&MEM_Null) ); - db->lastRowid = lastRowid = u.cr.rowid; + assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); + db->lastRowid = lastRowid = rowid; } if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ if( pOp->p5==OE_Ignore ){ @@ -72406,24 +72596,32 @@ case OP_MaxPgcnt: { /* out2-prerelease */ ** the UTF-8 string contained in P4 is emitted on the trace callback. */ case OP_Trace: { -#if 0 /* local variables moved into u.cs */ char *zTrace; char *z; -#endif /* local variables moved into u.cs */ if( db->xTrace && !p->doingRerun - && (u.cs.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 + && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ - u.cs.z = sqlite3VdbeExpandSql(p, u.cs.zTrace); - db->xTrace(db->pTraceArg, u.cs.z); - sqlite3DbFree(db, u.cs.z); + z = sqlite3VdbeExpandSql(p, zTrace); + db->xTrace(db->pTraceArg, z); + sqlite3DbFree(db, z); } +#ifdef SQLITE_USE_FCNTL_TRACE + zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); + if( zTrace ){ + int i; + for(i=0; inDb; i++){ + if( MASKBIT(i) & p->btreeMask)==0 ) continue; + sqlite3_file_control(db, db->aDb[i].zName, SQLITE_FCNTL_TRACE, zTrace); + } + } +#endif /* SQLITE_USE_FCNTL_TRACE */ #ifdef SQLITE_DEBUG if( (db->flags & SQLITE_SqlTrace)!=0 - && (u.cs.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 + && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ - sqlite3DebugPrintf("SQL-trace: %s\n", u.cs.zTrace); + sqlite3DebugPrintf("SQL-trace: %s\n", zTrace); } #endif /* SQLITE_DEBUG */ break; @@ -72476,13 +72674,13 @@ default: { /* This is really OP_Noop and OP_Explain */ assert( pc>=-1 && pcnOp ); #ifdef SQLITE_DEBUG - if( p->trace ){ - if( rc!=0 ) fprintf(p->trace,"rc=%d\n",rc); + if( db->flags & SQLITE_VdbeTrace ){ + if( rc!=0 ) printf("rc=%d\n",rc); if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){ - registerTrace(p->trace, pOp->p2, &aMem[pOp->p2]); + registerTrace(pOp->p2, &aMem[pOp->p2]); } if( pOp->opflags & OPFLG_OUT3 ){ - registerTrace(p->trace, pOp->p3, &aMem[pOp->p3]); + registerTrace(pOp->p3, &aMem[pOp->p3]); } } #endif /* SQLITE_DEBUG */ @@ -72553,6 +72751,7 @@ abort_due_to_interrupt: goto vdbe_error_halt; } + /************** End of vdbe.c ************************************************/ /************** Begin file vdbeblob.c ****************************************/ /* @@ -72619,7 +72818,8 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ rc = sqlite3_step(p->pStmt); if( rc==SQLITE_ROW ){ - u32 type = v->apCsr[0]->aType[p->iCol]; + VdbeCursor *pC = v->apCsr[0]; + u32 type = pC->aType[p->iCol]; if( type<12 ){ zErr = sqlite3MPrintf(p->db, "cannot open value of type %s", type==0?"null": type==7?"real": "integer" @@ -72628,9 +72828,9 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ sqlite3_finalize(p->pStmt); p->pStmt = 0; }else{ - p->iOffset = v->apCsr[0]->aOffset[p->iCol]; + p->iOffset = pC->aType[p->iCol + pC->nField]; p->nByte = sqlite3VdbeSerialTypeLen(type); - p->pCsr = v->apCsr[0]->pCursor; + p->pCsr = pC->pCursor; sqlite3BtreeEnterCursor(p->pCsr); sqlite3BtreeCacheOverflow(p->pCsr); sqlite3BtreeLeaveCursor(p->pCsr); @@ -72733,6 +72933,10 @@ SQLITE_API int sqlite3_blob_open( pTab = 0; sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable); } + if( pTab && !HasRowid(pTab) ){ + pTab = 0; + sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable); + } #ifndef SQLITE_OMIT_VIEW if( pTab && pTab->pSelect ){ pTab = 0; @@ -72790,7 +72994,7 @@ SQLITE_API int sqlite3_blob_open( #endif for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int j; - for(j=0; jnColumn; j++){ + for(j=0; jnKeyCol; j++){ if( pIdx->aiColumn[j]==iCol ){ zFault = "indexed"; } @@ -72805,7 +73009,7 @@ SQLITE_API int sqlite3_blob_open( } } - pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db); + pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse); assert( pBlob->pStmt || db->mallocFailed ); if( pBlob->pStmt ){ Vdbe *v = (Vdbe *)pBlob->pStmt; @@ -72879,6 +73083,7 @@ blob_open_out: } sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); + sqlite3ParserReset(pParse); sqlite3StackFree(db, pParse); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); @@ -73411,7 +73616,7 @@ static int vdbeSorterIterInit( */ static void vdbeSorterCompare( const VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */ - int bOmitRowid, /* Ignore rowid field at end of keys */ + int nIgnore, /* Ignore the last nIgnore fields */ const void *pKey1, int nKey1, /* Left side of comparison */ const void *pKey2, int nKey2, /* Right side of comparison */ int *pRes /* OUT: Result of comparison */ @@ -73425,8 +73630,8 @@ static void vdbeSorterCompare( sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2); } - if( bOmitRowid ){ - r2->nField = pKeyInfo->nField; + if( nIgnore ){ + r2->nField = pKeyInfo->nField - nIgnore; assert( r2->nField>0 ); for(i=0; inField; i++){ if( r2->aMem[i].flags & MEM_Null ){ @@ -74052,13 +74257,14 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){ SQLITE_PRIVATE int sqlite3VdbeSorterCompare( const VdbeCursor *pCsr, /* Sorter cursor */ Mem *pVal, /* Value to compare to current sorter key */ + int nIgnore, /* Ignore this many fields at the end */ int *pRes /* OUT: Result of comparison */ ){ VdbeSorter *pSorter = pCsr->pSorter; void *pKey; int nKey; /* Sorter key to compare pVal with */ pKey = vdbeSorterRowkey(pSorter, &nKey); - vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes); + vdbeSorterCompare(pCsr, nIgnore, pVal->z, pVal->n, pKey, nKey, pRes); return SQLITE_OK; } @@ -74693,9 +74899,12 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ /* ** Call sqlite3WalkExpr() for every expression in Select statement p. ** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and -** on the compound select chain, p->pPrior. Invoke the xSelectCallback() -** either before or after the walk of expressions and FROM clause, depending -** on whether pWalker->bSelectDepthFirst is false or true, respectively. +** on the compound select chain, p->pPrior. +** +** If it is not NULL, the xSelectCallback() callback is invoked before +** the walk of the expressions and FROM clause. The xSelectCallback2() +** method, if it is not NULL, is invoked following the walk of the +** expressions and FROM clause. ** ** Return WRC_Continue under normal conditions. Return WRC_Abort if ** there is an abort request. @@ -74705,11 +74914,13 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ */ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){ int rc; - if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue; + if( p==0 || (pWalker->xSelectCallback==0 && pWalker->xSelectCallback2==0) ){ + return WRC_Continue; + } rc = WRC_Continue; pWalker->walkerDepth++; while( p ){ - if( !pWalker->bSelectDepthFirst ){ + if( pWalker->xSelectCallback ){ rc = pWalker->xSelectCallback(pWalker, p); if( rc ) break; } @@ -74719,12 +74930,8 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){ pWalker->walkerDepth--; return WRC_Abort; } - if( pWalker->bSelectDepthFirst ){ - rc = pWalker->xSelectCallback(pWalker, p); - /* Depth-first search is currently only used for - ** selectAddSubqueryTypeInfo() and that routine always returns - ** WRC_Continue (0). So the following branch is never taken. */ - if( NEVER(rc) ) break; + if( pWalker->xSelectCallback2 ){ + pWalker->xSelectCallback2(pWalker, p); } p = p->pPrior; } @@ -74843,10 +75050,10 @@ static void resolveAlias( pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0); if( pDup==0 ) return; ExprSetProperty(pDup, EP_Skip); - if( pEList->a[iCol].iAlias==0 ){ - pEList->a[iCol].iAlias = (u16)(++pParse->nAlias); + if( pEList->a[iCol].u.x.iAlias==0 ){ + pEList->a[iCol].u.x.iAlias = (u16)(++pParse->nAlias); } - pDup->iTable = pEList->a[iCol].iAlias; + pDup->iTable = pEList->a[iCol].u.x.iAlias; } if( pExpr->op==TK_COLLATE ){ pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); @@ -74961,7 +75168,9 @@ static int lookupName( struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ - int isTrigger = 0; + int isTrigger = 0; /* True if resolved to a trigger column */ + Table *pTab = 0; /* Table hold the row */ + Column *pCol; /* A column of pTab */ assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ @@ -75002,9 +75211,6 @@ static int lookupName( if( pSrcList ){ for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ - Table *pTab; - Column *pCol; - pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); assert( pTab->nCol>0 ); @@ -75064,9 +75270,8 @@ static int lookupName( /* If we have not already resolved the name, then maybe ** it is a new.* or old.* trigger argument reference */ - if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){ + if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){ int op = pParse->eTriggerOp; - Table *pTab = 0; assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ pExpr->iTable = 1; @@ -75080,8 +75285,7 @@ static int lookupName( int iCol; pSchema = pTab->pSchema; cntTab++; - for(iCol=0; iColnCol; iCol++){ - Column *pCol = &pTab->aCol[iCol]; + for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ if( iCol==pTab->iPKey ){ iCol = -1; @@ -75089,8 +75293,10 @@ static int lookupName( break; } } - if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) ){ - iCol = -1; /* IMP: R-44911-55124 */ + if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && HasRowid(pTab) ){ + /* IMP: R-24309-18625 */ + /* IMP: R-44911-55124 */ + iCol = -1; } if( iColnCol ){ cnt++; @@ -75116,7 +75322,8 @@ static int lookupName( /* ** Perhaps the name is a reference to the ROWID */ - if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){ + assert( pTab!=0 || cntTab==0 ); + if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) && HasRowid(pTab) ){ cnt = 1; pExpr->iColumn = -1; /* IMP: R-44911-55124 */ pExpr->affinity = SQLITE_AFF_INTEGER; @@ -75404,7 +75611,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ /* Resolve function names */ - case TK_CONST_FUNC: case TK_FUNCTION: { ExprList *pList = pExpr->x.pList; /* The argument list */ int n = pList ? pList->nExpr : 0; /* Number of arguments */ @@ -75417,7 +75623,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ - testcase( pExpr->op==TK_CONST_FUNC ); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); notValidPartIdxWhere(pParse, pNC, "functions"); zId = pExpr->u.zToken; @@ -75462,6 +75667,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pExpr->op = TK_NULL; return WRC_Prune; } + if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ) ExprSetProperty(pExpr,EP_Constant); } #endif if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ @@ -75713,7 +75919,7 @@ static int resolveCompoundOrderBy( pItem->pExpr->pLeft = pNew; } sqlite3ExprDelete(db, pE); - pItem->iOrderByCol = (u16)iCol; + pItem->u.x.iOrderByCol = (u16)iCol; pItem->done = 1; }else{ moreToDo = 1; @@ -75734,8 +75940,8 @@ static int resolveCompoundOrderBy( /* ** Check every term in the ORDER BY or GROUP BY clause pOrderBy of ** the SELECT statement pSelect. If any term is reference to a -** result set expression (as determined by the ExprList.a.iOrderByCol field) -** then convert that term into a copy of the corresponding result set +** result set expression (as determined by the ExprList.a.u.x.iOrderByCol +** field) then convert that term into a copy of the corresponding result set ** column. ** ** If any errors are detected, add an error message to pParse and @@ -75762,12 +75968,12 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( pEList = pSelect->pEList; assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */ for(i=0, pItem=pOrderBy->a; inExpr; i++, pItem++){ - if( pItem->iOrderByCol ){ - if( pItem->iOrderByCol>pEList->nExpr ){ + if( pItem->u.x.iOrderByCol ){ + if( pItem->u.x.iOrderByCol>pEList->nExpr ){ resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); return 1; } - resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0); + resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, zType,0); } } return 0; @@ -75816,7 +76022,7 @@ static int resolveOrderGroupBy( ** a copy of the iCol-th result-set column. The subsequent call to ** sqlite3ResolveOrderGroupBy() will convert the expression to a ** copy of the iCol-th result-set expression. */ - pItem->iOrderByCol = (u16)iCol; + pItem->u.x.iOrderByCol = (u16)iCol; continue; } } @@ -75828,18 +76034,18 @@ static int resolveOrderGroupBy( resolveOutOfRangeError(pParse, zType, i+1, nResult); return 1; } - pItem->iOrderByCol = (u16)iCol; + pItem->u.x.iOrderByCol = (u16)iCol; continue; } /* Otherwise, treat the ORDER BY term as an ordinary expression */ - pItem->iOrderByCol = 0; + pItem->u.x.iOrderByCol = 0; if( sqlite3ResolveExprNames(pNC, pE) ){ return 1; } for(j=0; jpEList->nExpr; j++){ if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ - pItem->iOrderByCol = j+1; + pItem->u.x.iOrderByCol = j+1; } } } @@ -76711,16 +76917,25 @@ SQLITE_PRIVATE Expr *sqlite3PExpr( } /* -** Return 1 if an expression must be FALSE in all cases and 0 if the -** expression might be true. This is an optimization. If is OK to -** return 0 here even if the expression really is always false (a -** false negative). But it is a bug to return 1 if the expression -** might be true in some rare circumstances (a false positive.) +** If the expression is always either TRUE or FALSE (respectively), +** then return 1. If one cannot determine the truth value of the +** expression at compile-time return 0. +** +** This is an optimization. If is OK to return 0 here even if +** the expression really is always false or false (a false negative). +** But it is a bug to return 1 if the expression might have different +** boolean values in different circumstances (a false positive.) ** ** Note that if the expression is part of conditional for a ** LEFT JOIN, then we cannot determine at compile-time whether or not ** is it true or false, so always return 0. */ +static int exprAlwaysTrue(Expr *p){ + int v = 0; + if( ExprHasProperty(p, EP_FromJoin) ) return 0; + if( !sqlite3ExprIsInteger(p, &v) ) return 0; + return v!=0; +} static int exprAlwaysFalse(Expr *p){ int v = 0; if( ExprHasProperty(p, EP_FromJoin) ) return 0; @@ -77074,6 +77289,33 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){ return pNew; } +/* +** Create and return a deep copy of the object passed as the second +** argument. If an OOM condition is encountered, NULL is returned +** and the db->mallocFailed flag set. +*/ +#ifndef SQLITE_OMIT_CTE +static With *withDup(sqlite3 *db, With *p){ + With *pRet = 0; + if( p ){ + int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); + pRet = sqlite3DbMallocZero(db, nByte); + if( pRet ){ + int i; + pRet->nCte = p->nCte; + for(i=0; inCte; i++){ + pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0); + pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0); + pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName); + } + } + } + return pRet; +} +#else +# define withDup(x,y) 0 +#endif + /* ** The following group of routines make deep copies of expressions, ** expression lists, ID lists, and select statements. The copies can @@ -77118,8 +77360,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags) pItem->sortOrder = pOldItem->sortOrder; pItem->done = 0; pItem->bSpanIsTab = pOldItem->bSpanIsTab; - pItem->iOrderByCol = pOldItem->iOrderByCol; - pItem->iAlias = pOldItem->iAlias; + pItem->u = pOldItem->u; } return pNew; } @@ -77155,6 +77396,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){ pNewItem->regReturn = pOldItem->regReturn; pNewItem->isCorrelated = pOldItem->isCorrelated; pNewItem->viaCoroutine = pOldItem->viaCoroutine; + pNewItem->isRecursive = pOldItem->isRecursive; pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex); pNewItem->notIndexed = pOldItem->notIndexed; pNewItem->pIndex = pOldItem->pIndex; @@ -77216,6 +77458,8 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->addrOpenEphm[2] = -1; + pNew->nSelectRow = p->nSelectRow; + pNew->pWith = withDup(db, p->pWith); return pNew; } #else @@ -77380,9 +77624,12 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ switch( pExpr->op ){ /* Consider functions to be constant if all their arguments are constant - ** and pWalker->u.i==2 */ + ** and either pWalker->u.i==2 or the function as the SQLITE_FUNC_CONST + ** flag. */ case TK_FUNCTION: - if( pWalker->u.i==2 ) return 0; + if( pWalker->u.i==2 || ExprHasProperty(pExpr,EP_Constant) ){ + return WRC_Continue; + } /* Fall through */ case TK_ID: case TK_COLUMN: @@ -77720,8 +77967,8 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ sqlite3 *db = pParse->db; /* Database connection */ Table *pTab; /* Table . */ Expr *pExpr; /* Expression */ - int iCol; /* Index of column */ - int iDb; /* Database idx for pTab */ + i16 iCol; /* Index of column */ + i16 iDb; /* Database idx for pTab */ assert( p ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ @@ -77729,7 +77976,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ pTab = p->pSrc->a[0].pTab; pExpr = p->pEList->a[0].pExpr; - iCol = pExpr->iColumn; + iCol = (i16)pExpr->iColumn; /* Code an OP_VerifyCookie and OP_TableLock for
. */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -77767,16 +78014,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){ if( (pIdx->aiColumn[0]==iCol) && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq - && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None)) + && (!mustBeUnique || (pIdx->nKeyCol==1 && pIdx->onError!=OE_None)) ){ - int iAddr; - char *pKey; - - pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); - iAddr = sqlite3CodeOnce(pParse); - - sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, - pKey,P4_KEYINFO_HANDOFF); + int iAddr = sqlite3CodeOnce(pParse); + sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; @@ -77916,7 +78158,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED); - pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1); + pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) @@ -77934,13 +78176,14 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( pExpr->x.pSelect->iLimit = 0; testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ - sqlite3DbFree(pParse->db, pKeyInfo); + sqlite3KeyInfoUnref(pKeyInfo); return 0; } pEList = pExpr->x.pSelect->pEList; assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ assert( pEList!=0 ); assert( pEList->nExpr>0 ); + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pEList->a[0].pExpr); }else if( ALWAYS(pExpr->x.pList!=0) ){ @@ -77960,6 +78203,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( affinity = SQLITE_AFF_NONE; } if( pKeyInfo ){ + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); } @@ -78001,7 +78245,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( sqlite3ReleaseTempReg(pParse, r2); } if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO_HANDOFF); + sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); } break; } @@ -78359,6 +78603,11 @@ SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){ */ SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){ pParse->iCacheLevel++; +#ifdef SQLITE_DEBUG + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ + printf("PUSH to %d\n", pParse->iCacheLevel); + } +#endif } /* @@ -78372,6 +78621,11 @@ SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse, int N){ assert( N>0 ); assert( pParse->iCacheLevel>=N ); pParse->iCacheLevel -= N; +#ifdef SQLITE_DEBUG + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ + printf("POP to %d\n", pParse->iCacheLevel); + } +#endif for(i=0, p=pParse->aColCache; iiReg && p->iLevel>pParse->iCacheLevel ){ cacheEntryClear(pParse, p); @@ -78402,15 +78656,19 @@ static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable( Vdbe *v, /* The VDBE under construction */ Table *pTab, /* The table containing the value */ - int iTabCur, /* The cursor for this table */ + int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */ int iCol, /* Index of the column to extract */ - int regOut /* Extract the valud into this register */ + int regOut /* Extract the value into this register */ ){ if( iCol<0 || iCol==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut); }else{ int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; - sqlite3VdbeAddOp3(v, op, iTabCur, iCol, regOut); + int x = iCol; + if( !HasRowid(pTab) ){ + x = sqlite3ColumnOfIndex(sqlite3PrimaryKeyIndex(pTab), iCol); + } + sqlite3VdbeAddOp3(v, op, iTabCur, x, regOut); } if( iCol>=0 ){ sqlite3ColumnDefault(v, pTab, iCol, regOut); @@ -78462,6 +78720,11 @@ SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){ int i; struct yColCache *p; +#if SQLITE_DEBUG + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ + printf("CLEAR\n"); + } +#endif for(i=0, p=pParse->aColCache; iiReg ){ cacheEntryClear(pParse, p); @@ -78543,6 +78806,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) int regFree2 = 0; /* If non-zero free this temporary register */ int r1, r2, r3, r4; /* Various register numbers */ sqlite3 *db = pParse->db; /* The database connection */ + Expr tempX; /* Temporary expression node */ assert( target>0 && target<=pParse->nMem ); if( v==0 ){ @@ -78762,8 +79026,10 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) codeReal(v, pLeft->u.zToken, 1, target); #endif }else{ - regFree1 = r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Integer, 0, r1); + tempX.op = TK_INTEGER; + tempX.flags = EP_IntValue|EP_TokenOnly; + tempX.u.iValue = 0; + r1 = sqlite3ExprCodeTemp(pParse, &tempX, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free2); sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target); testcase( regFree2==0 ); @@ -78808,21 +79074,18 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) } break; } - case TK_CONST_FUNC: case TK_FUNCTION: { ExprList *pFarg; /* List of function arguments */ int nFarg; /* Number of function arguments */ FuncDef *pDef; /* The function definition object */ int nId; /* Length of the function name in bytes */ const char *zId; /* The function name */ - int constMask = 0; /* Mask of function arguments that are constant */ + u32 constMask = 0; /* Mask of function arguments that are constant */ int i; /* Loop counter */ u8 enc = ENC(db); /* The text encoding used by this database */ CollSeq *pColl = 0; /* A collating sequence */ assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - testcase( op==TK_CONST_FUNC ); - testcase( op==TK_FUNCTION ); if( ExprHasProperty(pExpr, EP_TokenOnly) ){ pFarg = 0; }else{ @@ -78866,8 +79129,22 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) break; } + for(i=0; ia[i].pExpr) ){ + testcase( i==31 ); + constMask |= MASKBIT32(i); + } + if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ + pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr); + } + } if( pFarg ){ - r1 = sqlite3GetTempRange(pParse, nFarg); + if( constMask ){ + r1 = pParse->nMem+1; + pParse->nMem += nFarg; + }else{ + r1 = sqlite3GetTempRange(pParse, nFarg); + } /* For length() and typeof() functions with a column argument, ** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG @@ -78882,14 +79159,15 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){ assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG ); assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG ); - testcase( (pDef->funcFlags&~SQLITE_FUNC_ENCMASK) - ==SQLITE_FUNC_LENGTH ); - pFarg->a[0].pExpr->op2 = pDef->funcFlags&~SQLITE_FUNC_ENCMASK; + testcase( pDef->funcFlags & OPFLAG_LENGTHARG ); + pFarg->a[0].pExpr->op2 = + pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG); } } sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ - sqlite3ExprCodeExprList(pParse, pFarg, r1, 1); + sqlite3ExprCodeExprList(pParse, pFarg, r1, + SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR); sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */ }else{ r1 = 0; @@ -78913,14 +79191,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr); } #endif - for(i=0; ia[i].pExpr) ){ - constMask |= (1<funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){ - pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr); - } - } if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){ if( !pColl ) pColl = db->pDfltColl; sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); @@ -78928,7 +79198,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target, (char*)pDef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nFarg); - if( nFarg ){ + if( nFarg && constMask==0 ){ sqlite3ReleaseTempRange(pParse, r1, nFarg); } break; @@ -79079,7 +79349,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) ExprList *pEList; /* List of WHEN terms */ struct ExprList_item *aListelem; /* Array of WHEN terms */ Expr opCompare; /* The X==Ei expression */ - Expr cacheX; /* Cached expression X */ Expr *pX; /* The X expression */ Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */ VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; ) @@ -79091,13 +79360,12 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) nExpr = pEList->nExpr; endLabel = sqlite3VdbeMakeLabel(v); if( (pX = pExpr->pLeft)!=0 ){ - cacheX = *pX; + tempX = *pX; testcase( pX->op==TK_COLUMN ); - testcase( pX->op==TK_REGISTER ); - exprToRegister(&cacheX, sqlite3ExprCodeTemp(pParse, pX, ®Free1)); + exprToRegister(&tempX, sqlite3ExprCodeTemp(pParse, pX, ®Free1)); testcase( regFree1==0 ); opCompare.op = TK_EQ; - opCompare.pLeft = &cacheX; + opCompare.pLeft = &tempX; pTest = &opCompare; /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001: ** The value in regFree1 might get SCopy-ed into the file result. @@ -79117,7 +79385,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) testcase( pTest->op==TK_COLUMN ); sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL); testcase( aListelem[i+1].pExpr->op==TK_COLUMN ); - testcase( aListelem[i+1].pExpr->op==TK_REGISTER ); sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target); sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel); sqlite3ExprCachePop(pParse, 1); @@ -79156,7 +79423,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); }else{ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, - pExpr->affinity, pExpr->u.zToken, 0); + pExpr->affinity, pExpr->u.zToken, 0, 0); } break; @@ -79168,6 +79435,28 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) return inReg; } +/* +** Factor out the code of the given expression to initialization time. +*/ +SQLITE_PRIVATE void sqlite3ExprCodeAtInit( + Parse *pParse, /* Parsing context */ + Expr *pExpr, /* The expression to code when the VDBE initializes */ + int regDest, /* Store the value in this register */ + u8 reusable /* True if this expression is reusable */ +){ + ExprList *p; + assert( ConstFactorOk(pParse) ); + p = pParse->pConstExpr; + pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); + p = sqlite3ExprListAppend(pParse, p, pExpr); + if( p ){ + struct ExprList_item *pItem = &p->a[p->nExpr-1]; + pItem->u.iConstExprReg = regDest; + pItem->reusable = reusable; + } + pParse->pConstExpr = p; +} + /* ** Generate code to evaluate an expression and store the results ** into a register. Return the register number where the results @@ -79176,15 +79465,40 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) ** If the register is a temporary register that can be deallocated, ** then write its number into *pReg. If the result register is not ** a temporary, then set *pReg to zero. +** +** If pExpr is a constant, then this routine might generate this +** code to fill the register in the initialization section of the +** VDBE program, in order to factor it out of the evaluation loop. */ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ - int r1 = sqlite3GetTempReg(pParse); - int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); - if( r2==r1 ){ - *pReg = r1; + int r2; + pExpr = sqlite3ExprSkipCollate(pExpr); + if( ConstFactorOk(pParse) + && pExpr->op!=TK_REGISTER + && sqlite3ExprIsConstantNotJoin(pExpr) + ){ + ExprList *p = pParse->pConstExpr; + int i; + *pReg = 0; + if( p ){ + struct ExprList_item *pItem; + for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){ + if( pItem->reusable && sqlite3ExprCompare(pItem->pExpr,pExpr,-1)==0 ){ + return pItem->u.iConstExprReg; + } + } + } + r2 = ++pParse->nMem; + sqlite3ExprCodeAtInit(pParse, pExpr, r2, 1); }else{ - sqlite3ReleaseTempReg(pParse, r1); - *pReg = 0; + int r1 = sqlite3GetTempReg(pParse); + r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); + if( r2==r1 ){ + *pReg = r1; + }else{ + sqlite3ReleaseTempReg(pParse, r1); + *pReg = 0; + } } return r2; } @@ -79227,12 +79541,13 @@ SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int targe int inReg; inReg = sqlite3ExprCode(pParse, pExpr, target); assert( target>0 ); - /* This routine is called for terms to INSERT or UPDATE. And the only - ** other place where expressions can be converted into TK_REGISTER is - ** in WHERE clause processing. So as currently implemented, there is - ** no way for a TK_REGISTER to exist here. But it seems prudent to - ** keep the ALWAYS() in case the conditions above change with future - ** modifications or enhancements. */ + /* The only place, other than this routine, where expressions can be + ** converted to TK_REGISTER is internal subexpressions in BETWEEN and + ** CASE operators. Neither ever calls this routine. And this routine + ** is never called twice on the same expression. Hence it is impossible + ** for the input to this routine to already be a register. Nevertheless, + ** it seems prudent to keep the ALWAYS() in case the conditions above + ** change with future modifications or enhancements. */ if( ALWAYS(pExpr->op!=TK_REGISTER) ){ int iMem; iMem = ++pParse->nMem; @@ -79364,7 +79679,6 @@ SQLITE_PRIVATE void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ } case TK_AGG_FUNCTION: - case TK_CONST_FUNC: case TK_FUNCTION: { ExprList *pFarg; /* List of function arguments */ if( ExprHasProperty(pExpr, EP_TokenOnly) ){ @@ -79515,164 +79829,51 @@ SQLITE_PRIVATE void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){ } #endif /* SQLITE_DEBUG */ -/* -** Return TRUE if pExpr is an constant expression that is appropriate -** for factoring out of a loop. Appropriate expressions are: -** -** * Any expression that evaluates to two or more opcodes. -** -** * Any OP_Integer, OP_Real, OP_String, OP_Blob, OP_Null, -** or OP_Variable that does not need to be placed in a -** specific register. -** -** There is no point in factoring out single-instruction constant -** expressions that need to be placed in a particular register. -** We could factor them out, but then we would end up adding an -** OP_SCopy instruction to move the value into the correct register -** later. We might as well just use the original instruction and -** avoid the OP_SCopy. -*/ -static int isAppropriateForFactoring(Expr *p){ - if( !sqlite3ExprIsConstantNotJoin(p) ){ - return 0; /* Only constant expressions are appropriate for factoring */ - } - if( (p->flags & EP_FixedDest)==0 ){ - return 1; /* Any constant without a fixed destination is appropriate */ - } - while( p->op==TK_UPLUS ) p = p->pLeft; - switch( p->op ){ -#ifndef SQLITE_OMIT_BLOB_LITERAL - case TK_BLOB: -#endif - case TK_VARIABLE: - case TK_INTEGER: - case TK_FLOAT: - case TK_NULL: - case TK_STRING: { - testcase( p->op==TK_BLOB ); - testcase( p->op==TK_VARIABLE ); - testcase( p->op==TK_INTEGER ); - testcase( p->op==TK_FLOAT ); - testcase( p->op==TK_NULL ); - testcase( p->op==TK_STRING ); - /* Single-instruction constants with a fixed destination are - ** better done in-line. If we factor them, they will just end - ** up generating an OP_SCopy to move the value to the destination - ** register. */ - return 0; - } - case TK_UMINUS: { - if( p->pLeft->op==TK_FLOAT || p->pLeft->op==TK_INTEGER ){ - return 0; - } - break; - } - default: { - break; - } - } - return 1; -} - -/* -** If pExpr is a constant expression that is appropriate for -** factoring out of a loop, then evaluate the expression -** into a register and convert the expression into a TK_REGISTER -** expression. -*/ -static int evalConstExpr(Walker *pWalker, Expr *pExpr){ - Parse *pParse = pWalker->pParse; - switch( pExpr->op ){ - case TK_IN: - case TK_REGISTER: { - return WRC_Prune; - } - case TK_COLLATE: { - return WRC_Continue; - } - case TK_FUNCTION: - case TK_AGG_FUNCTION: - case TK_CONST_FUNC: { - /* The arguments to a function have a fixed destination. - ** Mark them this way to avoid generated unneeded OP_SCopy - ** instructions. - */ - ExprList *pList = pExpr->x.pList; - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - if( pList ){ - int i = pList->nExpr; - struct ExprList_item *pItem = pList->a; - for(; i>0; i--, pItem++){ - if( ALWAYS(pItem->pExpr) ) pItem->pExpr->flags |= EP_FixedDest; - } - } - break; - } - } - if( isAppropriateForFactoring(pExpr) ){ - int r1 = ++pParse->nMem; - int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); - /* If r2!=r1, it means that register r1 is never used. That is harmless - ** but suboptimal, so we want to know about the situation to fix it. - ** Hence the following assert: */ - assert( r2==r1 ); - exprToRegister(pExpr, r2); - return WRC_Prune; - } - return WRC_Continue; -} - -/* -** Preevaluate constant subexpressions within pExpr and store the -** results in registers. Modify pExpr so that the constant subexpresions -** are TK_REGISTER opcodes that refer to the precomputed values. -** -** This routine is a no-op if the jump to the cookie-check code has -** already occur. Since the cookie-check jump is generated prior to -** any other serious processing, this check ensures that there is no -** way to accidently bypass the constant initializations. -** -** This routine is also a no-op if the SQLITE_FactorOutConst optimization -** is disabled via the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) -** interface. This allows test logic to verify that the same answer is -** obtained for queries regardless of whether or not constants are -** precomputed into registers or if they are inserted in-line. -*/ -SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){ - Walker w; - if( pParse->cookieGoto ) return; - if( OptimizationDisabled(pParse->db, SQLITE_FactorOutConst) ) return; - memset(&w, 0, sizeof(w)); - w.xExprCallback = evalConstExpr; - w.pParse = pParse; - sqlite3WalkExpr(&w, pExpr); -} - - /* ** Generate code that pushes the value of every element of the given ** expression list into a sequence of registers beginning at target. ** ** Return the number of elements evaluated. +** +** The SQLITE_ECEL_DUP flag prevents the arguments from being +** filled using OP_SCopy. OP_Copy must be used instead. +** +** The SQLITE_ECEL_FACTOR argument allows constant arguments to be +** factored out into initialization code. */ SQLITE_PRIVATE int sqlite3ExprCodeExprList( Parse *pParse, /* Parsing context */ ExprList *pList, /* The expression list to be coded */ int target, /* Where to write results */ - int doHardCopy /* Make a hard copy of every element */ + u8 flags /* SQLITE_ECEL_* flags */ ){ struct ExprList_item *pItem; int i, n; + u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy; assert( pList!=0 ); assert( target>0 ); assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ n = pList->nExpr; + if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR; for(pItem=pList->a, i=0; ipExpr; - int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); - if( inReg!=target+i ){ - sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy, - inReg, target+i); + if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){ + sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0); + }else{ + int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); + if( inReg!=target+i ){ + VdbeOp *pOp; + Vdbe *v = pParse->pVdbe; + if( copyOp==OP_Copy + && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy + && pOp->p1+pOp->p3+1==inReg + && pOp->p2+pOp->p3+1==target+i + ){ + pOp->p3++; + }else{ + sqlite3VdbeAddOp2(v, copyOp, inReg, target+i); + } + } } } return n; @@ -79762,8 +79963,8 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int case TK_AND: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); - sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL); + sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); sqlite3VdbeResolveLabel(v, d2); sqlite3ExprCachePop(pParse, 1); @@ -79772,7 +79973,9 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int case TK_OR: { testcase( jumpIfNull==0 ); sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); + sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); + sqlite3ExprCachePop(pParse, 1); break; } case TK_NOT: { @@ -79847,10 +80050,16 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int } #endif default: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); - sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); - testcase( regFree1==0 ); - testcase( jumpIfNull==0 ); + if( exprAlwaysTrue(pExpr) ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); + }else if( exprAlwaysFalse(pExpr) ){ + /* No-op */ + }else{ + r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); + sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); + testcase( regFree1==0 ); + testcase( jumpIfNull==0 ); + } break; } } @@ -79913,14 +80122,16 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int case TK_AND: { testcase( jumpIfNull==0 ); sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); + sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); + sqlite3ExprCachePop(pParse, 1); break; } case TK_OR: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); - sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL); + sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); sqlite3VdbeResolveLabel(v, d2); sqlite3ExprCachePop(pParse, 1); @@ -79992,10 +80203,16 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int } #endif default: { - r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); - sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); - testcase( regFree1==0 ); - testcase( jumpIfNull==0 ); + if( exprAlwaysFalse(pExpr) ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); + }else if( exprAlwaysTrue(pExpr) ){ + /* no-op */ + }else{ + r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); + sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); + testcase( regFree1==0 ); + testcase( jumpIfNull==0 ); + } break; } } @@ -80026,16 +80243,18 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int ** an incorrect 0 or 1 could lead to a malfunction. */ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){ - if( pA==0||pB==0 ){ + u32 combinedFlags; + if( pA==0 || pB==0 ){ return pB==pA ? 0 : 2; } - assert( !ExprHasProperty(pA, EP_TokenOnly|EP_Reduced) ); - assert( !ExprHasProperty(pB, EP_TokenOnly|EP_Reduced) ); - if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){ + combinedFlags = pA->flags | pB->flags; + if( combinedFlags & EP_IntValue ){ + if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){ + return 0; + } return 2; } - if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; - if( pA->op!=pB->op && (pA->op!=TK_REGISTER || pA->op2!=pB->op) ){ + if( pA->op!=pB->op ){ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB, iTab)<2 ){ return 1; } @@ -80044,23 +80263,23 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){ } return 2; } - if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2; - if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2; - if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; - if( pA->iColumn!=pB->iColumn ) return 2; - if( pA->iTable!=pB->iTable - && pA->op!=TK_REGISTER - && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; - if( ExprHasProperty(pA, EP_IntValue) ){ - if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ - return 2; - } - }else if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken){ - if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; + if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken ){ if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return pA->op==TK_COLLATE ? 1 : 2; } } + if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; + if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){ + if( combinedFlags & EP_xIsSelect ) return 2; + if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2; + if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2; + if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; + if( ALWAYS((combinedFlags & EP_Reduced)==0) ){ + if( pA->iColumn!=pB->iColumn ) return 2; + if( pA->iTable!=pB->iTable + && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; + } + } return 0; } @@ -81532,8 +81751,7 @@ static void openStatTable( /* Open the sqlite_stat[134] tables for writing. */ for(i=0; aTable[i].zCols; i++){ assert( inRowid ){ + sqlite3DbFree(db, p->u.aRowid); + p->nRowid = 0; + } +} +#endif + +/* Initialize the BLOB value of a ROWID +*/ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){ + assert( db!=0 ); + if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); + p->u.aRowid = sqlite3DbMallocRaw(db, n); + if( p->u.aRowid ){ + p->nRowid = n; + memcpy(p->u.aRowid, pData, n); + }else{ + p->nRowid = 0; + } +} +#endif + +/* Initialize the INTEGER value of a ROWID. +*/ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){ + assert( db!=0 ); + if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); + p->nRowid = 0; + p->u.iRowid = iRowid; +} +#endif + + +/* +** Copy the contents of object (*pFrom) into (*pTo). +*/ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){ + pTo->isPSample = pFrom->isPSample; + pTo->iCol = pFrom->iCol; + pTo->iHash = pFrom->iHash; + memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol); + memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol); + memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol); + if( pFrom->nRowid ){ + sampleSetRowid(p->db, pTo, pFrom->nRowid, pFrom->u.aRowid); + }else{ + sampleSetRowidInt64(p->db, pTo, pFrom->u.iRowid); + } +} +#endif + +/* +** Reclaim all memory of a Stat4Accum structure. +*/ +static void stat4Destructor(void *pOld){ + Stat4Accum *p = (Stat4Accum*)pOld; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + int i; + for(i=0; inCol; i++) sampleClear(p->db, p->aBest+i); + for(i=0; imxSample; i++) sampleClear(p->db, p->a+i); + sampleClear(p->db, &p->current); +#endif + sqlite3DbFree(p->db, p); +} + /* ** Implementation of the stat_init(N,C) SQL function. The two parameters ** are the number of rows in the table or index (C) and the number of columns @@ -81595,6 +81891,7 @@ static void statInit( int nCol; /* Number of columns in index being sampled */ int nColUp; /* nCol rounded up for alignment */ int n; /* Bytes of space to allocate */ + sqlite3 *db; /* Database connection */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 int mxSample = SQLITE_STAT4_SAMPLES; #endif @@ -81611,16 +81908,18 @@ static void statInit( + sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */ - + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */ + + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample) #endif ; - p = sqlite3MallocZero(n); + db = sqlite3_context_db_handle(context); + p = sqlite3DbMallocZero(db, n); if( p==0 ){ sqlite3_result_error_nomem(context); return; } + p->db = db; p->nRow = 0; p->nCol = nCol; p->current.anDLt = (tRowcnt*)&p[1]; @@ -81655,7 +81954,7 @@ static void statInit( #endif /* Return a pointer to the allocated object to the caller */ - sqlite3_result_blob(context, p, sizeof(p), sqlite3_free); + sqlite3_result_blob(context, p, sizeof(p), stat4Destructor); } static const FuncDef statInitFuncdef = { 1+IsStat34, /* nArg */ @@ -81729,25 +82028,12 @@ static int sampleIsBetter( #endif } -/* -** Copy the contents of object (*pFrom) into (*pTo). -*/ -static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){ - pTo->iRowid = pFrom->iRowid; - pTo->isPSample = pFrom->isPSample; - pTo->iCol = pFrom->iCol; - pTo->iHash = pFrom->iHash; - memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol); - memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol); - memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol); -} - /* ** Copy the contents of sample *pNew into the p->a[] array. If necessary, ** remove the least desirable sample from p->a[] to make room. */ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ - Stat4Sample *pSample; + Stat4Sample *pSample = 0; int i; assert( IsStat4 || nEqZero==0 ); @@ -81787,8 +82073,10 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ tRowcnt *anEq = pMin->anEq; tRowcnt *anLt = pMin->anLt; tRowcnt *anDLt = pMin->anDLt; + sampleClear(p->db, pMin); memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1)); pSample = &p->a[p->nSample-1]; + pSample->nRowid = 0; pSample->anEq = anEq; pSample->anDLt = anDLt; pSample->anLt = anLt; @@ -81885,16 +82173,17 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){ } /* -** Implementation of the stat_push SQL function: stat_push(P,R,C) +** Implementation of the stat_push SQL function: stat_push(P,C,R) ** Arguments: ** ** P Pointer to the Stat4Accum object created by stat_init() ** C Index of left-most column to differ from previous row -** R Rowid for the current row +** R Rowid for the current row. Might be a key record for +** WITHOUT ROWID tables. ** ** The SQL function always returns NULL. ** -** The R parameter is only used for STAT3 and STAT4. +** The R parameter is only used for STAT3 and STAT4 */ static void statPush( sqlite3_context *context, @@ -81934,7 +82223,12 @@ static void statPush( } p->nRow++; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - p->current.iRowid = sqlite3_value_int64(argv[2]); + if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ + sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); + }else{ + sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), + sqlite3_value_blob(argv[2])); + } p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; #endif @@ -82058,7 +82352,13 @@ static void statGet( p->iGet = 0; } if( p->iGetnSample ){ - sqlite3_result_int64(context, p->a[p->iGet].iRowid); + Stat4Sample *pS = p->a + p->iGet; + if( pS->nRowid==0 ){ + sqlite3_result_int64(context, pS->u.iRowid); + }else{ + sqlite3_result_blob(context, pS->u.aRowid, pS->nRowid, + SQLITE_TRANSIENT); + } } }else{ tRowcnt *aCnt = 0; @@ -82195,22 +82495,26 @@ static void analyzeOneTable( for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns indexed by pIdx */ - KeyInfo *pKey; /* KeyInfo structure for pIdx */ int *aGotoChng; /* Array of jump instruction addresses */ int addrRewind; /* Address of "OP_Rewind iIdxCur" */ int addrGotoChng0; /* Address of "Goto addr_chng_0" */ int addrNextRow; /* Address of "next_row:" */ + const char *zIdxName; /* Name of the index */ if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0; VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName)); - nCol = pIdx->nColumn; + nCol = pIdx->nKeyCol; aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*(nCol+1)); if( aGotoChng==0 ) continue; - pKey = sqlite3IndexKeyinfo(pParse, pIdx); /* Populate the register containing the index name. */ - sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); + if( pIdx->autoIndex==2 && !HasRowid(pTab) ){ + zIdxName = pTab->zName; + }else{ + zIdxName = pIdx->zName; + } + sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, zIdxName, 0); /* ** Pseudo-code for loop that calls stat_push(): @@ -82253,7 +82557,7 @@ static void analyzeOneTable( /* Open a read-only cursor on the index being analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); /* Invoke the stat_init() function. The arguments are: @@ -82327,8 +82631,21 @@ static void analyzeOneTable( */ sqlite3VdbeJumpHere(v, aGotoChng[nCol]); #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); assert( regRowid==(regStat4+2) ); + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); + }else{ + Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); + int j, k, regKey; + regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); + for(j=0; jnKeyCol; j++){ + k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]); + sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); + VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName)); + } + sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); + sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); + } #endif assert( regChng==(regStat4+1) ); sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp); @@ -82354,6 +82671,7 @@ static void analyzeOneTable( int regSampleRowid = regCol + nCol; int addrNext; int addrIsNull; + u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; pParse->nMem = MAX(pParse->nMem, regCol+nCol+1); @@ -82363,13 +82681,13 @@ static void analyzeOneTable( callStatGet(v, regStat4, STAT_GET_NEQ, regEq); callStatGet(v, regStat4, STAT_GET_NLT, regLt); callStatGet(v, regStat4, STAT_GET_NDLT, regDLt); - sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, addrNext, regSampleRowid); + sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0); #ifdef SQLITE_ENABLE_STAT3 sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, pIdx->aiColumn[0], regSample); #else for(i=0; iaiColumn[i]; + i16 iCol = pIdx->aiColumn[i]; sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i); } sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol+1, regSample); @@ -82617,15 +82935,17 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ if( pTable==0 ){ return 0; } - if( argv[1] ){ - pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); - }else{ + if( argv[1]==0 ){ pIndex = 0; + }else if( sqlite3_stricmp(argv[0],argv[1])==0 ){ + pIndex = sqlite3PrimaryKeyIndex(pTable); + }else{ + pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); } z = argv[2]; if( pIndex ){ - decodeIntArray((char*)z, pIndex->nColumn+1, pIndex->aiRowEst, pIndex); + decodeIntArray((char*)z, pIndex->nKeyCol+1, pIndex->aiRowEst, pIndex); if( pIndex->pPartIdxWhere==0 ) pTable->nRowEst = pIndex->aiRowEst[0]; }else{ Index fakeIdx; @@ -82671,7 +82991,7 @@ static void initAvgEq(Index *pIdx){ IndexSample *aSample = pIdx->aSample; IndexSample *pFinal = &aSample[pIdx->nSample-1]; int iCol; - for(iCol=0; iColnColumn; iCol++){ + for(iCol=0; iColnKeyCol; iCol++){ int i; /* Used to iterate through samples */ tRowcnt sumEq = 0; /* Sum of the nEq values */ tRowcnt nSum = 0; /* Number of terms contributing to sumEq */ @@ -82699,6 +83019,23 @@ static void initAvgEq(Index *pIdx){ } } +/* +** Look up an index by name. Or, if the name of a WITHOUT ROWID table +** is supplied instead, find the PRIMARY KEY index for that table. +*/ +static Index *findIndexOrPrimaryKey( + sqlite3 *db, + const char *zName, + const char *zDb +){ + Index *pIdx = sqlite3FindIndex(db, zName, zDb); + if( pIdx==0 ){ + Table *pTab = sqlite3FindTable(db, zName, zDb); + if( pTab && !HasRowid(pTab) ) pIdx = sqlite3PrimaryKeyIndex(pTab); + } + return pIdx; +} + /* ** Load the content from either the sqlite_stat4 or sqlite_stat3 table ** into the relevant Index.aSample[] arrays. @@ -82748,14 +83085,14 @@ static int loadStatTbl( zIndex = (char *)sqlite3_column_text(pStmt, 0); if( zIndex==0 ) continue; nSample = sqlite3_column_int(pStmt, 1); - pIdx = sqlite3FindIndex(db, zIndex, zDb); + pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); assert( pIdx==0 || bStat3 || pIdx->nSample==0 ); /* Index.nSample is non-zero at this point if data has already been ** loaded from the stat4 table. In this case ignore stat3 data. */ if( pIdx==0 || pIdx->nSample ) continue; if( bStat3==0 ){ - nIdxCol = pIdx->nColumn+1; - nAvgCol = pIdx->nColumn; + nIdxCol = pIdx->nKeyCol+1; + nAvgCol = pIdx->nKeyCol; } pIdx->nSampleCol = nIdxCol; nByte = sizeof(IndexSample) * nSample; @@ -82794,7 +83131,7 @@ static int loadStatTbl( zIndex = (char *)sqlite3_column_text(pStmt, 0); if( zIndex==0 ) continue; - pIdx = sqlite3FindIndex(db, zIndex, zDb); + pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); if( pIdx==0 ) continue; /* This next condition is true if data has already been loaded from ** the sqlite_stat4 table. In this case ignore stat3 data. */ @@ -82975,10 +83312,6 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr) if( pExpr ){ if( pExpr->op!=TK_ID ){ rc = sqlite3ResolveExprNames(pName, pExpr); - if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){ - sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken); - return SQLITE_ERROR; - } }else{ pExpr->op = TK_STRING; } @@ -83908,6 +84241,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ assert( !pParse->isMultiWrite || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); if( v ){ + while( sqlite3VdbeDeletePriorOpcode(v, OP_Close) ){} sqlite3VdbeAddOp0(v, OP_Halt); /* The cookie mask contains one bit for each database file open. @@ -83918,7 +84252,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ */ if( pParse->cookieGoto>0 ){ yDbMask mask; - int iDb; + int iDb, i, addr; sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDbnDb; mask<<=1, iDb++){ if( (mask & pParse->cookieMask)==0 ) continue; @@ -83932,14 +84266,11 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ } } #ifndef SQLITE_OMIT_VIRTUALTABLE - { - int i; - for(i=0; inVtabLock; i++){ - char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); - sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); - } - pParse->nVtabLock = 0; + for(i=0; inVtabLock; i++){ + char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); + sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); } + pParse->nVtabLock = 0; #endif /* Once all the cookies have been verified and transactions opened, @@ -83952,8 +84283,18 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ */ sqlite3AutoincrementBegin(pParse); + /* Code constant expressions that where factored out of inner loops */ + addr = pParse->cookieGoto; + if( pParse->pConstExpr ){ + ExprList *pEL = pParse->pConstExpr; + pParse->cookieGoto = 0; + for(i=0; inExpr; i++){ + sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg); + } + } + /* Finally, jump back to the beginning of the executable code. */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); } } @@ -83961,10 +84302,6 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ /* Get the VDBE program ready for execution */ if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){ -#ifdef SQLITE_DEBUG - FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; - sqlite3VdbeTrace(v, trace); -#endif assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ @@ -84150,8 +84487,10 @@ static void freeIndex(sqlite3 *db, Index *p){ #ifndef SQLITE_OMIT_ANALYZE sqlite3DeleteIndexSamples(db, p); #endif + if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo); sqlite3ExprDelete(db, p->pPartIdxWhere); sqlite3DbFree(db, p->zColAff); + if( p->isResized ) sqlite3DbFree(db, p->azColl); sqlite3DbFree(db, p); } @@ -84409,8 +84748,7 @@ SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){ SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *p, int iDb){ Vdbe *v = sqlite3GetVdbe(p); sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb)); - sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb); - sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */ + sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5); if( p->nTab==0 ){ p->nTab = 1; } @@ -84515,6 +84853,27 @@ SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){ return SQLITE_OK; } +/* +** Return the PRIMARY KEY index of a table +*/ +SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table *pTab){ + Index *p; + for(p=pTab->pIndex; p && p->autoIndex!=2; p=p->pNext){} + return p; +} + +/* +** Return the column of index pIdx that corresponds to table +** column iCol. Return -1 if not found. +*/ +SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){ + int i; + for(i=0; inColumn; i++){ + if( iCol==pIdx->aiColumn[i] ) return i; + } + return -1; +} + /* ** Begin constructing a new table representation in memory. This is ** the first of several action routines that get called in response @@ -84714,7 +85073,7 @@ SQLITE_PRIVATE void sqlite3StartTable( }else #endif { - sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2); + pParse->addrCrTab = sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2); } sqlite3OpenMasterTable(pParse, iDb); sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1); @@ -84982,6 +85341,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( Table *pTab = pParse->pNewTable; char *zType = 0; int iCol = -1, i; + int nTerm; if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit; if( pTab->tabFlags & TF_HasPrimaryKey ){ sqlite3ErrorMsg(pParse, @@ -84992,39 +85352,43 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey( if( pList==0 ){ iCol = pTab->nCol - 1; pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY; + zType = pTab->aCol[iCol].zType; + nTerm = 1; }else{ - for(i=0; inExpr; i++){ + nTerm = pList->nExpr; + for(i=0; inCol; iCol++){ if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){ + pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY; + zType = pTab->aCol[iCol].zType; break; } } - if( iColnCol ){ - pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY; - } } - if( pList->nExpr>1 ) iCol = -1; } - if( iCol>=0 && iColnCol ){ - zType = pTab->aCol[iCol].zType; - } - if( zType && sqlite3StrICmp(zType, "INTEGER")==0 - && sortOrder==SQLITE_SO_ASC ){ + if( nTerm==1 + && zType && sqlite3StrICmp(zType, "INTEGER")==0 + && sortOrder==SQLITE_SO_ASC + ){ pTab->iPKey = iCol; pTab->keyConf = (u8)onError; assert( autoInc==0 || autoInc==1 ); pTab->tabFlags |= autoInc*TF_Autoincrement; + if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder; }else if( autoInc ){ #ifndef SQLITE_OMIT_AUTOINCREMENT sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " "INTEGER PRIMARY KEY"); #endif }else{ + Vdbe *v = pParse->pVdbe; Index *p; + if( v ) pParse->addrSkipPK = sqlite3VdbeAddOp0(v, OP_Noop); p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); if( p ){ p->autoIndex = 2; + if( v ) sqlite3VdbeJumpHere(v, pParse->addrSkipPK); } pList = 0; } @@ -85081,7 +85445,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){ ** collation type was added. Correct this if it is the case. */ for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){ - assert( pIdx->nColumn==1 ); + assert( pIdx->nKeyCol==1 ); if( pIdx->aiColumn[0]==i ){ pIdx->azColl[0] = p->aCol[i].zColl; } @@ -85189,10 +85553,10 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){ for(j=0; zIdent[j]; j++){ if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break; } - needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID; - if( !needQuote ){ - needQuote = zIdent[j]; - } + needQuote = sqlite3Isdigit(zIdent[0]) + || sqlite3KeywordCode(zIdent, j)!=TK_ID + || zIdent[j]!=0 + || j==0; if( needQuote ) z[i++] = '"'; for(j=0; zIdent[j]; j++){ @@ -85273,6 +85637,31 @@ static char *createTableStmt(sqlite3 *db, Table *p){ return zStmt; } +/* +** Resize an Index object to hold N columns total. Return SQLITE_OK +** on success and SQLITE_NOMEM on an OOM error. +*/ +static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){ + char *zExtra; + int nByte; + if( pIdx->nColumn>=N ) return SQLITE_OK; + assert( pIdx->isResized==0 ); + nByte = (sizeof(char*) + sizeof(i16) + 1)*N; + zExtra = sqlite3DbMallocZero(db, nByte); + if( zExtra==0 ) return SQLITE_NOMEM; + memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); + pIdx->azColl = (char**)zExtra; + zExtra += sizeof(char*)*N; + memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn); + pIdx->aiColumn = (i16*)zExtra; + zExtra += sizeof(i16)*N; + memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn); + pIdx->aSortOrder = (u8*)zExtra; + pIdx->nColumn = N; + pIdx->isResized = 1; + return SQLITE_OK; +} + /* ** Estimate the total row width for a table. */ @@ -85291,16 +85680,148 @@ static void estimateTableWidth(Table *pTab){ ** Estimate the average size of a row for an index. */ static void estimateIndexWidth(Index *pIdx){ - unsigned wIndex = 1; + unsigned wIndex = 0; int i; const Column *aCol = pIdx->pTable->aCol; for(i=0; inColumn; i++){ - assert( pIdx->aiColumn[i]>=0 && pIdx->aiColumn[i]pTable->nCol ); - wIndex += aCol[pIdx->aiColumn[i]].szEst; + i16 x = pIdx->aiColumn[i]; + assert( xpTable->nCol ); + wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst; } pIdx->szIdxRow = sqlite3LogEst(wIndex*4); } +/* Return true if value x is found any of the first nCol entries of aiCol[] +*/ +static int hasColumn(const i16 *aiCol, int nCol, int x){ + while( nCol-- > 0 ) if( x==*(aiCol++) ) return 1; + return 0; +} + +/* +** This routine runs at the end of parsing a CREATE TABLE statement that +** has a WITHOUT ROWID clause. The job of this routine is to convert both +** internal schema data structures and the generated VDBE code so that they +** are appropriate for a WITHOUT ROWID table instead of a rowid table. +** Changes include: +** +** (1) Convert the OP_CreateTable into an OP_CreateIndex. There is +** no rowid btree for a WITHOUT ROWID. Instead, the canonical +** data storage is a covering index btree. +** (2) Bypass the creation of the sqlite_master table entry +** for the PRIMARY KEY as the the primary key index is now +** identified by the sqlite_master table entry of the table itself. +** (3) Set the Index.tnum of the PRIMARY KEY Index object in the +** schema to the rootpage from the main table. +** (4) Set all columns of the PRIMARY KEY schema object to be NOT NULL. +** (5) Add all table columns to the PRIMARY KEY Index object +** so that the PRIMARY KEY is a covering index. The surplus +** columns are part of KeyInfo.nXField and are not used for +** sorting or lookup or uniqueness checks. +** (6) Replace the rowid tail on all automatically generated UNIQUE +** indices with the PRIMARY KEY columns. +*/ +static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ + Index *pIdx; + Index *pPk; + int nPk; + int i, j; + sqlite3 *db = pParse->db; + Vdbe *v = pParse->pVdbe; + + /* Convert the OP_CreateTable opcode that would normally create the + ** root-page for the table into a OP_CreateIndex opcode. The index + ** created will become the PRIMARY KEY index. + */ + if( pParse->addrCrTab ){ + assert( v ); + sqlite3VdbeGetOp(v, pParse->addrCrTab)->opcode = OP_CreateIndex; + } + + /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master + ** table entry. + */ + if( pParse->addrSkipPK ){ + assert( v ); + sqlite3VdbeGetOp(v, pParse->addrSkipPK)->opcode = OP_Goto; + } + + /* Locate the PRIMARY KEY index. Or, if this table was originally + ** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index. + */ + if( pTab->iPKey>=0 ){ + ExprList *pList; + pList = sqlite3ExprListAppend(pParse, 0, 0); + if( pList==0 ) return; + pList->a[0].zName = sqlite3DbStrDup(pParse->db, + pTab->aCol[pTab->iPKey].zName); + pList->a[0].sortOrder = pParse->iPkSortOrder; + assert( pParse->pNewTable==pTab ); + pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0); + if( pPk==0 ) return; + pPk->autoIndex = 2; + pTab->iPKey = -1; + }else{ + pPk = sqlite3PrimaryKeyIndex(pTab); + } + pPk->isCovering = 1; + assert( pPk!=0 ); + nPk = pPk->nKeyCol; + + /* Make sure every column of the PRIMARY KEY is NOT NULL */ + for(i=0; iaCol[pPk->aiColumn[i]].notNull = 1; + } + pPk->uniqNotNull = 1; + + /* The root page of the PRIMARY KEY is the table root page */ + pPk->tnum = pTab->tnum; + + /* Update the in-memory representation of all UNIQUE indices by converting + ** the final rowid column into one or more columns of the PRIMARY KEY. + */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int n; + if( pIdx->autoIndex==2 ) continue; + for(i=n=0; iaiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++; + } + if( n==0 ){ + /* This index is a superset of the primary key */ + pIdx->nColumn = pIdx->nKeyCol; + continue; + } + if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return; + for(i=0, j=pIdx->nKeyCol; iaiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ){ + pIdx->aiColumn[j] = pPk->aiColumn[i]; + pIdx->azColl[j] = pPk->azColl[i]; + j++; + } + } + assert( pIdx->nColumn>=pIdx->nKeyCol+n ); + assert( pIdx->nColumn>=j ); + } + + /* Add all table columns to the PRIMARY KEY index + */ + if( nPknCol ){ + if( resizeIndexObject(db, pPk, pTab->nCol) ) return; + for(i=0, j=nPk; inCol; i++){ + if( !hasColumn(pPk->aiColumn, j, i) ){ + assert( jnColumn ); + pPk->aiColumn[j] = i; + pPk->azColl[j] = "BINARY"; + j++; + } + } + assert( pPk->nColumn==j ); + assert( pTab->nCol==j ); + }else{ + pPk->nColumn = pTab->nCol; + } +} + /* ** This routine is called to report the final ")" that terminates ** a CREATE TABLE statement. @@ -85324,7 +85845,8 @@ static void estimateIndexWidth(Index *pIdx){ SQLITE_PRIVATE void sqlite3EndTable( Parse *pParse, /* Parse context */ Token *pCons, /* The ',' token after the last column defn. */ - Token *pEnd, /* The final ')' token in the CREATE TABLE */ + Token *pEnd, /* The ')' before options in the CREATE TABLE */ + u8 tabOpts, /* Extra table options. Usually 0. */ Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; /* The new table */ @@ -85340,6 +85862,31 @@ SQLITE_PRIVATE void sqlite3EndTable( assert( !db->init.busy || !pSelect ); + /* If the db->init.busy is 1 it means we are reading the SQL off the + ** "sqlite_master" or "sqlite_temp_master" table on the disk. + ** So do not write to the disk again. Extract the root page number + ** for the table from the db->init.newTnum field. (The page number + ** should have been put there by the sqliteOpenCb routine.) + */ + if( db->init.busy ){ + p->tnum = db->init.newTnum; + } + + /* Special processing for WITHOUT ROWID Tables */ + if( tabOpts & TF_WithoutRowid ){ + if( (p->tabFlags & TF_Autoincrement) ){ + sqlite3ErrorMsg(pParse, + "AUTOINCREMENT not allowed on WITHOUT ROWID tables"); + return; + } + if( (p->tabFlags & TF_HasPrimaryKey)==0 ){ + sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName); + }else{ + p->tabFlags |= TF_WithoutRowid; + convertToWithoutRowidTable(pParse, p); + } + } + iDb = sqlite3SchemaToIndex(db, p->pSchema); #ifndef SQLITE_OMIT_CHECK @@ -85356,16 +85903,6 @@ SQLITE_PRIVATE void sqlite3EndTable( estimateIndexWidth(pIdx); } - /* If the db->init.busy is 1 it means we are reading the SQL off the - ** "sqlite_master" or "sqlite_temp_master" table on the disk. - ** So do not write to the disk again. Extract the root page number - ** for the table from the db->init.newTnum field. (The page number - ** should have been put there by the sqliteOpenCb routine.) - */ - if( db->init.busy ){ - p->tnum = db->init.newTnum; - } - /* If not initializing, then create a record for the new table ** in the SQLITE_MASTER table of the database. ** @@ -85439,7 +85976,9 @@ SQLITE_PRIVATE void sqlite3EndTable( if( pSelect ){ zStmt = createTableStmt(db, p); }else{ - n = (int)(pEnd->z - pParse->sNameToken.z) + 1; + Token *pEnd2 = tabOpts ? &pParse->sLastToken : pEnd; + n = (int)(pEnd2->z - pParse->sNameToken.z); + if( pEnd2->z[0]!=';' ) n += pEnd2->n; zStmt = sqlite3MPrintf(db, "CREATE %s %.*s", zType2, n, pParse->sNameToken.z ); @@ -85482,7 +86021,7 @@ SQLITE_PRIVATE void sqlite3EndTable( /* Reparse everything to update our internal data structures */ sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlite3MPrintf(db, "tbl_name='%q'", p->zName)); + sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName)); } @@ -85587,7 +86126,7 @@ SQLITE_PRIVATE void sqlite3CreateView( sEnd.n = 1; /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */ - sqlite3EndTable(pParse, 0, &sEnd, 0); + sqlite3EndTable(pParse, 0, &sEnd, 0, 0); return; } #endif /* SQLITE_OMIT_VIEW */ @@ -86042,8 +86581,8 @@ exit_drop_table: ** currently under construction. pFromCol determines which columns ** in the current table point to the foreign key. If pFromCol==0 then ** connect the key to the last column inserted. pTo is the name of -** the table referred to. pToCol is a list of tables in the other -** pTo table that the foreign key points to. flags contains all +** the table referred to (a.k.a the "parent" table). pToCol is a list +** of tables in the parent pTo table. flags contains all ** information about the conflict resolution algorithms specified ** in the ON DELETE, ON UPDATE and ON INSERT clauses. ** @@ -86226,16 +86765,13 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ tnum = memRootPage; }else{ tnum = pIndex->tnum; - sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); } - pKey = sqlite3IndexKeyinfo(pParse, pIndex); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, - (char *)pKey, P4_KEYINFO_HANDOFF); - sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); + pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); /* Open the sorter cursor if we are to use one. */ iSorter = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO); + sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*) + sqlite3KeyInfoRef(pKey), P4_KEYINFO); /* Open the table. Loop through all rows of the table, inserting index ** records into the sorter. */ @@ -86243,20 +86779,25 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); regRecord = sqlite3GetTempReg(pParse); - sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1, &iPartIdxLabel); + sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); sqlite3VdbeResolveLabel(v, iPartIdxLabel); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); sqlite3VdbeJumpHere(v, addr1); + if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); + sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, + (char *)pKey, P4_KEYINFO); + sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); + addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); - if( pIndex->onError!=OE_None ){ + assert( pKey!=0 || db->mallocFailed || pParse->nErr ); + if( pIndex->onError!=OE_None && pKey!=0 ){ int j2 = sqlite3VdbeCurrentAddr(v) + 3; sqlite3VdbeAddOp2(v, OP_Goto, 0, j2); addr2 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE, - OE_Abort, "indexed columns are not unique", P4_STATIC - ); + sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord, + pKey->nField - pIndex->nKeyCol); + sqlite3UniqueConstraint(pParse, OE_Abort, pIndex); }else{ addr2 = sqlite3VdbeCurrentAddr(v); } @@ -86272,6 +86813,41 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ sqlite3VdbeAddOp1(v, OP_Close, iSorter); } +/* +** Allocate heap space to hold an Index object with nCol columns. +** +** Increase the allocation size to provide an extra nExtra bytes +** of 8-byte aligned space after the Index object and return a +** pointer to this extra space in *ppExtra. +*/ +SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( + sqlite3 *db, /* Database connection */ + i16 nCol, /* Total number of columns in the index */ + int nExtra, /* Number of bytes of extra space to alloc */ + char **ppExtra /* Pointer to the "extra" space */ +){ + Index *p; /* Allocated index object */ + int nByte; /* Bytes of space for Index object + arrays */ + + nByte = ROUND8(sizeof(Index)) + /* Index structure */ + ROUND8(sizeof(char*)*nCol) + /* Index.azColl */ + ROUND8(sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */ + sizeof(i16)*nCol + /* Index.aiColumn */ + sizeof(u8)*nCol); /* Index.aSortOrder */ + p = sqlite3DbMallocZero(db, nByte + nExtra); + if( p ){ + char *pExtra = ((char*)p)+ROUND8(sizeof(Index)); + p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol); + p->aiRowEst = (tRowcnt*)pExtra; pExtra += sizeof(tRowcnt)*(nCol+1); + p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol; + p->aSortOrder = (u8*)pExtra; + p->nColumn = nCol; + p->nKeyCol = nCol - 1; + *ppExtra = ((char*)p) + nByte; + } + return p; +} + /* ** Create a new index for an SQL table. pName1.pName2 is the name of the index ** and pTblList is the name of the table that is to be indexed. Both will @@ -86306,7 +86882,6 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( char *zName = 0; /* Name of the index */ int nName; /* Number of characters in zName */ int i, j; - Token nullId; /* Fake token for an empty ID list */ DbFixer sFix; /* For assigning database names to pTable */ int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */ sqlite3 *db = pParse->db; @@ -86315,9 +86890,10 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( Token *pName = 0; /* Unqualified name of the index to create */ struct ExprList_item *pListItem; /* For looping over pList */ const Column *pTabCol; /* A column in the table */ - int nCol; /* Number of columns */ int nExtra = 0; /* Space allocated for zExtra[] */ - char *zExtra; /* Extra space after the Index object */ + int nExtraCol; /* Number of extra columns needed */ + char *zExtra = 0; /* Extra space after the Index object */ + Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ assert( pParse->nErr==0 ); /* Never called with prior errors */ if( db->mallocFailed || IN_DECLARE_VTAB ){ @@ -86369,6 +86945,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( pTab->zName); goto exit_create_index; } + if( !HasRowid(pTab) ) pPk = sqlite3PrimaryKeyIndex(pTab); }else{ assert( pName==0 ); assert( pStart==0 ); @@ -86464,11 +87041,10 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( ** So create a fake list to simulate this. */ if( pList==0 ){ - nullId.z = pTab->aCol[pTab->nCol-1].zName; - nullId.n = sqlite3Strlen30((char*)nullId.z); pList = sqlite3ExprListAppend(pParse, 0, 0); if( pList==0 ) goto exit_create_index; - sqlite3ExprListSetName(pParse, pList, &nullId, 0); + pList->a[0].zName = sqlite3DbStrDup(pParse->db, + pTab->aCol[pTab->nCol-1].zName); pList->a[0].sortOrder = (u8)sortOrder; } @@ -86487,36 +87063,23 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( ** Allocate the index structure. */ nName = sqlite3Strlen30(zName); - nCol = pList->nExpr; - pIndex = sqlite3DbMallocZero(db, - ROUND8(sizeof(Index)) + /* Index structure */ - ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */ - sizeof(char *)*nCol + /* Index.azColl */ - sizeof(int)*nCol + /* Index.aiColumn */ - sizeof(u8)*nCol + /* Index.aSortOrder */ - nName + 1 + /* Index.zName */ - nExtra /* Collation sequence names */ - ); + nExtraCol = pPk ? pPk->nKeyCol : 1; + pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol, + nName + nExtra + 1, &zExtra); if( db->mallocFailed ){ goto exit_create_index; } - zExtra = (char*)pIndex; - pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))]; - pIndex->azColl = (char**) - ((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1)); assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) ); assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) ); - pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]); - pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]); - pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]); - zExtra = (char *)(&pIndex->zName[nName+1]); + pIndex->zName = zExtra; + zExtra += nName + 1; memcpy(pIndex->zName, zName, nName+1); pIndex->pTable = pTab; - pIndex->nColumn = pList->nExpr; pIndex->onError = (u8)onError; - pIndex->uniqNotNull = onError==OE_Abort; + pIndex->uniqNotNull = onError!=OE_None; pIndex->autoIndex = (u8)(pName==0); pIndex->pSchema = db->aDb[iDb].pSchema; + pIndex->nKeyCol = pList->nExpr; if( pPIWhere ){ sqlite3ResolveSelfReference(pParse, pTab, NC_PartIdx, pPIWhere, 0); pIndex->pPartIdxWhere = pPIWhere; @@ -86556,7 +87119,8 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( pParse->checkSchema = 1; goto exit_create_index; } - pIndex->aiColumn[i] = j; + assert( pTab->nCol<=0x7fff && j<=0x7fff ); + pIndex->aiColumn[i] = (i16)j; if( pListItem->pExpr ){ int nColl; assert( pListItem->pExpr->op==TK_COLLATE ); @@ -86579,6 +87143,23 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( pIndex->aSortOrder[i] = (u8)requestedSortOrder; if( pTab->aCol[j].notNull==0 ) pIndex->uniqNotNull = 0; } + if( pPk ){ + for(j=0; jnKeyCol; j++){ + int x = pPk->aiColumn[j]; + if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){ + pIndex->nColumn--; + }else{ + pIndex->aiColumn[i] = x; + pIndex->azColl[i] = pPk->azColl[j]; + pIndex->aSortOrder[i] = pPk->aSortOrder[j]; + i++; + } + } + assert( i==pIndex->nColumn ); + }else{ + pIndex->aiColumn[i] = -1; + pIndex->azColl[i] = "BINARY"; + } sqlite3DefaultRowEst(pIndex); if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex); @@ -86611,8 +87192,8 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( assert( pIdx->autoIndex ); assert( pIndex->onError!=OE_None ); - if( pIdx->nColumn!=pIndex->nColumn ) continue; - for(k=0; knColumn; k++){ + if( pIdx->nKeyCol!=pIndex->nKeyCol ) continue; + for(k=0; knKeyCol; k++){ const char *z1; const char *z2; if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break; @@ -86620,7 +87201,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( z2 = pIndex->azColl[k]; if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break; } - if( k==pIdx->nColumn ){ + if( k==pIdx->nKeyCol ){ if( pIdx->onError!=pIndex->onError ){ /* This constraint creates the same index as a previous ** constraint specified somewhere in the CREATE TABLE statement. @@ -86662,22 +87243,20 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( } } - /* If the db->init.busy is 0 then create the index on disk. This - ** involves writing the index into the master table and filling in the - ** index with the current table contents. + /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the + ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then + ** emit code to allocate the index rootpage on disk and make an entry for + ** the index in the sqlite_master table and populate the index with + ** content. But, do not do this if we are simply reading the sqlite_master + ** table to parse the schema, or if this index is the PRIMARY KEY index + ** of a WITHOUT ROWID table. ** - ** The db->init.busy is 0 when the user first enters a CREATE INDEX - ** command. db->init.busy is 1 when a database is opened and - ** CREATE INDEX statements are read out of the master table. In - ** the latter case the index already exists on disk, which is why - ** we don't want to recreate it. - ** - ** If pTblName==0 it means this index is generated as a primary key - ** or UNIQUE constraint of a CREATE TABLE statement. Since the table + ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY + ** or UNIQUE index in a CREATE TABLE statement. Since the table ** has just been created, it contains no data and the index initialization ** step can be skipped. */ - else if( pParse->nErr==0 ){ + else if( pParse->nErr==0 && (HasRowid(pTab) || pTblName!=0) ){ Vdbe *v; char *zStmt; int iMem = ++pParse->nMem; @@ -86789,12 +87368,12 @@ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ a[0] = pIdx->pTable->nRowEst; if( a[0]<10 ) a[0] = 10; n = 10; - for(i=1; i<=pIdx->nColumn; i++){ + for(i=1; i<=pIdx->nKeyCol; i++){ a[i] = n; if( n>5 ) n--; } if( pIdx->onError!=OE_None ){ - a[pIdx->nColumn] = 1; + a[pIdx->nKeyCol] = 1; } } @@ -87482,7 +88061,8 @@ SQLITE_PRIVATE void sqlite3HaltConstraint( int errCode, /* extended error code */ int onError, /* Constraint type */ char *p4, /* Error message */ - int p4type /* P4_STATIC or P4_TRANSIENT */ + i8 p4type, /* P4_STATIC or P4_TRANSIENT */ + u8 p5Errmsg /* P5_ErrMsg type */ ){ Vdbe *v = sqlite3GetVdbe(pParse); assert( (errCode&0xff)==SQLITE_CONSTRAINT ); @@ -87490,6 +88070,58 @@ SQLITE_PRIVATE void sqlite3HaltConstraint( sqlite3MayAbort(pParse); } sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type); + if( p5Errmsg ) sqlite3VdbeChangeP5(v, p5Errmsg); +} + +/* +** Code an OP_Halt due to UNIQUE or PRIMARY KEY constraint violation. +*/ +SQLITE_PRIVATE void sqlite3UniqueConstraint( + Parse *pParse, /* Parsing context */ + int onError, /* Constraint type */ + Index *pIdx /* The index that triggers the constraint */ +){ + char *zErr; + int j; + StrAccum errMsg; + Table *pTab = pIdx->pTable; + + sqlite3StrAccumInit(&errMsg, 0, 0, 200); + errMsg.db = pParse->db; + for(j=0; jnKeyCol; j++){ + char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; + if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2); + sqlite3StrAccumAppendAll(&errMsg, pTab->zName); + sqlite3StrAccumAppend(&errMsg, ".", 1); + sqlite3StrAccumAppendAll(&errMsg, zCol); + } + zErr = sqlite3StrAccumFinish(&errMsg); + sqlite3HaltConstraint(pParse, + (pIdx->autoIndex==2)?SQLITE_CONSTRAINT_PRIMARYKEY:SQLITE_CONSTRAINT_UNIQUE, + onError, zErr, P4_DYNAMIC, P5_ConstraintUnique); +} + + +/* +** Code an OP_Halt due to non-unique rowid. +*/ +SQLITE_PRIVATE void sqlite3RowidConstraint( + Parse *pParse, /* Parsing context */ + int onError, /* Conflict resolution algorithm */ + Table *pTab /* The table with the non-unique rowid */ +){ + char *zMsg; + int rc; + if( pTab->iPKey>=0 ){ + zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName, + pTab->aCol[pTab->iPKey].zName); + rc = SQLITE_CONSTRAINT_PRIMARYKEY; + }else{ + zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName); + rc = SQLITE_CONSTRAINT_ROWID; + } + sqlite3HaltConstraint(pParse, rc, onError, zMsg, P4_DYNAMIC, + P5_ConstraintUnique); } /* @@ -87502,8 +88134,8 @@ static int collationMatch(const char *zColl, Index *pIndex){ assert( zColl!=0 ); for(i=0; inColumn; i++){ const char *z = pIndex->azColl[i]; - assert( z!=0 ); - if( 0==sqlite3StrICmp(z, zColl) ){ + assert( z!=0 || pIndex->aiColumn[i]<0 ); + if( pIndex->aiColumn[i]>=0 && 0==sqlite3StrICmp(z, zColl) ){ return 1; } } @@ -87622,37 +88254,122 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ #endif /* -** Return a dynamicly allocated KeyInfo structure that can be used -** with OP_OpenRead or OP_OpenWrite to access database index pIdx. +** Return a KeyInfo structure that is appropriate for the given Index. ** -** If successful, a pointer to the new structure is returned. In this case -** the caller is responsible for calling sqlite3DbFree(db, ) on the returned -** pointer. If an error occurs (out of memory or missing collation -** sequence), NULL is returned and the state of pParse updated to reflect -** the error. +** The KeyInfo structure for an index is cached in the Index object. +** So there might be multiple references to the returned pointer. The +** caller should not try to modify the KeyInfo object. +** +** The caller should invoke sqlite3KeyInfoUnref() on the returned object +** when it has finished using it. */ -SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){ - int i; - int nCol = pIdx->nColumn; - KeyInfo *pKey; +SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ + if( pParse->nErr ) return 0; +#ifndef SQLITE_OMIT_SHARED_CACHE + if( pIdx->pKeyInfo && pIdx->pKeyInfo->db!=pParse->db ){ + sqlite3KeyInfoUnref(pIdx->pKeyInfo); + pIdx->pKeyInfo = 0; + } +#endif + if( pIdx->pKeyInfo==0 ){ + int i; + int nCol = pIdx->nColumn; + int nKey = pIdx->nKeyCol; + KeyInfo *pKey; + if( pIdx->uniqNotNull ){ + pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); + }else{ + pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); + } + if( pKey ){ + assert( sqlite3KeyInfoIsWriteable(pKey) ); + for(i=0; iazColl[i]; + assert( zColl!=0 ); + pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 : + sqlite3LocateCollSeq(pParse, zColl); + pKey->aSortOrder[i] = pIdx->aSortOrder[i]; + } + if( pParse->nErr ){ + sqlite3KeyInfoUnref(pKey); + }else{ + pIdx->pKeyInfo = pKey; + } + } + } + return sqlite3KeyInfoRef(pIdx->pKeyInfo); +} - pKey = sqlite3KeyInfoAlloc(pParse->db, nCol); - if( pKey ){ - for(i=0; iazColl[i]; - assert( zColl ); - pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl); - pKey->aSortOrder[i] = pIdx->aSortOrder[i]; +#ifndef SQLITE_OMIT_CTE +/* +** This routine is invoked once per CTE by the parser while parsing a +** WITH clause. +*/ +SQLITE_PRIVATE With *sqlite3WithAdd( + Parse *pParse, /* Parsing context */ + With *pWith, /* Existing WITH clause, or NULL */ + Token *pName, /* Name of the common-table */ + ExprList *pArglist, /* Optional column name list for the table */ + Select *pQuery /* Query used to initialize the table */ +){ + sqlite3 *db = pParse->db; + With *pNew; + char *zName; + + /* Check that the CTE name is unique within this WITH clause. If + ** not, store an error in the Parse structure. */ + zName = sqlite3NameFromToken(pParse->db, pName); + if( zName && pWith ){ + int i; + for(i=0; inCte; i++){ + if( sqlite3StrICmp(zName, pWith->a[i].zName)==0 ){ + sqlite3ErrorMsg(pParse, "duplicate WITH table name: %s", zName); + } } } - if( pParse->nErr ){ - sqlite3DbFree(pParse->db, pKey); - pKey = 0; + if( pWith ){ + int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); + pNew = sqlite3DbRealloc(db, pWith, nByte); + }else{ + pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); } - return pKey; + assert( zName!=0 || pNew==0 ); + assert( db->mallocFailed==0 || pNew==0 ); + + if( pNew==0 ){ + sqlite3ExprListDelete(db, pArglist); + sqlite3SelectDelete(db, pQuery); + sqlite3DbFree(db, zName); + pNew = pWith; + }else{ + pNew->a[pNew->nCte].pSelect = pQuery; + pNew->a[pNew->nCte].pCols = pArglist; + pNew->a[pNew->nCte].zName = zName; + pNew->a[pNew->nCte].zErr = 0; + pNew->nCte++; + } + + return pNew; } +/* +** Free the contents of the With object passed as the second argument. +*/ +SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){ + if( pWith ){ + int i; + for(i=0; inCte; i++){ + struct Cte *pCte = &pWith->a[i]; + sqlite3ExprListDelete(db, pCte->pCols); + sqlite3SelectDelete(db, pCte->pSelect); + sqlite3DbFree(db, pCte->zName); + } + sqlite3DbFree(db, pWith); + } +} +#endif /* !defined(SQLITE_OMIT_CTE) */ + /************** End of build.c ***********************************************/ /************** Begin file callback.c ****************************************/ /* @@ -88013,7 +88730,6 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction( assert( nArg>=(-2) ); assert( nArg>=(-1) || createFlag==0 ); - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a); /* First search for a match amongst the application-defined functions. @@ -88270,7 +88986,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( ExprList *pOrderBy, /* The ORDER BY clause. May be null */ Expr *pLimit, /* The LIMIT clause. May be null */ Expr *pOffset, /* The OFFSET clause. May be null */ - char *zStmtType /* Either DELETE or UPDATE. For error messages. */ + char *zStmtType /* Either DELETE or UPDATE. For err msgs. */ ){ Expr *pWhereRowid = NULL; /* WHERE rowid .. */ Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */ @@ -88345,7 +89061,8 @@ limit_where_cleanup_2: sqlite3ExprDelete(pParse->db, pOffset); return 0; } -#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ +#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */ + /* && !defined(SQLITE_OMIT_SUBQUERY) */ /* ** Generate code for a DELETE FROM statement. @@ -88362,18 +89079,34 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( Vdbe *v; /* The virtual database engine */ Table *pTab; /* The table from which records will be deleted */ const char *zDb; /* Name of database holding pTab */ - int end, addr = 0; /* A couple addresses of generated code */ int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ - int iCur; /* VDBE Cursor number for pTab */ + int iTabCur; /* Cursor number for the table */ + int iDataCur; /* VDBE cursor for the canonical data source */ + int iIdxCur; /* Cursor number of the first index */ + int nIdx; /* Number of indices */ sqlite3 *db; /* Main database structure */ AuthContext sContext; /* Authorization context */ NameContext sNC; /* Name context to resolve expressions in */ int iDb; /* Database number */ int memCnt = -1; /* Memory cell used for change counting */ int rcauth; /* Value returned by authorization callback */ - + int okOnePass; /* True for one-pass algorithm without the FIFO */ + int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */ + u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */ + Index *pPk; /* The PRIMARY KEY index on the table */ + int iPk = 0; /* First of nPk registers holding PRIMARY KEY value */ + i16 nPk = 1; /* Number of columns in the PRIMARY KEY */ + int iKey; /* Memory cell holding key of row to be deleted */ + i16 nKey; /* Number of memory cells in the row key */ + int iEphCur = 0; /* Ephemeral table holding all primary key values */ + int iRowSet = 0; /* Register for rowset of rows to delete */ + int addrBypass = 0; /* Address of jump over the delete logic */ + int addrLoop = 0; /* Top of the delete loop */ + int addrDelete = 0; /* Jump directly to the delete logic */ + int addrEphOpen = 0; /* Instruction to open the Ephermeral table */ + #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ Trigger *pTrigger; /* List of table triggers, if required */ @@ -88428,11 +89161,11 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( } assert(!isView || pTrigger); - /* Assign cursor number to the table and all its indices. + /* Assign cursor numbers to the table and all its indices. */ assert( pTabList->nSrc==1 ); - iCur = pTabList->a[0].iCursor = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + iTabCur = pTabList->a[0].iCursor = pParse->nTab++; + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ pParse->nTab++; } @@ -88456,7 +89189,8 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( */ #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) if( isView ){ - sqlite3MaterializeView(pParse, pTab, pWhere, iCur); + sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur); + iDataCur = iIdxCur = iTabCur; } #endif @@ -88487,78 +89221,168 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( ){ assert( !isView ); sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); - sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, - pTab->zName, P4_STATIC); + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, + pTab->zName, P4_STATIC); + } for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); } }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ - /* The usual case: There is a WHERE clause so we have to scan through - ** the table and pick which records to delete. - */ { - int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ - int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ - int regRowid; /* Actual register containing rowids */ - - /* Collect rowids of every row to be deleted. + if( HasRowid(pTab) ){ + /* For a rowid table, initialize the RowSet to an empty set */ + pPk = 0; + nPk = 1; + iRowSet = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); + }else{ + /* For a WITHOUT ROWID table, create an ephermeral table used to + ** hold all primary keys for rows to be deleted. */ + pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); + nPk = pPk->nKeyCol; + iPk = pParse->nMem+1; + pParse->nMem += nPk; + iEphCur = pParse->nTab++; + addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); + } + + /* Construct a query to find the rowid or primary key for every row + ** to be deleted, based on the WHERE clause. */ - sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); - pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0 - ); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, + WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK, + iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; - regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0); - sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); + okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + + /* Keep track of the number of rows to be deleted */ if( db->flags & SQLITE_CountRows ){ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } + + /* Extract the rowid or primary key for the current row */ + if( pPk ){ + for(i=0; iaiColumn[i], iPk+i); + } + iKey = iPk; + }else{ + iKey = pParse->nMem + 1; + iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0); + if( iKey>pParse->nMem ) pParse->nMem = iKey; + } + + if( okOnePass ){ + /* For ONEPASS, no need to store the rowid/primary-key. There is only + ** one, so just keep it in its register(s) and fall through to the + ** delete code. + */ + nKey = nPk; /* OP_Found will use an unpacked key */ + aToOpen = sqlite3DbMallocRaw(db, nIdx+2); + if( aToOpen==0 ){ + sqlite3WhereEnd(pWInfo); + goto delete_from_cleanup; + } + memset(aToOpen, 1, nIdx+1); + aToOpen[nIdx+1] = 0; + if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0; + if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0; + if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen); + addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */ + }else if( pPk ){ + /* Construct a composite key for the row to be deleted and remember it */ + iKey = ++pParse->nMem; + nKey = 0; /* Zero tells OP_Found to use a composite key */ + sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, + sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey); + }else{ + /* Get the rowid of the row to be deleted and remember it in the RowSet */ + nKey = 1; /* OP_Seek always uses a single rowid */ + sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); + } + + /* End of the WHERE loop */ sqlite3WhereEnd(pWInfo); - - /* Delete every item whose key was written to the list during the - ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. */ - end = sqlite3VdbeMakeLabel(v); - + if( okOnePass ){ + /* Bypass the delete logic below if the WHERE loop found zero rows */ + addrBypass = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBypass); + sqlite3VdbeJumpHere(v, addrDelete); + } + /* Unless this is a view, open cursors for the table we are ** deleting from and all its indices. If this is a view, then the ** only effect this statement has is to fire the INSTEAD OF - ** triggers. */ + ** triggers. + */ if( !isView ){ - sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen, + &iDataCur, &iIdxCur); + assert( pPk || iDataCur==iTabCur ); + assert( pPk || iIdxCur==iDataCur+1 ); } - - addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - + + /* Set up a loop over the rowids/primary-keys that were found in the + ** where-clause loop above. + */ + if( okOnePass ){ + /* Just one row. Hence the top-of-loop is a no-op */ + assert( nKey==nPk ); /* OP_Found will use an unpacked key */ + if( aToOpen[iDataCur-iTabCur] ){ + assert( pPk!=0 ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); + } + }else if( pPk ){ + addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); + sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey); + assert( nKey==0 ); /* OP_Found will use a composite key */ + }else{ + addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey); + assert( nKey==1 ); + } + /* Delete the row */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, OE_Abort); sqlite3MayAbort(pParse); }else #endif { int count = (pParse->nested==0); /* True to count changes */ - sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default); + sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, + iKey, nKey, count, OE_Default, okOnePass); } - - /* End of the delete loop */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); - sqlite3VdbeResolveLabel(v, end); - + + /* End of the loop over all rowids/primary-keys. */ + if( okOnePass ){ + sqlite3VdbeResolveLabel(v, addrBypass); + }else if( pPk ){ + sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); + sqlite3VdbeJumpHere(v, addrLoop); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop); + sqlite3VdbeJumpHere(v, addrLoop); + } + /* Close the cursors open on the table and its indexes. */ if( !isView && !IsVirtual(pTab) ){ - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum); + if( !pPk ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur); + for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i); } - sqlite3VdbeAddOp1(v, OP_Close, iCur); } - } + } /* End non-truncate path */ /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into @@ -88582,6 +89406,7 @@ delete_from_cleanup: sqlite3AuthContextPop(&sContext); sqlite3SrcListDelete(db, pTabList); sqlite3ExprDelete(db, pWhere); + sqlite3DbFree(db, aToOpen); return; } /* Make sure "isView" and other macros defined above are undefined. Otherwise @@ -88596,50 +89421,59 @@ delete_from_cleanup: /* ** This routine generates VDBE code that causes a single row of a -** single table to be deleted. +** single table to be deleted. Both the original table entry and +** all indices are removed. ** -** The VDBE must be in a particular state when this routine is called. -** These are the requirements: +** Preconditions: ** -** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number $iCur. +** 1. iDataCur is an open cursor on the btree that is the canonical data +** store for the table. (This will be either the table itself, +** in the case of a rowid table, or the PRIMARY KEY index in the case +** of a WITHOUT ROWID table.) ** ** 2. Read/write cursors for all indices of pTab must be open as -** cursor number base+i for the i-th index. +** cursor number iIdxCur+i for the i-th index. ** -** 3. The record number of the row to be deleted must be stored in -** memory cell iRowid. -** -** This routine generates code to remove both the table record and all -** index entries that point to that record. +** 3. The primary key for the row to be deleted must be stored in a +** sequence of nPk memory cells starting at iPk. If nPk==0 that means +** that a search record formed from OP_MakeRecord is contained in the +** single memory location iPk. */ SQLITE_PRIVATE void sqlite3GenerateRowDelete( Parse *pParse, /* Parsing context */ Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ - int iRowid, /* Memory cell that contains the rowid to delete */ - int count, /* If non-zero, increment the row change counter */ Trigger *pTrigger, /* List of triggers to (potentially) fire */ - int onconf /* Default ON CONFLICT policy for triggers */ + int iDataCur, /* Cursor from which column data is extracted */ + int iIdxCur, /* First index cursor */ + int iPk, /* First memory cell containing the PRIMARY KEY */ + i16 nPk, /* Number of PRIMARY KEY memory cells */ + u8 count, /* If non-zero, increment the row change counter */ + u8 onconf, /* Default ON CONFLICT policy for triggers */ + u8 bNoSeek /* iDataCur is already pointing to the row to delete */ ){ Vdbe *v = pParse->pVdbe; /* Vdbe */ int iOld = 0; /* First register in OLD.* array */ int iLabel; /* Label resolved to end of generated code */ + u8 opSeek; /* Seek opcode */ /* Vdbe is guaranteed to have been allocated by this stage. */ assert( v ); + VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)", + iDataCur, iIdxCur, iPk, (int)nPk)); /* Seek cursor iCur to the row to delete. If this row no longer exists ** (this can happen if a trigger program has already deleted it), do ** not attempt to delete it or fire any DELETE triggers. */ iLabel = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); + opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound; + if( !bNoSeek ) sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); /* If there are any triggers to fire, allocate a range of registers to ** use for the old.* references in the triggers. */ if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){ u32 mask; /* Mask of OLD.* columns in use */ int iCol; /* Iterator used while populating OLD.* */ + int addrStart; /* Start of BEFORE trigger programs */ /* TODO: Could use temporary registers here. Also could attempt to ** avoid copying the contents of the rowid register. */ @@ -88652,23 +89486,29 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( /* Populate the OLD.* pseudo-table register array. These values will be ** used by any BEFORE and AFTER triggers that exist. */ - sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld); + sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld); for(iCol=0; iColnCol; iCol++){ - if( mask==0xffffffff || mask&(1<pSelect==0 ){ - sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); - sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); + sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0); + sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); if( count ){ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } @@ -88701,49 +89541,64 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( ** trigger programs were invoked. Or if a trigger program throws a ** RAISE(IGNORE) exception. */ sqlite3VdbeResolveLabel(v, iLabel); + VdbeModuleComment((v, "END: GenRowDel()")); } /* ** This routine generates VDBE code that causes the deletion of all -** index entries associated with a single row of a single table. +** index entries associated with a single row of a single table, pTab ** -** The VDBE must be in a particular state when this routine is called. -** These are the requirements: +** Preconditions: ** -** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number "iCur". +** 1. A read/write cursor "iDataCur" must be open on the canonical storage +** btree for the table pTab. (This will be either the table itself +** for rowid tables or to the primary key index for WITHOUT ROWID +** tables.) ** ** 2. Read/write cursors for all indices of pTab must be open as -** cursor number iCur+i for the i-th index. +** cursor number iIdxCur+i for the i-th index. (The pTab->pIndex +** index is the 0-th index.) ** -** 3. The "iCur" cursor must be pointing to the row that is to be -** deleted. +** 3. The "iDataCur" cursor must be already be positioned on the row +** that is to be deleted. */ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete( Parse *pParse, /* Parsing and code generating context */ Table *pTab, /* Table containing the row to be deleted */ - int iCur, /* Cursor number for the table */ + int iDataCur, /* Cursor of table holding data. */ + int iIdxCur, /* First index cursor */ int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */ ){ - int i; - Index *pIdx; - int r1; - int iPartIdxLabel; - Vdbe *v = pParse->pVdbe; + int i; /* Index loop counter */ + int r1 = -1; /* Register holding an index key */ + int iPartIdxLabel; /* Jump destination for skipping partial index entries */ + Index *pIdx; /* Current index */ + Index *pPrior = 0; /* Prior index */ + Vdbe *v; /* The prepared statement under construction */ + Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */ - for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue; - r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0, &iPartIdxLabel); - sqlite3VdbeAddOp3(v, OP_IdxDelete, iCur+i, r1, pIdx->nColumn+1); + v = pParse->pVdbe; + pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); + for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + assert( iIdxCur+i!=iDataCur || pPk==pIdx ); + if( aRegIdx!=0 && aRegIdx[i]==0 ) continue; + if( pIdx==pPk ) continue; + VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName)); + r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, + &iPartIdxLabel, pPrior, r1); + sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, + pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); sqlite3VdbeResolveLabel(v, iPartIdxLabel); + pPrior = pIdx; } } /* -** Generate code that will assemble an index key and put it in register +** Generate code that will assemble an index key and stores it in register ** regOut. The key with be for index pIdx which is an index on pTab. ** iCur is the index of a cursor open on the pTab table and pointing to -** the entry that needs indexing. +** the entry that needs indexing. If pTab is a WITHOUT ROWID table, then +** iCur must be the cursor of the PRIMARY KEY index. ** ** Return a register number which is the first in a block of ** registers that holds the elements of the index key. The @@ -88756,14 +89611,27 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete( ** to false or null. If pIdx is not a partial index, *piPartIdxLabel ** will be set to zero which is an empty label that is ignored by ** sqlite3VdbeResolveLabel(). +** +** The pPrior and regPrior parameters are used to implement a cache to +** avoid unnecessary register loads. If pPrior is not NULL, then it is +** a pointer to a different index for which an index key has just been +** computed into register regPrior. If the current pIdx index is generating +** its key into the same sequence of registers and if pPrior and pIdx share +** a column in common, then the register corresponding to that column already +** holds the correct value and the loading of that register is skipped. +** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK +** on a table with multiple indices, and especially with the ROWID or +** PRIMARY KEY columns of the index. */ SQLITE_PRIVATE int sqlite3GenerateIndexKey( Parse *pParse, /* Parsing context */ Index *pIdx, /* The index for which to generate a key */ - int iCur, /* Cursor number for the pIdx->pTable table */ - int regOut, /* Write the new index key to this register */ - int doMakeRec, /* Run the OP_MakeRecord instruction if true */ - int *piPartIdxLabel /* OUT: Jump to this label to skip partial index */ + int iDataCur, /* Cursor number from which to take column data */ + int regOut, /* Put the new key into this register if not 0 */ + int prefixOnly, /* Compute only a unique prefix of the key */ + int *piPartIdxLabel, /* OUT: Jump to this label to skip partial index */ + Index *pPrior, /* Previously generated index key */ + int regPrior /* Register holding previous generated key */ ){ Vdbe *v = pParse->pVdbe; int j; @@ -88774,38 +89642,32 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey( if( piPartIdxLabel ){ if( pIdx->pPartIdxWhere ){ *piPartIdxLabel = sqlite3VdbeMakeLabel(v); - pParse->iPartIdxTab = iCur; + pParse->iPartIdxTab = iDataCur; sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, SQLITE_JUMPIFNULL); }else{ *piPartIdxLabel = 0; } } - nCol = pIdx->nColumn; - regBase = sqlite3GetTempRange(pParse, nCol+1); - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol); + nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn; + regBase = sqlite3GetTempRange(pParse, nCol); + if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0; for(j=0; jaiColumn[j]; - if( idx==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j); - }else{ - sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j); - sqlite3ColumnDefault(v, pTab, idx, -1); - } + if( pPrior && pPrior->aiColumn[j]==pIdx->aiColumn[j] ) continue; + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j], + regBase+j); + /* If the column affinity is REAL but the number is an integer, then it + ** might be stored in the table as an integer (using a compact + ** representation) then converted to REAL by an OP_RealAffinity opcode. + ** But we are getting ready to store this value back into an index, where + ** it should be converted by to INTEGER again. So omit the OP_RealAffinity + ** opcode if it is present */ + sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); } - if( doMakeRec ){ - const char *zAff; - if( pTab->pSelect - || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt) - ){ - zAff = 0; - }else{ - zAff = sqlite3IndexAffinityStr(v, pIdx); - } - sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); - sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); + if( regOut ){ + sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); } - sqlite3ReleaseTempRange(pParse, regBase, nCol+1); + sqlite3ReleaseTempRange(pParse, regBase, nCol); return regBase; } @@ -88948,9 +89810,9 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ case SQLITE_INTEGER: { i64 iVal = sqlite3_value_int64(argv[0]); if( iVal<0 ){ - if( (iVal<<1)==0 ){ - /* IMP: R-35460-15084 If X is the integer -9223372036854775807 then - ** abs(X) throws an integer overflow error since there is no + if( iVal==SMALLEST_INT64 ){ + /* IMP: R-31676-45509 If X is the integer -9223372036854775808 + ** then abs(X) throws an integer overflow error since there is no ** equivalent positive 64-bit two complement value. */ sqlite3_result_error(context, "integer overflow", -1); return; @@ -89029,6 +89891,32 @@ static void instrFunc( sqlite3_result_int(context, N); } +/* +** Implementation of the printf() function. +*/ +static void printfFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + PrintfArguments x; + StrAccum str; + const char *zFormat; + int n; + + if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){ + x.nArg = argc-1; + x.nUsed = 0; + x.apArg = argv+1; + sqlite3StrAccumInit(&str, 0, 0, SQLITE_MAX_LENGTH); + str.db = sqlite3_context_db_handle(context); + sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x); + n = str.nChar; + sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n, + SQLITE_DYNAMIC); + } +} + /* ** Implementation of the substr() function. ** @@ -90322,11 +91210,11 @@ static void groupConcatStep( zSep = ","; nSep = 1; } - sqlite3StrAccumAppend(pAccum, zSep, nSep); + if( nSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep); } zVal = (char*)sqlite3_value_text(argv[0]); nVal = sqlite3_value_bytes(argv[0]); - sqlite3StrAccumAppend(pAccum, zVal, nVal); + if( nVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal); } } static void groupConcatFinalize(sqlite3_context *context){ @@ -90459,6 +91347,7 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){ FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(substr, 2, 0, 0, substrFunc ), FUNCTION(substr, 3, 0, 0, substrFunc ), + FUNCTION(printf, -1, 0, 0, printfFunc ), FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), @@ -90475,8 +91364,8 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){ FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), - FUNCTION(random, 0, 0, 0, randomFunc ), - FUNCTION(randomblob, 1, 0, 0, randomBlob ), + VFUNCTION(random, 0, 0, 0, randomFunc ), + VFUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), @@ -90486,9 +91375,9 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){ FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ FUNCTION(quote, 1, 0, 0, quoteFunc ), - FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), - FUNCTION(changes, 0, 0, 0, changes ), - FUNCTION(total_changes, 0, 0, 0, total_changes ), + VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), + VFUNCTION(changes, 0, 0, 0, changes ), + VFUNCTION(total_changes, 0, 0, 0, total_changes ), FUNCTION(replace, 3, 0, 0, replaceFunc ), FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), #ifdef SQLITE_SOUNDEX @@ -90761,7 +91650,7 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex( } for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ + if( pIdx->nKeyCol==nCol && pIdx->onError!=OE_None ){ /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number ** of columns. If each indexed column corresponds to a foreign key ** column of pFKey, then this index is a winner. */ @@ -90784,7 +91673,7 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex( ** the default collation sequences for each column. */ int i, j; for(i=0; iaiColumn[i]; /* Index of column in parent tbl */ + i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */ char *zDfltColl; /* Def. collation for column */ char *zIdxCol; /* Name of indexed column */ @@ -90915,10 +91804,9 @@ static void fkLookupParent( int nCol = pFKey->nCol; int regTemp = sqlite3GetTempRange(pParse, nCol); int regRec = sqlite3GetTempReg(pParse); - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); for(i=0; i0 && pFKey->isDeferred==0 ){ sqlite3ParseToplevel(pParse)->mayAbort = 1; @@ -90981,6 +91868,62 @@ static void fkLookupParent( sqlite3VdbeAddOp1(v, OP_Close, iCur); } + +/* +** Return an Expr object that refers to a memory register corresponding +** to column iCol of table pTab. +** +** regBase is the first of an array of register that contains the data +** for pTab. regBase itself holds the rowid. regBase+1 holds the first +** column. regBase+2 holds the second column, and so forth. +*/ +static Expr *exprTableRegister( + Parse *pParse, /* Parsing and code generating context */ + Table *pTab, /* The table whose content is at r[regBase]... */ + int regBase, /* Contents of table pTab */ + i16 iCol /* Which column of pTab is desired */ +){ + Expr *pExpr; + Column *pCol; + const char *zColl; + sqlite3 *db = pParse->db; + + pExpr = sqlite3Expr(db, TK_REGISTER, 0); + if( pExpr ){ + if( iCol>=0 && iCol!=pTab->iPKey ){ + pCol = &pTab->aCol[iCol]; + pExpr->iTable = regBase + iCol + 1; + pExpr->affinity = pCol->affinity; + zColl = pCol->zColl; + if( zColl==0 ) zColl = db->pDfltColl->zName; + pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl); + }else{ + pExpr->iTable = regBase; + pExpr->affinity = SQLITE_AFF_INTEGER; + } + } + return pExpr; +} + +/* +** Return an Expr object that refers to column iCol of table pTab which +** has cursor iCur. +*/ +static Expr *exprTableColumn( + sqlite3 *db, /* The database connection */ + Table *pTab, /* The table whose column is desired */ + int iCursor, /* The open cursor on the table */ + i16 iCol /* The column that is wanted */ +){ + Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0); + if( pExpr ){ + pExpr->pTab = pTab; + pExpr->iTable = iCursor; + pExpr->iColumn = iCol; + } + return pExpr; +} + /* ** This function is called to generate code executed when a row is deleted ** from the parent table of foreign key constraint pFKey and, if pFKey is @@ -90996,13 +91939,13 @@ static void fkLookupParent( ** -------------------------------------------------------------------------- ** DELETE immediate Increment the "immediate constraint counter". ** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "foreign key constraint failed" exception. +** throw a "FOREIGN KEY constraint failed" exception. ** ** INSERT immediate Decrement the "immediate constraint counter". ** ** DELETE deferred Increment the "deferred constraint counter". ** Or, if the ON (UPDATE|DELETE) action is RESTRICT, -** throw a "foreign key constraint failed" exception. +** throw a "FOREIGN KEY constraint failed" exception. ** ** INSERT deferred Decrement the "deferred constraint counter". ** @@ -91011,12 +91954,12 @@ static void fkLookupParent( */ static void fkScanChildren( Parse *pParse, /* Parse context */ - SrcList *pSrc, /* SrcList containing the table to scan */ - Table *pTab, - Index *pIdx, /* Foreign key index */ - FKey *pFKey, /* Foreign key relationship */ + SrcList *pSrc, /* The child table to be scanned */ + Table *pTab, /* The parent table */ + Index *pIdx, /* Index on parent covering the foreign key */ + FKey *pFKey, /* The foreign key linking pSrc to pTab */ int *aiCol, /* Map from pIdx cols to child table cols */ - int regData, /* Referenced table data starts here */ + int regData, /* Parent row data starts here */ int nIncr /* Amount to increment deferred counter by */ ){ sqlite3 *db = pParse->db; /* Database handle */ @@ -91027,7 +91970,10 @@ static void fkScanChildren( int iFkIfZero = 0; /* Address of OP_FkIfZero */ Vdbe *v = sqlite3GetVdbe(pParse); - assert( !pIdx || pIdx->pTable==pTab ); + assert( pIdx==0 || pIdx->pTable==pTab ); + assert( pIdx==0 || pIdx->nKeyCol==pFKey->nCol ); + assert( pIdx!=0 || pFKey->nCol==1 ); + assert( pIdx!=0 || HasRowid(pTab) ); if( nIncr<0 ){ iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); @@ -91045,29 +91991,11 @@ static void fkScanChildren( Expr *pLeft; /* Value from parent table row */ Expr *pRight; /* Column ref to child table */ Expr *pEq; /* Expression (pLeft = pRight) */ - int iCol; /* Index of column in child table */ + i16 iCol; /* Index of column in child table */ const char *zCol; /* Name of column in child table */ - pLeft = sqlite3Expr(db, TK_REGISTER, 0); - if( pLeft ){ - /* Set the collation sequence and affinity of the LHS of each TK_EQ - ** expression to the parent key column defaults. */ - if( pIdx ){ - Column *pCol; - const char *zColl; - iCol = pIdx->aiColumn[i]; - pCol = &pTab->aCol[iCol]; - if( pTab->iPKey==iCol ) iCol = -1; - pLeft->iTable = regData+iCol+1; - pLeft->affinity = pCol->affinity; - zColl = pCol->zColl; - if( zColl==0 ) zColl = db->pDfltColl->zName; - pLeft = sqlite3ExprAddCollateString(pParse, pLeft, zColl); - }else{ - pLeft->iTable = regData; - pLeft->affinity = SQLITE_AFF_INTEGER; - } - } + iCol = pIdx ? pIdx->aiColumn[i] : -1; + pLeft = exprTableRegister(pParse, pTab, regData, iCol); iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iCol>=0 ); zCol = pFKey->pFrom->aCol[iCol].zName; @@ -91076,24 +92004,39 @@ static void fkScanChildren( pWhere = sqlite3ExprAnd(db, pWhere, pEq); } - /* If the child table is the same as the parent table, and this scan - ** is taking place as part of a DELETE operation (operation D.2), omit the - ** row being deleted from the scan by adding ($rowid != rowid) to the WHERE - ** clause, where $rowid is the rowid of the row being deleted. */ + /* If the child table is the same as the parent table, then add terms + ** to the WHERE clause that prevent this entry from being scanned. + ** The added WHERE clause terms are like this: + ** + ** $current_rowid!=rowid + ** NOT( $current_a==a AND $current_b==b AND ... ) + ** + ** The first form is used for rowid tables. The second form is used + ** for WITHOUT ROWID tables. In the second form, the primary key is + ** (a,b,...) + */ if( pTab==pFKey->pFrom && nIncr>0 ){ - Expr *pEq; /* Expression (pLeft = pRight) */ + Expr *pNe; /* Expression (pLeft != pRight) */ Expr *pLeft; /* Value from parent table row */ Expr *pRight; /* Column ref to child table */ - pLeft = sqlite3Expr(db, TK_REGISTER, 0); - pRight = sqlite3Expr(db, TK_COLUMN, 0); - if( pLeft && pRight ){ - pLeft->iTable = regData; - pLeft->affinity = SQLITE_AFF_INTEGER; - pRight->iTable = pSrc->a[0].iCursor; - pRight->iColumn = -1; + if( HasRowid(pTab) ){ + pLeft = exprTableRegister(pParse, pTab, regData, -1); + pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1); + pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0); + }else{ + Expr *pEq, *pAll = 0; + Index *pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pIdx!=0 ); + for(i=0; inKeyCol; i++){ + i16 iCol = pIdx->aiColumn[i]; + pLeft = exprTableRegister(pParse, pTab, regData, iCol); + pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol); + pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0); + pAll = sqlite3ExprAnd(db, pAll, pEq); + } + pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0); } - pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0); - pWhere = sqlite3ExprAnd(db, pWhere, pEq); + pWhere = sqlite3ExprAnd(db, pWhere, pNe); } /* Resolve the references in the WHERE clause. */ @@ -91123,8 +92066,8 @@ static void fkScanChildren( } /* -** This function returns a pointer to the head of a linked list of FK -** constraints for which table pTab is the parent table. For example, +** This function returns a linked list of FKey objects (connected by +** FKey.pNextTo) holding all children of table pTab. For example, ** given the following schema: ** ** CREATE TABLE t1(a PRIMARY KEY); @@ -91215,8 +92158,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa if( (db->flags & SQLITE_DeferFKs)==0 ){ sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2); sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, - OE_Abort, "foreign key constraint failed", P4_STATIC - ); + OE_Abort, 0, P4_STATIC, P5_ConstraintFK); } if( iSkip ){ @@ -91426,7 +92368,8 @@ SQLITE_PRIVATE void sqlite3FkCheck( sqlite3DbFree(db, aiFree); } - /* Loop through all the foreign key constraints that refer to this table */ + /* Loop through all the foreign key constraints that refer to this table. + ** (the "child" constraints) */ for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ Index *pIdx = 0; /* Foreign key index for pFKey */ SrcList *pSrc; @@ -91451,9 +92394,8 @@ SQLITE_PRIVATE void sqlite3FkCheck( } assert( aiCol || pFKey->nCol==1 ); - /* Create a SrcList structure containing a single table (the table - ** the foreign key that refers to this table is attached to). This - ** is required for the sqlite3WhereXXX() interface. */ + /* Create a SrcList structure containing the child table. We need the + ** child table as a SrcList for sqlite3WhereBegin() */ pSrc = sqlite3SrcListAppend(db, 0, 0, 0); if( pSrc ){ struct SrcList_item *pItem = pSrc->a; @@ -91502,7 +92444,7 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask( Index *pIdx = 0; sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); if( pIdx ){ - for(i=0; inColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); + for(i=0; inKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); } } } @@ -91694,7 +92636,7 @@ static Trigger *fkActionTrigger( tFrom.z = zFrom; tFrom.n = nFrom; - pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed"); + pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); if( pRaise ){ pRaise->affinity = OE_Abort; } @@ -91858,10 +92800,16 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){ */ /* -** Generate code that will open a table for reading. +** Generate code that will +** +** (1) acquire a lock for table pTab then +** (2) open pTab as cursor iCur. +** +** If pTab is a WITHOUT ROWID table, then it is the PRIMARY KEY index +** for that table that is actually opened. */ SQLITE_PRIVATE void sqlite3OpenTable( - Parse *p, /* Generate code into this VDBE */ + Parse *pParse, /* Generate code into this VDBE */ int iCur, /* The cursor number of the table */ int iDb, /* The database index in sqlite3.aDb[] */ Table *pTab, /* The table to be opened */ @@ -91869,12 +92817,21 @@ SQLITE_PRIVATE void sqlite3OpenTable( ){ Vdbe *v; assert( !IsVirtual(pTab) ); - v = sqlite3GetVdbe(p); + v = sqlite3GetVdbe(pParse); assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); - sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); - sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32); - VdbeComment((v, "%s", pTab->zName)); + sqlite3TableLock(pParse, iDb, pTab->tnum, + (opcode==OP_OpenWrite)?1:0, pTab->zName); + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nCol); + VdbeComment((v, "%s", pTab->zName)); + }else{ + Index *pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); + assert( pPk->tnum=pTab->tnum ); + sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); + VdbeComment((v, "%s", pTab->zName)); + } } /* @@ -91910,15 +92867,15 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ int n; Table *pTab = pIdx->pTable; sqlite3 *db = sqlite3VdbeDb(v); - pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+2); + pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1); if( !pIdx->zColAff ){ db->mallocFailed = 1; return 0; } for(n=0; nnColumn; n++){ - pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; + i16 x = pIdx->aiColumn[n]; + pIdx->zColAff[n] = x<0 ? SQLITE_AFF_INTEGER : pTab->aCol[x].affinity; } - pIdx->zColAff[n++] = SQLITE_AFF_INTEGER; pIdx->zColAff[n] = 0; } @@ -92264,7 +93221,7 @@ static int xferOptimization( ); /* -** This routine is call to handle SQL of the following forms: +** This routine is called to handle SQL of the following forms: ** ** insert into TABLE (IDLIST) values(EXPRLIST) ** insert into TABLE (IDLIST) select @@ -92279,12 +93236,12 @@ static int xferOptimization( ** data for the insert. ** ** The code generated follows one of four templates. For a simple -** select with data coming from a VALUES clause, the code executes +** insert with data coming from a VALUES clause, the code executes ** once straight down through. Pseudo-code follows (we call this ** the "1st template"): ** ** open write cursor to
and its indices -** puts VALUES clause expressions onto the stack +** put VALUES clause expressions into registers ** write the resulting record into
** cleanup ** @@ -92368,7 +93325,6 @@ static int xferOptimization( SQLITE_PRIVATE void sqlite3Insert( Parse *pParse, /* Parser context */ SrcList *pTabList, /* Name of table into which we are inserting */ - ExprList *pList, /* List of values to be inserted */ Select *pSelect, /* A SELECT statement to use as the data source */ IdList *pColumn, /* Column names corresponding to IDLIST. */ int onError /* How to handle constraint errors */ @@ -92382,8 +93338,9 @@ SQLITE_PRIVATE void sqlite3Insert( Index *pIdx; /* For looping over indices of the table */ int nColumn; /* Number of columns in the data */ int nHidden = 0; /* Number of hidden columns if TABLE is virtual */ - int baseCur = 0; /* VDBE Cursor number for pTab */ - int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ + int iDataCur = 0; /* VDBE cursor that is the main data repository */ + int iIdxCur = 0; /* First index cursor */ + int ipkColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ int endOfLoop; /* Label for the end of the insertion loop */ int useTempTable = 0; /* Store SELECT results in intermediate table */ int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ @@ -92394,6 +93351,8 @@ SQLITE_PRIVATE void sqlite3Insert( int iDb; /* Index of database holding TABLE */ Db *pDb; /* The database containing table being inserted into */ int appendFlag = 0; /* True if the insert is likely to be an append */ + int withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */ + ExprList *pList = 0; /* List of VALUES() to be inserted */ /* Register allocations */ int regFromSelect = 0;/* Base register for data coming from SELECT */ @@ -92417,6 +93376,17 @@ SQLITE_PRIVATE void sqlite3Insert( goto insert_cleanup; } + /* If the Select object is really just a simple VALUES() list with a + ** single row values (the common case) then keep that one row of values + ** and go ahead and discard the Select object + */ + if( pSelect && (pSelect->selFlags & SF_Values)!=0 && pSelect->pPrior==0 ){ + pList = pSelect->pEList; + pSelect->pEList = 0; + sqlite3SelectDelete(db, pSelect); + pSelect = 0; + } + /* Locate the table into which we will be inserting new information. */ assert( pTabList->nSrc==1 ); @@ -92433,6 +93403,7 @@ SQLITE_PRIVATE void sqlite3Insert( if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ goto insert_cleanup; } + withoutRowid = !HasRowid(pTab); /* Figure out if we have any triggers and if the table being ** inserted into is a view @@ -92452,16 +93423,13 @@ SQLITE_PRIVATE void sqlite3Insert( assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); /* If pTab is really a view, make sure it has been initialized. - ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual - ** module table). + ** ViewGetColumnNames() is a no-op if pTab is not a view. */ if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto insert_cleanup; } - /* Ensure that: - * (a) the table is not read-only, - * (b) that if it is a view then ON INSERT triggers exist + /* Cannot insert into a read-only table. */ if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ goto insert_cleanup; @@ -92502,8 +93470,7 @@ SQLITE_PRIVATE void sqlite3Insert( ** co-routine is the common header to the 3rd and 4th templates. */ if( pSelect ){ - /* Data is coming from a SELECT. Generate a co-routine to run that - ** SELECT. */ + /* Data is coming from a SELECT. Generate a co-routine to run the SELECT */ int rc = sqlite3CodeCoroutine(pParse, pSelect, &dest); if( rc ) goto insert_cleanup; @@ -92515,7 +93482,7 @@ SQLITE_PRIVATE void sqlite3Insert( /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table (template 4). Set to - ** FALSE if each* row of the SELECT can be written directly into + ** FALSE if each output row of the SELECT can be written directly into ** the destination table (template 3). ** ** A temp table must be used if the table being updated is also one @@ -92598,11 +93565,11 @@ SQLITE_PRIVATE void sqlite3Insert( ** remember the column indices. ** ** If the table has an INTEGER PRIMARY KEY column and that column - ** is named in the IDLIST, then record in the keyColumn variable - ** the index into IDLIST of the primary key column. keyColumn is + ** is named in the IDLIST, then record in the ipkColumn variable + ** the index into IDLIST of the primary key column. ipkColumn is ** the index of the primary key as it appears in IDLIST, not as - ** is appears in the original table. (The index of the primary - ** key in the original table is pTab->iPKey.) + ** is appears in the original table. (The index of the INTEGER + ** PRIMARY KEY in the original table is pTab->iPKey.) */ if( pColumn ){ for(i=0; inId; i++){ @@ -92613,14 +93580,14 @@ SQLITE_PRIVATE void sqlite3Insert( if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ pColumn->a[i].idx = j; if( j==pTab->iPKey ){ - keyColumn = i; + ipkColumn = i; assert( !withoutRowid ); } break; } } if( j>=pTab->nCol ){ - if( sqlite3IsRowid(pColumn->a[i].zName) ){ - keyColumn = i; + if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){ + ipkColumn = i; }else{ sqlite3ErrorMsg(pParse, "table %S has no column named %s", pTabList, 0, pColumn->a[i].zName); @@ -92632,11 +93599,11 @@ SQLITE_PRIVATE void sqlite3Insert( } /* If there is no IDLIST term but the table has an integer primary - ** key, the set the keyColumn variable to the primary key column index - ** in the original table definition. + ** key, the set the ipkColumn variable to the integer primary key + ** column index in the original table definition. */ if( pColumn==0 && nColumn>0 ){ - keyColumn = pTab->iPKey; + ipkColumn = pTab->iPKey; } /* Initialize the count of rows to be inserted @@ -92649,9 +93616,8 @@ SQLITE_PRIVATE void sqlite3Insert( /* If this is not a view, open the table and and all indices */ if( !isView ){ int nIdx; - - baseCur = pParse->nTab; - nIdx = sqlite3OpenTableAndIndices(pParse, pTab, baseCur, OP_OpenWrite); + nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, -1, 0, + &iDataCur, &iIdxCur); aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1)); if( aRegIdx==0 ){ goto insert_cleanup; @@ -92711,15 +93677,16 @@ SQLITE_PRIVATE void sqlite3Insert( ** we do not know what the unique ID will be (because the insert has ** not happened yet) so we substitute a rowid of -1 */ - if( keyColumn<0 ){ + if( ipkColumn<0 ){ sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); }else{ int j1; + assert( !withoutRowid ); if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols); + sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regCols); }else{ assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regCols); + sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regCols); } j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); @@ -92769,29 +93736,27 @@ SQLITE_PRIVATE void sqlite3Insert( sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1); } - /* Push the record number for the new entry onto the stack. The - ** record number is a randomly generate integer created by NewRowid - ** except when the table has an INTEGER PRIMARY KEY column, in which - ** case the record number is the same as that column. + /* Compute the content of the next row to insert into a range of + ** registers beginning at regIns. */ if( !isView ){ if( IsVirtual(pTab) ){ /* The row that the VUpdate opcode will delete: none */ sqlite3VdbeAddOp2(v, OP_Null, 0, regIns); } - if( keyColumn>=0 ){ + if( ipkColumn>=0 ){ if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid); + sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regRowid); }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+keyColumn, regRowid); + sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+ipkColumn, regRowid); }else{ VdbeOp *pOp; - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid); + sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid); pOp = sqlite3VdbeGetOp(v, -1); if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){ appendFlag = 1; pOp->opcode = OP_NewRowid; - pOp->p1 = baseCur; + pOp->p1 = iDataCur; pOp->p2 = regRowid; pOp->p3 = regAutoinc; } @@ -92803,7 +93768,7 @@ SQLITE_PRIVATE void sqlite3Insert( int j1; if( !IsVirtual(pTab) ){ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); - sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); + sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc); sqlite3VdbeJumpHere(v, j1); }else{ j1 = sqlite3VdbeCurrentAddr(v); @@ -92811,15 +93776,15 @@ SQLITE_PRIVATE void sqlite3Insert( } sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); } - }else if( IsVirtual(pTab) ){ + }else if( IsVirtual(pTab) || withoutRowid ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid); }else{ - sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc); + sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc); appendFlag = 1; } autoIncStep(pParse, regAutoinc, regRowid); - /* Push onto the stack, data for all columns of the new entry, beginning + /* Compute data for all columns of the new entry, beginning ** with the first column. */ nHidden = 0; @@ -92827,8 +93792,8 @@ SQLITE_PRIVATE void sqlite3Insert( int iRegStore = regRowid+1+i; if( i==pTab->iPKey ){ /* The value of the INTEGER PRIMARY KEY column is always a NULL. - ** Whenever this column is read, the record number will be substituted - ** in its place. So will fill this column with a NULL to avoid + ** Whenever this column is read, the rowid will be substituted + ** in its place. Hence, fill this column with a NULL to avoid ** taking up data space with information that will never be used. */ sqlite3VdbeAddOp2(v, OP_Null, 0, iRegStore); continue; @@ -92871,13 +93836,12 @@ SQLITE_PRIVATE void sqlite3Insert( #endif { int isReplace; /* Set to true if constraints may cause a replace */ - sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx, - keyColumn>=0, 0, onError, endOfLoop, &isReplace + sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, + regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace ); sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); - sqlite3CompleteInsertion( - pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0 - ); + sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, + regIns, aRegIdx, 0, appendFlag, isReplace==0); } } @@ -92908,9 +93872,9 @@ SQLITE_PRIVATE void sqlite3Insert( if( !IsVirtual(pTab) && !isView ){ /* Close all tables opened */ - sqlite3VdbeAddOp1(v, OP_Close, baseCur); - for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqlite3VdbeAddOp1(v, OP_Close, idx+baseCur); + if( iDataCurpIndex; pIdx; pIdx=pIdx->pNext, idx++){ + sqlite3VdbeAddOp1(v, OP_Close, idx+iIdxCur); } } @@ -92955,36 +93919,48 @@ insert_cleanup: #undef tmask #endif - /* -** Generate code to do constraint checks prior to an INSERT or an UPDATE. +** Generate code to do constraint checks prior to an INSERT or an UPDATE +** on table pTab. ** -** The input is a range of consecutive registers as follows: +** The regNewData parameter is the first register in a range that contains +** the data to be inserted or the data after the update. There will be +** pTab->nCol+1 registers in this range. The first register (the one +** that regNewData points to) will contain the new rowid, or NULL in the +** case of a WITHOUT ROWID table. The second register in the range will +** contain the content of the first table column. The third register will +** contain the content of the second table column. And so forth. ** -** 1. The rowid of the row after the update. +** The regOldData parameter is similar to regNewData except that it contains +** the data prior to an UPDATE rather than afterwards. regOldData is zero +** for an INSERT. This routine can distinguish between UPDATE and INSERT by +** checking regOldData for zero. ** -** 2. The data in the first column of the entry after the update. +** For an UPDATE, the pkChng boolean is true if the true primary key (the +** rowid for a normal table or the PRIMARY KEY for a WITHOUT ROWID table) +** might be modified by the UPDATE. If pkChng is false, then the key of +** the iDataCur content table is guaranteed to be unchanged by the UPDATE. ** -** i. Data from middle columns... +** For an INSERT, the pkChng boolean indicates whether or not the rowid +** was explicitly specified as part of the INSERT statement. If pkChng +** is zero, it means that the either rowid is computed automatically or +** that the table is a WITHOUT ROWID table and has no rowid. On an INSERT, +** pkChng will only be true if the INSERT statement provides an integer +** value for either the rowid column or its INTEGER PRIMARY KEY alias. ** -** N. The data in the last column of the entry after the update. -** -** The regRowid parameter is the index of the register containing (1). -** -** If isUpdate is true and rowidChng is non-zero, then rowidChng contains -** the address of a register containing the rowid before the update takes -** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate -** is false, indicating an INSERT statement, then a non-zero rowidChng -** indicates that the rowid was explicitly specified as part of the -** INSERT statement. If rowidChng is false, it means that the rowid is -** computed automatically in an insert or that the rowid value is not -** modified by an update. -** -** The code generated by this routine store new index entries into +** The code generated by this routine will store new index entries into ** registers identified by aRegIdx[]. No index entry is created for ** indices where aRegIdx[i]==0. The order of indices in aRegIdx[] is ** the same as the order of indices on the linked list of indices -** attached to the table. +** at pTab->pIndex. +** +** The caller must have already opened writeable cursors on the main +** table and all applicable indices (that is to say, all indices for which +** aRegIdx[] is not zero). iDataCur is the cursor for the main table when +** inserting or updating a rowid table, or the cursor for the PRIMARY KEY +** index when operating on a WITHOUT ROWID table. iIdxCur is the cursor +** for the first index in the pTab->pIndex list. Cursors for other indices +** are at iIdxCur+N for the N-th element of the pTab->pIndex list. ** ** This routine also generates code to check constraints. NOT NULL, ** CHECK, and UNIQUE constraints are all checked. If a constraint fails, @@ -92994,22 +93970,23 @@ insert_cleanup: ** Constraint type Action What Happens ** --------------- ---------- ---------------------------------------- ** any ROLLBACK The current transaction is rolled back and -** sqlite3_exec() returns immediately with a +** sqlite3_step() returns immediately with a ** return code of SQLITE_CONSTRAINT. ** ** any ABORT Back out changes from the current command ** only (do not do a complete rollback) then -** cause sqlite3_exec() to return immediately +** cause sqlite3_step() to return immediately ** with SQLITE_CONSTRAINT. ** -** any FAIL Sqlite3_exec() returns immediately with a +** any FAIL Sqlite3_step() returns immediately with a ** return code of SQLITE_CONSTRAINT. The ** transaction is not rolled back and any -** prior changes are retained. +** changes to prior rows are retained. ** -** any IGNORE The record number and data is popped from -** the stack and there is an immediate jump -** to label ignoreDest. +** any IGNORE The attempt in insert or update the current +** row is skipped, without throwing an error. +** Processing continues with the next row. +** (There is an immediate jump to ignoreDest.) ** ** NOT NULL REPLACE The NULL value is replace by the default ** value for that column. If the default value @@ -93024,44 +94001,58 @@ insert_cleanup: ** Or if overrideError==OE_Default, then the pParse->onError parameter ** is used. Or if pParse->onError==OE_Default then the onError value ** for the constraint is used. -** -** The calling routine must open a read/write cursor for pTab with -** cursor number "baseCur". All indices of pTab must also have open -** read/write cursors with cursor number baseCur+i for the i-th cursor. -** Except, if there is no possibility of a REPLACE action then -** cursors do not need to be open for indices where aRegIdx[i]==0. */ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( - Parse *pParse, /* The parser context */ - Table *pTab, /* the table into which we are inserting */ - int baseCur, /* Index of a read/write cursor pointing at pTab */ - int regRowid, /* Index of the range of input registers */ - int *aRegIdx, /* Register used by each index. 0 for unused indices */ - int rowidChng, /* True if the rowid might collide with existing entry */ - int isUpdate, /* True for UPDATE, False for INSERT */ - int overrideError, /* Override onError to this if not OE_Default */ - int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ - int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ + Parse *pParse, /* The parser context */ + Table *pTab, /* The table being inserted or updated */ + int *aRegIdx, /* Use register aRegIdx[i] for index i. 0 for unused */ + int iDataCur, /* Canonical data cursor (main table or PK index) */ + int iIdxCur, /* First index cursor */ + int regNewData, /* First register in a range holding values to insert */ + int regOldData, /* Previous content. 0 for INSERTs */ + u8 pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */ + u8 overrideError, /* Override onError to this if not OE_Default */ + int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ + int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ ){ - int i; /* loop counter */ - Vdbe *v; /* VDBE under constrution */ - int nCol; /* Number of columns */ - int onError; /* Conflict resolution strategy */ - int j1; /* Addresss of jump instruction */ - int j2 = 0, j3; /* Addresses of jump instructions */ - int regData; /* Register containing first data column */ - int iCur; /* Table cursor number */ + Vdbe *v; /* VDBE under constrution */ Index *pIdx; /* Pointer to one of the indices */ + Index *pPk = 0; /* The PRIMARY KEY index */ sqlite3 *db; /* Database connection */ + int i; /* loop counter */ + int ix; /* Index loop counter */ + int nCol; /* Number of columns */ + int onError; /* Conflict resolution strategy */ + int j1; /* Addresss of jump instruction */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ - int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; + int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ + int ipkTop = 0; /* Top of the rowid change constraint check */ + int ipkBottom = 0; /* Bottom of the rowid change constraint check */ + u8 isUpdate; /* True if this is an UPDATE operation */ + int regRowid = -1; /* Register holding ROWID value */ + isUpdate = regOldData!=0; db = pParse->db; v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ nCol = pTab->nCol; - regData = regRowid + 1; + + /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for + ** normal rowid tables. nPkField is the number of key fields in the + ** pPk index or 1 for a rowid table. In other words, nPkField is the + ** number of fields in the true primary key of the table. */ + if( HasRowid(pTab) ){ + pPk = 0; + nPkField = 1; + }else{ + pPk = sqlite3PrimaryKeyIndex(pTab); + nPkField = pPk->nKeyCol; + } + + /* Record that this module has started */ + VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)", + iDataCur, iIdxCur, regNewData, regOldData, pkChng)); /* Test all NOT NULL constraints. */ @@ -93084,24 +94075,24 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( switch( onError ){ case OE_Abort: sqlite3MayAbort(pParse); + /* Fall through */ case OE_Rollback: case OE_Fail: { - char *zMsg; - sqlite3VdbeAddOp3(v, OP_HaltIfNull, - SQLITE_CONSTRAINT_NOTNULL, onError, regData+i); - zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL", - pTab->zName, pTab->aCol[i].zName); - sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); + char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, + pTab->aCol[i].zName); + sqlite3VdbeAddOp4(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError, + regNewData+1+i, zMsg, P4_DYNAMIC); + sqlite3VdbeChangeP5(v, P5_ConstraintNotNull); break; } case OE_Ignore: { - sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest); + sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest); break; } default: { assert( onError==OE_Replace ); - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i); - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i); + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i); sqlite3VdbeJumpHere(v, j1); break; } @@ -93113,7 +94104,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( #ifndef SQLITE_OMIT_CHECK if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = pTab->pCheck; - pParse->ckBase = regData; + pParse->ckBase = regNewData+1; onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; inExpr; i++){ int allOk = sqlite3VdbeMakeLabel(v); @@ -93121,37 +94112,58 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( if( onError==OE_Ignore ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); }else{ - char *zConsName = pCheck->a[i].zName; + char *zName = pCheck->a[i].zName; + if( zName==0 ) zName = pTab->zName; if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ - if( zConsName ){ - zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName); - }else{ - zConsName = 0; - } sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK, - onError, zConsName, P4_DYNAMIC); + onError, zName, P4_TRANSIENT, + P5_ConstraintCheck); } sqlite3VdbeResolveLabel(v, allOk); } } #endif /* !defined(SQLITE_OMIT_CHECK) */ - /* If we have an INTEGER PRIMARY KEY, make sure the primary key - ** of the new record does not previously exist. Except, if this - ** is an UPDATE and the primary key is not changing, that is OK. + /* If rowid is changing, make sure the new rowid does not previously + ** exist in the table. */ - if( rowidChng ){ + if( pkChng && pPk==0 ){ + int addrRowidOk = sqlite3VdbeMakeLabel(v); + + /* Figure out what action to take in case of a rowid collision */ onError = pTab->keyConf; if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } - + if( isUpdate ){ - j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng); + /* pkChng!=0 does not mean that the rowid has change, only that + ** it might have changed. Skip the conflict logic below if the rowid + ** is unchanged. */ + sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData); } - j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid); + + /* If the response to a rowid conflict is REPLACE but the response + ** to some other UNIQUE constraint is FAIL or IGNORE, then we need + ** to defer the running of the rowid conflict checking until after + ** the UNIQUE constraints have run. + */ + if( onError==OE_Replace && overrideError!=OE_Replace ){ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){ + ipkTop = sqlite3VdbeAddOp0(v, OP_Goto); + break; + } + } + } + + /* Check to see if the new rowid already exists in the table. Skip + ** the following conflict logic if it does not. */ + sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData); + + /* Generate code that deals with a rowid collision */ switch( onError ){ default: { onError = OE_Abort; @@ -93160,8 +94172,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Abort: case OE_Fail: { - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY, - onError, "PRIMARY KEY must be unique", P4_STATIC); + sqlite3RowidConstraint(pParse, onError, pTab); break; } case OE_Replace: { @@ -93193,68 +94204,90 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( } if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ sqlite3MultiWrite(pParse); - sqlite3GenerateRowDelete( - pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace - ); + sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, + regNewData, 1, 0, OE_Replace, 1); }else if( pTab->pIndex ){ sqlite3MultiWrite(pParse); - sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); + sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0); } seenReplace = 1; break; } case OE_Ignore: { - assert( seenReplace==0 ); + /*assert( seenReplace==0 );*/ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); break; } } - sqlite3VdbeJumpHere(v, j3); - if( isUpdate ){ - sqlite3VdbeJumpHere(v, j2); + sqlite3VdbeResolveLabel(v, addrRowidOk); + if( ipkTop ){ + ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeJumpHere(v, ipkTop); } } /* Test all UNIQUE constraints by creating entries for each UNIQUE ** index and making sure that duplicate entries do not already exist. - ** Add the new records to the indices as we go. + ** Compute the revised record entries for indices as we go. + ** + ** This loop also handles the case of the PRIMARY KEY index for a + ** WITHOUT ROWID table. */ - for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ - int regIdx; - int regR; - int addrSkipRow = 0; + for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ + int regIdx; /* Range of registers hold conent for pIdx */ + int regR; /* Range of registers holding conflicting PK */ + int iThisCur; /* Cursor for this UNIQUE index */ + int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ - if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ + if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ + iThisCur = iIdxCur+ix; + addrUniqueOk = sqlite3VdbeMakeLabel(v); + /* Skip partial indices for which the WHERE clause is not true */ if( pIdx->pPartIdxWhere ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[iCur]); - addrSkipRow = sqlite3VdbeMakeLabel(v); - pParse->ckBase = regData; - sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrSkipRow, + sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]); + pParse->ckBase = regNewData+1; + sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrUniqueOk, SQLITE_JUMPIFNULL); pParse->ckBase = 0; } - /* Create a key for accessing the index entry */ - regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1); + /* Create a record for this index entry as it should appear after + ** the insert or update. Store that record in the aRegIdx[ix] register + */ + regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn); for(i=0; inColumn; i++){ - int idx = pIdx->aiColumn[i]; - if( idx==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); + int iField = pIdx->aiColumn[i]; + int x; + if( iField<0 || iField==pTab->iPKey ){ + if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */ + x = regNewData; + regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i; }else{ - sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i); + x = iField + regNewData + 1; } + sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i); + VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName)); } - sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT); - sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); + VdbeComment((v, "for %s", pIdx->zName)); + sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn); - /* Find out what action to take in case there is an indexing conflict */ + /* In an UPDATE operation, if this index is the PRIMARY KEY index + ** of a WITHOUT ROWID table and there has been no change the + ** primary key, then no collision is possible. The collision detection + ** logic below can all be skipped. */ + if( isUpdate && pPk==pIdx && pkChng==0 ){ + sqlite3VdbeResolveLabel(v, addrUniqueOk); + continue; + } + + /* Find out what action to take in case there is a uniqueness conflict */ onError = pIdx->onError; if( onError==OE_None ){ - sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); - sqlite3VdbeResolveLabel(v, addrSkipRow); + sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn); + sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; /* pIdx is not a UNIQUE index */ } if( overrideError!=OE_Default ){ @@ -93262,18 +94295,59 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( }else if( onError==OE_Default ){ onError = OE_Abort; } - if( seenReplace ){ - if( onError==OE_Ignore ) onError = OE_Replace; - else if( onError==OE_Fail ) onError = OE_Abort; - } /* Check to see if the new index entry will be unique */ - regR = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR); - j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0, - regR, SQLITE_INT_TO_PTR(regIdx), - P4_INT32); - sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); + sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, + regIdx, pIdx->nKeyCol); + + /* Generate code to handle collisions */ + regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); + if( isUpdate || onError==OE_Replace ){ + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); + /* Conflict only if the rowid of the existing index entry + ** is different from old-rowid */ + if( isUpdate ){ + sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); + } + }else{ + int x; + /* Extract the PRIMARY KEY from the end of the index entry and + ** store it in registers regR..regR+nPk-1 */ + if( pIdx!=pPk ){ + for(i=0; inKeyCol; i++){ + x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); + sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); + VdbeComment((v, "%s.%s", pTab->zName, + pTab->aCol[pPk->aiColumn[i]].zName)); + } + } + if( isUpdate ){ + /* If currently processing the PRIMARY KEY of a WITHOUT ROWID + ** table, only conflict if the new PRIMARY KEY values are actually + ** different from the old. + ** + ** For a UNIQUE index, only conflict if the PRIMARY KEY values + ** of the matched index row are different from the original PRIMARY + ** KEY values of this row before the update. */ + int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; + int op = OP_Ne; + int regCmp = (pIdx->autoIndex==2 ? regIdx : regR); + + for(i=0; inKeyCol; i++){ + char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); + x = pPk->aiColumn[i]; + if( i==(pPk->nKeyCol-1) ){ + addrJump = addrUniqueOk; + op = OP_Eq; + } + sqlite3VdbeAddOp4(v, op, + regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ + ); + } + } + } + } /* Generate code that executes if the new index entry is not unique */ assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail @@ -93282,30 +94356,10 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Abort: case OE_Fail: { - int j; - StrAccum errMsg; - const char *zSep; - char *zErr; - - sqlite3StrAccumInit(&errMsg, 0, 0, 200); - errMsg.db = db; - zSep = pIdx->nColumn>1 ? "columns " : "column "; - for(j=0; jnColumn; j++){ - char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; - sqlite3StrAccumAppend(&errMsg, zSep, -1); - zSep = ", "; - sqlite3StrAccumAppend(&errMsg, zCol, -1); - } - sqlite3StrAccumAppend(&errMsg, - pIdx->nColumn>1 ? " are not unique" : " is not unique", -1); - zErr = sqlite3StrAccumFinish(&errMsg); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE, - onError, zErr, 0); - sqlite3DbFree(errMsg.db, zErr); + sqlite3UniqueConstraint(pParse, onError, pIdx); break; } case OE_Ignore: { - assert( seenReplace==0 ); sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); break; } @@ -93316,27 +94370,29 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( if( db->flags&SQLITE_RecTriggers ){ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } - sqlite3GenerateRowDelete( - pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace - ); + sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, + regR, nPkField, 0, OE_Replace, pIdx==pPk); seenReplace = 1; break; } } - sqlite3VdbeJumpHere(v, j3); - sqlite3VdbeResolveLabel(v, addrSkipRow); - sqlite3ReleaseTempReg(pParse, regR); + sqlite3VdbeResolveLabel(v, addrUniqueOk); + sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn); + if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField); + } + if( ipkTop ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, ipkTop+1); + sqlite3VdbeJumpHere(v, ipkBottom); } - if( pbMayReplace ){ - *pbMayReplace = seenReplace; - } + *pbMayReplace = seenReplace; + VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace)); } /* ** This routine generates code to finish the INSERT or UPDATE operation ** that was started by a prior call to sqlite3GenerateConstraintChecks. -** A consecutive range of registers starting at regRowid contains the +** A consecutive range of registers starting at regNewData contains the ** rowid and the content to be inserted. ** ** The arguments to this routine should be the same as the first six @@ -93345,19 +94401,20 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( SQLITE_PRIVATE void sqlite3CompleteInsertion( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ - int baseCur, /* Index of a read/write cursor pointing at pTab */ - int regRowid, /* Range of content */ + int iDataCur, /* Cursor of the canonical data source */ + int iIdxCur, /* First index cursor */ + int regNewData, /* Range of content */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ int isUpdate, /* True for UPDATE, False for INSERT */ int appendBias, /* True if this is likely to be an append */ int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */ ){ - int i; - Vdbe *v; - Index *pIdx; - u8 pik_flags; - int regData; - int regRec; + Vdbe *v; /* Prepared statements under construction */ + Index *pIdx; /* An index being inserted or updated */ + u8 pik_flags; /* flag values passed to the btree insert */ + int regData; /* Content registers (after the rowid) */ + int regRec; /* Register holding assemblied record for the table */ + int i; /* Loop counter */ v = sqlite3GetVdbe(pParse); assert( v!=0 ); @@ -93367,12 +94424,17 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2); } - sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]); - if( useSeekResult ){ - sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]); + pik_flags = 0; + if( useSeekResult ) pik_flags = OPFLAG_USESEEKRESULT; + if( pIdx->autoIndex==2 && !HasRowid(pTab) ){ + assert( pParse->nested==0 ); + pik_flags |= OPFLAG_NCHANGE; } + if( pik_flags ) sqlite3VdbeChangeP5(v, pik_flags); } - regData = regRowid + 1; + if( !HasRowid(pTab) ) return; + regData = regNewData + 1; regRec = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); sqlite3TableAffinityStr(v, pTab); @@ -93389,7 +94451,7 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( if( useSeekResult ){ pik_flags |= OPFLAG_USESEEKRESULT; } - sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData); if( !pParse->nested ){ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } @@ -93397,39 +94459,71 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( } /* -** Generate code that will open cursors for a table and for all -** indices of that table. The "baseCur" parameter is the cursor number used -** for the table. Indices are opened on subsequent cursors. +** Allocate cursors for the pTab table and all its indices and generate +** code to open and initialized those cursors. ** -** Return the number of indices on the table. +** The cursor for the object that contains the complete data (normally +** the table itself, but the PRIMARY KEY index in the case of a WITHOUT +** ROWID table) is returned in *piDataCur. The first index cursor is +** returned in *piIdxCur. The number of indices is returned. +** +** Use iBase as the first cursor (either the *piDataCur for rowid tables +** or the first index for WITHOUT ROWID tables) if it is non-negative. +** If iBase is negative, then allocate the next available cursor. +** +** For a rowid table, *piDataCur will be exactly one less than *piIdxCur. +** For a WITHOUT ROWID table, *piDataCur will be somewhere in the range +** of *piIdxCurs, depending on where the PRIMARY KEY index appears on the +** pTab->pIndex list. */ SQLITE_PRIVATE int sqlite3OpenTableAndIndices( Parse *pParse, /* Parsing context */ Table *pTab, /* Table to be opened */ - int baseCur, /* Cursor number assigned to the table */ - int op /* OP_OpenRead or OP_OpenWrite */ + int op, /* OP_OpenRead or OP_OpenWrite */ + int iBase, /* Use this for the table cursor, if there is one */ + u8 *aToOpen, /* If not NULL: boolean for each table and index */ + int *piDataCur, /* Write the database source cursor number here */ + int *piIdxCur /* Write the first index cursor number here */ ){ int i; int iDb; + int iDataCur; Index *pIdx; Vdbe *v; - if( IsVirtual(pTab) ) return 0; + assert( op==OP_OpenRead || op==OP_OpenWrite ); + if( IsVirtual(pTab) ){ + assert( aToOpen==0 ); + *piDataCur = 0; + *piIdxCur = 1; + return 0; + } iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); v = sqlite3GetVdbe(pParse); assert( v!=0 ); - sqlite3OpenTable(pParse, baseCur, iDb, pTab, op); - for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + if( iBase<0 ) iBase = pParse->nTab; + iDataCur = iBase++; + if( piDataCur ) *piDataCur = iDataCur; + if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){ + sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op); + }else{ + sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); + } + if( piIdxCur ) *piIdxCur = iBase; + for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); - sqlite3VdbeAddOp4(v, op, i+baseCur, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIdx->zName)); + if( pIdx->autoIndex==2 && !HasRowid(pTab) && piDataCur ){ + *piDataCur = iIdxCur; + } + if( aToOpen==0 || aToOpen[i+1] ){ + sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); + VdbeComment((v, "%s", pIdx->zName)); + } } - if( pParse->nTabnTab = baseCur+i; - } - return i-1; + if( iBase>pParse->nTab ) pParse->nTab = iBase; + return i; } @@ -93474,13 +94568,13 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){ int i; assert( pDest && pSrc ); assert( pDest->pTable!=pSrc->pTable ); - if( pDest->nColumn!=pSrc->nColumn ){ + if( pDest->nKeyCol!=pSrc->nKeyCol ){ return 0; /* Different number of columns */ } if( pDest->onError!=pSrc->onError ){ return 0; /* Different conflict resolution strategies */ } - for(i=0; inColumn; i++){ + for(i=0; inKeyCol; i++){ if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){ return 0; /* Different columns indexed */ } @@ -93539,10 +94633,9 @@ static int xferOptimization( int iDbSrc; /* The database of pSrc */ int iSrc, iDest; /* Cursors from source and destination */ int addr1, addr2; /* Loop addresses */ - int emptyDestTest; /* Address of test for empty pDest */ - int emptySrcTest; /* Address of test for empty pSrc */ + int emptyDestTest = 0; /* Address of test for empty pDest */ + int emptySrcTest = 0; /* Address of test for empty pSrc */ Vdbe *v; /* The VDBE we are building */ - KeyInfo *pKey; /* Key information for an index */ int regAutoinc; /* Memory register used by AUTOINC */ int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ int regData, regRowid; /* Registers holding data and rowid */ @@ -93550,6 +94643,12 @@ static int xferOptimization( if( pSelect==0 ){ return 0; /* Must be of the form INSERT INTO ... SELECT ... */ } + if( pParse->pWith || pSelect->pWith ){ + /* Do not attempt to process this query if there are an WITH clauses + ** attached to it. Proceeding may generate a false "no such table: xxx" + ** error if pSelect reads from a CTE named "xxx". */ + return 0; + } if( sqlite3TriggerList(pParse, pDest) ){ return 0; /* tab1 must not have triggers */ } @@ -93612,6 +94711,9 @@ static int xferOptimization( if( pSrc==pDest ){ return 0; /* tab1 and tab2 may not be the same table */ } + if( HasRowid(pDest)!=HasRowid(pSrc) ){ + return 0; /* source and destination must both be WITHOUT ROWID or not */ + } #ifndef SQLITE_OMIT_VIRTUALTABLE if( pSrc->tabFlags & TF_Virtual ){ return 0; /* tab2 must not be a virtual table */ @@ -93682,7 +94784,10 @@ static int xferOptimization( iSrc = pParse->nTab++; iDest = pParse->nTab++; regAutoinc = autoIncBegin(pParse, iDbDest, pDest); + regData = sqlite3GetTempReg(pParse); + regRowid = sqlite3GetTempReg(pParse); sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); + assert( HasRowid(pDest) || destHasUniqueIdx ); if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ || destHasUniqueIdx /* (2) */ || (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */ @@ -93704,45 +94809,43 @@ static int xferOptimization( addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); sqlite3VdbeJumpHere(v, addr1); - }else{ - emptyDestTest = 0; } - sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead); - emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); - regData = sqlite3GetTempReg(pParse); - regRowid = sqlite3GetTempReg(pParse); - if( pDest->iPKey>=0 ){ - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); - addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY, - onError, "PRIMARY KEY must be unique", P4_STATIC); - sqlite3VdbeJumpHere(v, addr2); - autoIncStep(pParse, regAutoinc, regRowid); - }else if( pDest->pIndex==0 ){ - addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); + if( HasRowid(pSrc) ){ + sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead); + emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); + if( pDest->iPKey>=0 ){ + addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); + addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); + sqlite3RowidConstraint(pParse, onError, pDest); + sqlite3VdbeJumpHere(v, addr2); + autoIncStep(pParse, regAutoinc, regRowid); + }else if( pDest->pIndex==0 ){ + addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); + }else{ + addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); + assert( (pDest->tabFlags & TF_Autoincrement)==0 ); + } + sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData); + sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); + sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); + sqlite3VdbeChangeP4(v, -1, pDest->zName, 0); + sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); + sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); + sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); }else{ - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); - assert( (pDest->tabFlags & TF_Autoincrement)==0 ); + sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName); + sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName); } - sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData); - sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); - sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); - sqlite3VdbeChangeP4(v, -1, pDest->zName, 0); - sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } assert( pSrcIdx ); - sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); - sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); - pKey = sqlite3IndexKeyinfo(pParse, pSrcIdx); - sqlite3VdbeAddOp4(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc); + sqlite3VdbeSetP4KeyInfo(pParse, pSrcIdx); VdbeComment((v, "%s", pSrcIdx->zName)); - pKey = sqlite3IndexKeyinfo(pParse, pDestIdx); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest); + sqlite3VdbeSetP4KeyInfo(pParse, pDestIdx); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); @@ -93750,12 +94853,12 @@ static int xferOptimization( sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); sqlite3VdbeJumpHere(v, addr1); + sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); + sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); } sqlite3VdbeJumpHere(v, emptySrcTest); sqlite3ReleaseTempReg(pParse, regRowid); sqlite3ReleaseTempReg(pParse, regData); - sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); - sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); if( emptyDestTest ){ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0); sqlite3VdbeJumpHere(v, emptyDestTest); @@ -95257,11 +96360,13 @@ static const struct sPragmaNames { /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_AUTOMATIC_INDEX) { /* zName: */ "automatic_index", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_AutoIndex }, +#endif #endif { /* zName: */ "busy_timeout", /* ePragTyp: */ PragTyp_BUSY_TIMEOUT, @@ -95273,18 +96378,22 @@ static const struct sPragmaNames { /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "cache_spill", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_CacheSpill }, +#endif { /* zName: */ "case_sensitive_like", /* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE, /* ePragFlag: */ 0, /* iArg: */ 0 }, +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "checkpoint_fullfsync", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_CkptFullFSync }, +#endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) { /* zName: */ "collation_list", /* ePragTyp: */ PragTyp_COLLATION_LIST, @@ -95297,10 +96406,12 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "count_changes", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_CountRows }, +#endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN { /* zName: */ "data_store_directory", /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY, @@ -95319,16 +96430,20 @@ static const struct sPragmaNames { /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) { /* zName: */ "defer_foreign_keys", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_DeferFKs }, #endif +#endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "empty_result_callbacks", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_NullCallback }, +#endif #if !defined(SQLITE_OMIT_UTF16) { /* zName: */ "encoding", /* ePragTyp: */ PragTyp_ENCODING, @@ -95347,18 +96462,21 @@ static const struct sPragmaNames { /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) { /* zName: */ "foreign_keys", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_ForeignKeys }, #endif +#endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) { /* zName: */ "freelist_count", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "full_column_names", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, @@ -95367,6 +96485,7 @@ static const struct sPragmaNames { /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_FullFSync }, +#endif #if defined(SQLITE_HAS_CODEC) { /* zName: */ "hexkey", /* ePragTyp: */ PragTyp_HEXKEY, @@ -95377,12 +96496,14 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_CHECK) { /* zName: */ "ignore_check_constraints", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_IgnoreChecks }, #endif +#endif #if !defined(SQLITE_OMIT_AUTOVACUUM) { /* zName: */ "incremental_vacuum", /* ePragTyp: */ PragTyp_INCREMENTAL_VACUUM, @@ -95421,10 +96542,12 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "legacy_file_format", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_LegacyFileFmt }, +#endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE { /* zName: */ "lock_proxy_file", /* ePragTyp: */ PragTyp_LOCK_PROXY_FILE, @@ -95465,16 +96588,19 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "query_only", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_QueryOnly }, +#endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) { /* zName: */ "quick_check", /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "read_uncommitted", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, @@ -95483,16 +96609,19 @@ static const struct sPragmaNames { /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_RecTriggers }, +#endif #if defined(SQLITE_HAS_CODEC) { /* zName: */ "rekey", /* ePragTyp: */ PragTyp_REKEY, /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "reverse_unordered_selects", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_ReverseOrder }, +#endif #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) { /* zName: */ "schema_version", /* ePragTyp: */ PragTyp_HEADER_VALUE, @@ -95505,10 +96634,12 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "short_column_names", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_ShortColNames }, +#endif { /* zName: */ "shrink_memory", /* ePragTyp: */ PragTyp_SHRINK_MEMORY, /* ePragFlag: */ 0, @@ -95517,12 +96648,14 @@ static const struct sPragmaNames { /* ePragTyp: */ PragTyp_SOFT_HEAP_LIMIT, /* ePragFlag: */ 0, /* iArg: */ 0 }, +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if defined(SQLITE_DEBUG) { /* zName: */ "sql_trace", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_SqlTrace }, #endif +#endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) { /* zName: */ "stats", /* ePragTyp: */ PragTyp_STATS, @@ -95557,6 +96690,7 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if defined(SQLITE_DEBUG) { /* zName: */ "vdbe_addoptrace", /* ePragTyp: */ PragTyp_FLAG, @@ -95566,6 +96700,10 @@ static const struct sPragmaNames { /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace }, + { /* zName: */ "vdbe_eqp", + /* ePragTyp: */ PragTyp_FLAG, + /* ePragFlag: */ 0, + /* iArg: */ SQLITE_VdbeEQP }, { /* zName: */ "vdbe_listing", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, @@ -95575,6 +96713,7 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ SQLITE_VdbeTrace }, #endif +#endif #if !defined(SQLITE_OMIT_WAL) { /* zName: */ "wal_autocheckpoint", /* ePragTyp: */ PragTyp_WAL_AUTOCHECKPOINT, @@ -95585,12 +96724,14 @@ static const struct sPragmaNames { /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "writable_schema", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, +#endif }; -/* Number of pragmas: 56 on by default, 68 total. */ +/* Number of pragmas: 56 on by default, 69 total. */ /* End of the automatically generated pragma table. ***************************************************************************/ @@ -96554,8 +97695,7 @@ SQLITE_PRIVATE void sqlite3Pragma( int i, k; int nHidden = 0; Column *pCol; - Index *pPk; - for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} + Index *pPk = sqlite3PrimaryKeyIndex(pTab); sqlite3VdbeSetNumCols(v, 6); pParse->nMem = 6; sqlite3CodeVerifySchema(pParse, iDb); @@ -96638,8 +97778,8 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC); - for(i=0; inColumn; i++){ - int cnum = pIdx->aiColumn[i]; + for(i=0; inKeyCol; i++){ + i16 cnum = pIdx->aiColumn[i]; sqlite3VdbeAddOp2(v, OP_Integer, i, 1); sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2); assert( pTab->nCol>cnum ); @@ -96810,9 +97950,8 @@ SQLITE_PRIVATE void sqlite3Pragma( if( pIdx==0 ){ sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); }else{ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); } }else{ k = 0; @@ -96976,16 +98115,20 @@ SQLITE_PRIVATE void sqlite3Pragma( for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx; - sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt); - cnt++; + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt); + VdbeComment((v, "%s", pTab->zName)); + cnt++; + } for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt); + VdbeComment((v, "%s", pIdx->zName)); cnt++; } } /* Make sure sufficient number of registers have been allocated */ - pParse->nMem = MAX( pParse->nMem, cnt+7 ); + pParse->nMem = MAX( pParse->nMem, cnt+8 ); /* Do the b-tree integrity checks */ sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1); @@ -97003,58 +98146,63 @@ SQLITE_PRIVATE void sqlite3Pragma( */ for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); - Index *pIdx; + Index *pIdx, *pPk; + Index *pPrior = 0; int loopTop; + int iDataCur, iIdxCur; + int r1 = -1; if( pTab->pIndex==0 ) continue; + pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */ sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); sqlite3VdbeJumpHere(v, addr); sqlite3ExprCacheClear(pParse); - sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead); + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, + 1, 0, &iDataCur, &iIdxCur); + sqlite3VdbeAddOp2(v, OP_Integer, 0, 7); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - sqlite3VdbeAddOp2(v, OP_Integer, 0, 7+j); /* index entries counter */ + sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */ } - pParse->nMem = MAX(pParse->nMem, 7+j); - loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0) + 1; + pParse->nMem = MAX(pParse->nMem, 8+j); + sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); + loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - int jmp2, jmp3; - int r1; - static const VdbeOpList idxErr[] = { - { OP_AddImm, 1, -1, 0}, - { OP_String8, 0, 3, 0}, /* 1 */ - { OP_Rowid, 1, 4, 0}, - { OP_String8, 0, 5, 0}, /* 3 */ - { OP_String8, 0, 6, 0}, /* 4 */ - { OP_Concat, 4, 3, 3}, - { OP_Concat, 5, 3, 3}, - { OP_Concat, 6, 3, 3}, - { OP_ResultRow, 3, 1, 0}, - { OP_IfPos, 1, 0, 0}, /* 9 */ - { OP_Halt, 0, 0, 0}, - }; - r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0, &jmp3); - sqlite3VdbeAddOp2(v, OP_AddImm, 7+j, 1); /* increment entry count */ - jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1); - addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr); - sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT); - sqlite3VdbeJumpHere(v, addr+9); + int jmp2, jmp3, jmp4; + if( pPk==pIdx ) continue; + r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, + pPrior, r1); + pPrior = pIdx; + sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */ + jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, 0, r1, + pIdx->nColumn); + sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */ + sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, "row ", P4_STATIC); + sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); + sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, " missing from index ", + P4_STATIC); + sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); + sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, pIdx->zName, P4_TRANSIENT); + sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); + sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1); + jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); + sqlite3VdbeAddOp0(v, OP_Halt); + sqlite3VdbeJumpHere(v, jmp4); sqlite3VdbeJumpHere(v, jmp2); sqlite3VdbeResolveLabel(v, jmp3); } - sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop); + sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); sqlite3VdbeJumpHere(v, loopTop-1); #ifndef SQLITE_OMIT_BTREECOUNT sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, "wrong # of entries in index ", P4_STATIC); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ + if( pPk==pIdx ) continue; addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2); sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); - sqlite3VdbeAddOp2(v, OP_Count, j+2, 3); - sqlite3VdbeAddOp3(v, OP_Eq, 7+j, addr+8, 3); + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); + sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3); sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pIdx->zName, P4_TRANSIENT); sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7); @@ -97948,6 +99096,17 @@ SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ return i; } +/* +** Free all memory allocations in the pParse object +*/ +SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){ + if( pParse ){ + sqlite3 *db = pParse->db; + sqlite3DbFree(db, pParse->aLabel); + sqlite3ExprListDelete(db, pParse->pConstExpr); + } +} + /* ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. */ @@ -98105,6 +99264,7 @@ static int sqlite3Prepare( end_prepare: + sqlite3ParserReset(pParse); sqlite3StackFree(db, pParse); rc = sqlite3ApiExit(db, rc); assert( (rc&db->errMask)==rc ); @@ -98328,6 +99488,7 @@ static void clearSelect(sqlite3 *db, Select *p){ sqlite3SelectDelete(db, p->pPrior); sqlite3ExprDelete(db, p->pLimit); sqlite3ExprDelete(db, p->pOffset); + sqlite3WithDelete(db, p->pWith); } /* @@ -98760,13 +99921,13 @@ static void pushOntoSorter( */ static void codeOffset( Vdbe *v, /* Generate code into this VM */ - Select *p, /* The SELECT statement being coded */ + int iOffset, /* Register holding the offset counter */ int iContinue /* Jump here to skip the current record */ ){ - if( p->iOffset && iContinue!=0 ){ + if( iOffset>0 && iContinue!=0 ){ int addr; - sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1); - addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset); + sqlite3VdbeAddOp2(v, OP_AddImm, iOffset, -1); + addr = sqlite3VdbeAddOp1(v, OP_IfNeg, iOffset); sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue); VdbeComment((v, "skip OFFSET records")); sqlite3VdbeJumpHere(v, addr); @@ -98841,17 +100002,16 @@ struct DistinctCtx { ** This routine generates the code for the inside of the inner loop ** of a SELECT. ** -** If srcTab and nColumn are both zero, then the pEList expressions -** are evaluated in order to get the data for this row. If nColumn>0 -** then data is pulled from srcTab and pEList is used only to get the -** datatypes for each column. +** If srcTab is negative, then the pEList expressions +** are evaluated in order to get the data for this row. If srcTab is +** zero or more, then data is pulled from srcTab and pEList is used only +** to get number columns and the datatype for each column. */ static void selectInnerLoop( Parse *pParse, /* The parser context */ Select *p, /* The complete select statement being coded */ ExprList *pEList, /* List of values being extracted */ int srcTab, /* Pull data from this table */ - int nColumn, /* Number of columns in the source table */ ExprList *pOrderBy, /* If not NULL, sort results using this key */ DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */ SelectDest *pDest, /* How to dispose of the results */ @@ -98867,20 +100027,15 @@ static void selectInnerLoop( int nResultCol; /* Number of result columns */ assert( v ); - if( NEVER(v==0) ) return; assert( pEList!=0 ); hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP; if( pOrderBy==0 && !hasDistinct ){ - codeOffset(v, p, iContinue); + codeOffset(v, p->iOffset, iContinue); } /* Pull the requested columns. */ - if( nColumn>0 ){ - nResultCol = nColumn; - }else{ - nResultCol = pEList->nExpr; - } + nResultCol = pEList->nExpr; if( pDest->iSdst==0 ){ pDest->iSdst = pParse->nMem+1; pDest->nSdst = nResultCol; @@ -98889,26 +100044,24 @@ static void selectInnerLoop( assert( pDest->nSdst==nResultCol ); } regResult = pDest->iSdst; - if( nColumn>0 ){ - for(i=0; i=0 ){ + for(i=0; ia[i].zName)); } }else if( eDest!=SRT_Exists ){ /* If the destination is an EXISTS(...) expression, the actual ** values returned by the SELECT are not required. */ - sqlite3ExprCacheClear(pParse); - sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Output); + sqlite3ExprCodeExprList(pParse, pEList, regResult, + (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); } - nColumn = nResultCol; /* If the DISTINCT keyword was present on the SELECT statement ** and this row has been seen before, then do not make this row ** part of the result. */ if( hasDistinct ){ - assert( pEList!=0 ); - assert( pEList->nExpr==nColumn ); switch( pDistinct->eTnctType ){ case WHERE_DISTINCT_ORDERED: { VdbeOp *pOp; /* No longer required OpenEphemeral instr. */ @@ -98917,7 +100070,7 @@ static void selectInnerLoop( /* Allocate space for the previous row */ regPrev = pParse->nMem+1; - pParse->nMem += nColumn; + pParse->nMem += nResultCol; /* Change the OP_OpenEphemeral coded earlier to an OP_Null ** sets the MEM_Cleared bit on the first register of the @@ -98931,10 +100084,10 @@ static void selectInnerLoop( pOp->p1 = 1; pOp->p2 = regPrev; - iJump = sqlite3VdbeCurrentAddr(v) + nColumn; - for(i=0; ia[i].pExpr); - if( ieTnctType==WHERE_DISTINCT_UNORDERED ); - codeDistinct(pParse, pDistinct->tabTnct, iContinue, nColumn, regResult); + codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, regResult); break; } } if( pOrderBy==0 ){ - codeOffset(v, p, iContinue); + codeOffset(v, p->iOffset, iContinue); } } @@ -98971,7 +100124,7 @@ static void selectInnerLoop( case SRT_Union: { int r1; r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); sqlite3ReleaseTempReg(pParse, r1); break; @@ -98982,19 +100135,33 @@ static void selectInnerLoop( ** the temporary table iParm. */ case SRT_Except: { - sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nColumn); + sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol); break; } -#endif +#endif /* SQLITE_OMIT_COMPOUND_SELECT */ /* Store the result as data using a unique key. */ + case SRT_DistTable: case SRT_Table: case SRT_EphemTab: { int r1 = sqlite3GetTempReg(pParse); testcase( eDest==SRT_Table ); testcase( eDest==SRT_EphemTab ); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); +#ifndef SQLITE_OMIT_CTE + if( eDest==SRT_DistTable ){ + /* If the destination is DistTable, then cursor (iParm+1) is open + ** on an ephemeral index. If the current row is already present + ** in the index, do not write it to the output. If not, add the + ** current row to the index and proceed with writing it to the + ** output table as well. */ + int addr = sqlite3VdbeCurrentAddr(v) + 4; + sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1); + assert( pOrderBy==0 ); + } +#endif if( pOrderBy ){ pushOntoSorter(pParse, pOrderBy, p, r1); }else{ @@ -99014,7 +100181,7 @@ static void selectInnerLoop( ** item into the set table with bogus data. */ case SRT_Set: { - assert( nColumn==1 ); + assert( nResultCol==1 ); pDest->affSdst = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst); if( pOrderBy ){ @@ -99046,7 +100213,7 @@ static void selectInnerLoop( ** of the scan loop. */ case SRT_Mem: { - assert( nColumn==1 ); + assert( nResultCol==1 ); if( pOrderBy ){ pushOntoSorter(pParse, pOrderBy, p, regResult); }else{ @@ -99067,18 +100234,64 @@ static void selectInnerLoop( testcase( eDest==SRT_Output ); if( pOrderBy ){ int r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); pushOntoSorter(pParse, pOrderBy, p, r1); sqlite3ReleaseTempReg(pParse, r1); }else if( eDest==SRT_Coroutine ){ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); }else{ - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn); - sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn); + sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol); + sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol); } break; } +#ifndef SQLITE_OMIT_CTE + /* Write the results into a priority queue that is order according to + ** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an + ** index with pSO->nExpr+2 columns. Build a key using pSO for the first + ** pSO->nExpr columns, then make sure all keys are unique by adding a + ** final OP_Sequence column. The last column is the record as a blob. + */ + case SRT_DistQueue: + case SRT_Queue: { + int nKey; + int r1, r2, r3; + int addrTest = 0; + ExprList *pSO; + pSO = pDest->pOrderBy; + assert( pSO ); + nKey = pSO->nExpr; + r1 = sqlite3GetTempReg(pParse); + r2 = sqlite3GetTempRange(pParse, nKey+2); + r3 = r2+nKey+1; + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r3); + if( eDest==SRT_DistQueue ){ + /* If the destination is DistQueue, then cursor (iParm+1) is open + ** on a second ephemeral index that holds all values every previously + ** added to the queue. Only add this new value if it has never before + ** been added */ + addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0, r3, 0); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3); + sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); + } + for(i=0; ia[i].u.x.iOrderByCol - 1, + r2+i); + } + sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey); + sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); + if( addrTest ) sqlite3VdbeJumpHere(v, addrTest); + sqlite3ReleaseTempReg(pParse, r1); + sqlite3ReleaseTempRange(pParse, r2, nKey+2); + break; + } +#endif /* SQLITE_OMIT_CTE */ + + + #if !defined(SQLITE_OMIT_TRIGGER) /* Discard the results. This is used for SELECT statements inside ** the body of a TRIGGER. The purpose of such selects is to call @@ -99102,24 +100315,57 @@ static void selectInnerLoop( } /* -** Allocate a KeyInfo object sufficient for an index of N columns. -** -** Actually, always allocate one extra column for the rowid at the end -** of the index. So the KeyInfo returned will have space sufficient for -** N+1 columns. +** Allocate a KeyInfo object sufficient for an index of N key columns and +** X extra columns. */ -SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N){ - KeyInfo *p = sqlite3DbMallocZero(db, - sizeof(KeyInfo) + (N+1)*(sizeof(CollSeq*)+1)); +SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ + KeyInfo *p = sqlite3DbMallocZero(0, + sizeof(KeyInfo) + (N+X)*(sizeof(CollSeq*)+1)); if( p ){ - p->aSortOrder = (u8*)&p->aColl[N+1]; + p->aSortOrder = (u8*)&p->aColl[N+X]; p->nField = (u16)N; + p->nXField = (u16)X; p->enc = ENC(db); p->db = db; + p->nRef = 1; + }else{ + db->mallocFailed = 1; } return p; } +/* +** Deallocate a KeyInfo object +*/ +SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){ + if( p ){ + assert( p->nRef>0 ); + p->nRef--; + if( p->nRef==0 ) sqlite3DbFree(0, p); + } +} + +/* +** Make a new pointer to a KeyInfo object +*/ +SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo *p){ + if( p ){ + assert( p->nRef>0 ); + p->nRef++; + } + return p; +} + +#ifdef SQLITE_DEBUG +/* +** Return TRUE if a KeyInfo object can be change. The KeyInfo object +** can only be changed if this is just a single reference to the object. +** +** This routine is used only inside of assert() statements. +*/ +SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; } +#endif /* SQLITE_DEBUG */ + /* ** Given an expression list, generate a KeyInfo structure that records ** the collating sequence for each expression in that expression list. @@ -99132,10 +100378,9 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N){ ** ** Space to hold the KeyInfo structure is obtain from malloc. The calling ** function is responsible for seeing that this structure is eventually -** freed. Add the KeyInfo structure to the P4 field of an opcode using -** P4_KEYINFO_HANDOFF is the usual way of dealing with this. +** freed. */ -static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){ +static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList, int nExtra){ int nExpr; KeyInfo *pInfo; struct ExprList_item *pItem; @@ -99143,8 +100388,9 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){ int i; nExpr = pList->nExpr; - pInfo = sqlite3KeyInfoAlloc(db, nExpr); + pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra, 1); if( pInfo ){ + assert( sqlite3KeyInfoIsWriteable(pInfo) ); for(i=0, pItem=pList->a; ipExpr); @@ -99283,13 +100529,13 @@ static void generateSortTail( int ptab2 = pParse->nTab++; sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2); addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); - codeOffset(v, p, addrContinue); + codeOffset(v, p->iOffset, addrContinue); sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow); sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); }else{ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); - codeOffset(v, p, addrContinue); + codeOffset(v, p->iOffset, addrContinue); sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow); } switch( eDest ){ @@ -99468,7 +100714,7 @@ static const char *columnTypeImpl( sNC.pParse = pNC->pParse; zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol, &estWidth); } - }else if( ALWAYS(pTab->pSchema) ){ + }else if( pTab->pSchema ){ /* A real table */ assert( !pS ); if( iCol<0 ) iCol = pTab->iPKey; @@ -99629,8 +100875,9 @@ static void generateColumnNames( sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); } }else{ - sqlite3VdbeSetColName(v, i, COLNAME_NAME, - sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); + const char *z = pEList->a[i].zSpan; + z = z==0 ? sqlite3MPrintf(db, "column%d", i+1) : sqlite3DbStrDup(db, z); + sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, SQLITE_DYNAMIC); } } generateColumnTypes(pParse, pTabList, pEList); @@ -99830,7 +101077,7 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){ SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ Vdbe *v = pParse->pVdbe; if( v==0 ){ - v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db); + v = pParse->pVdbe = sqlite3VdbeCreate(pParse); #ifndef SQLITE_OMIT_TRACE if( v ){ sqlite3VdbeAddOp0(v, OP_Trace); @@ -99852,8 +101099,13 @@ SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ ** ** This routine changes the values of iLimit and iOffset only if ** a limit or offset is defined by pLimit and pOffset. iLimit and -** iOffset should have been preset to appropriate default values -** (usually but not always -1) prior to calling this routine. +** iOffset should have been preset to appropriate default values (zero) +** prior to calling this routine. +** +** The iOffset register (if it exists) is initialized to the value +** of the OFFSET. The iLimit register is initialized to LIMIT. Register +** iOffset+1 is initialized to LIMIT+OFFSET. +** ** Only if pLimit!=0 or pOffset!=0 do the limit registers get ** redefined. The UNION ALL operator uses this property to force ** the reuse of the same limit and offset registers across multiple @@ -99877,7 +101129,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ if( p->pLimit ){ p->iLimit = iLimit = ++pParse->nMem; v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ + assert( v!=0 ); if( sqlite3ExprIsInteger(p->pLimit, &n) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); @@ -99932,9 +101184,204 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ } return pRet; } -#endif /* SQLITE_OMIT_COMPOUND_SELECT */ -/* Forward reference */ +/* +** The select statement passed as the second parameter is a compound SELECT +** with an ORDER BY clause. This function allocates and returns a KeyInfo +** structure suitable for implementing the ORDER BY. +** +** Space to hold the KeyInfo structure is obtained from malloc. The calling +** function is responsible for ensuring that this structure is eventually +** freed. +*/ +static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ + ExprList *pOrderBy = p->pOrderBy; + int nOrderBy = p->pOrderBy->nExpr; + sqlite3 *db = pParse->db; + KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); + if( pRet ){ + int i; + for(i=0; ia[i]; + Expr *pTerm = pItem->pExpr; + CollSeq *pColl; + + if( pTerm->flags & EP_Collate ){ + pColl = sqlite3ExprCollSeq(pParse, pTerm); + }else{ + pColl = multiSelectCollSeq(pParse, p, pItem->u.x.iOrderByCol-1); + if( pColl==0 ) pColl = db->pDfltColl; + pOrderBy->a[i].pExpr = + sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName); + } + assert( sqlite3KeyInfoIsWriteable(pRet) ); + pRet->aColl[i] = pColl; + pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder; + } + } + + return pRet; +} + +#ifndef SQLITE_OMIT_CTE +/* +** This routine generates VDBE code to compute the content of a WITH RECURSIVE +** query of the form: +** +** AS ( UNION [ALL] ) +** \___________/ \_______________/ +** p->pPrior p +** +** +** There is exactly one reference to the recursive-table in the FROM clause +** of recursive-query, marked with the SrcList->a[].isRecursive flag. +** +** The setup-query runs once to generate an initial set of rows that go +** into a Queue table. Rows are extracted from the Queue table one by +** one. Each row extracted from Queue is output to pDest. Then the single +** extracted row (now in the iCurrent table) becomes the content of the +** recursive-table for a recursive-query run. The output of the recursive-query +** is added back into the Queue table. Then another row is extracted from Queue +** and the iteration continues until the Queue table is empty. +** +** If the compound query operator is UNION then no duplicate rows are ever +** inserted into the Queue table. The iDistinct table keeps a copy of all rows +** that have ever been inserted into Queue and causes duplicates to be +** discarded. If the operator is UNION ALL, then duplicates are allowed. +** +** If the query has an ORDER BY, then entries in the Queue table are kept in +** ORDER BY order and the first entry is extracted for each cycle. Without +** an ORDER BY, the Queue table is just a FIFO. +** +** If a LIMIT clause is provided, then the iteration stops after LIMIT rows +** have been output to pDest. A LIMIT of zero means to output no rows and a +** negative LIMIT means to output all rows. If there is also an OFFSET clause +** with a positive value, then the first OFFSET outputs are discarded rather +** than being sent to pDest. The LIMIT count does not begin until after OFFSET +** rows have been skipped. +*/ +static void generateWithRecursiveQuery( + Parse *pParse, /* Parsing context */ + Select *p, /* The recursive SELECT to be coded */ + SelectDest *pDest /* What to do with query results */ +){ + SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */ + int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */ + Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ + Select *pSetup = p->pPrior; /* The setup query */ + int addrTop; /* Top of the loop */ + int addrCont, addrBreak; /* CONTINUE and BREAK addresses */ + int iCurrent = 0; /* The Current table */ + int regCurrent; /* Register holding Current table */ + int iQueue; /* The Queue table */ + int iDistinct = 0; /* To ensure unique results if UNION */ + int eDest = SRT_Table; /* How to write to Queue */ + SelectDest destQueue; /* SelectDest targetting the Queue table */ + int i; /* Loop counter */ + int rc; /* Result code */ + ExprList *pOrderBy; /* The ORDER BY clause */ + Expr *pLimit, *pOffset; /* Saved LIMIT and OFFSET */ + int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */ + + /* Obtain authorization to do a recursive query */ + if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; + + /* Process the LIMIT and OFFSET clauses, if they exist */ + addrBreak = sqlite3VdbeMakeLabel(v); + computeLimitRegisters(pParse, p, addrBreak); + pLimit = p->pLimit; + pOffset = p->pOffset; + regLimit = p->iLimit; + regOffset = p->iOffset; + p->pLimit = p->pOffset = 0; + p->iLimit = p->iOffset = 0; + pOrderBy = p->pOrderBy; + + /* Locate the cursor number of the Current table */ + for(i=0; ALWAYS(inSrc); i++){ + if( pSrc->a[i].isRecursive ){ + iCurrent = pSrc->a[i].iCursor; + break; + } + } + + /* Allocate cursors numbers for Queue and Distinct. The cursor number for + ** the Distinct table must be exactly one greater than Queue in order + ** for the SRT_DistTable and SRT_DistQueue destinations to work. */ + iQueue = pParse->nTab++; + if( p->op==TK_UNION ){ + eDest = pOrderBy ? SRT_DistQueue : SRT_DistTable; + iDistinct = pParse->nTab++; + }else{ + eDest = pOrderBy ? SRT_Queue : SRT_Table; + } + sqlite3SelectDestInit(&destQueue, eDest, iQueue); + + /* Allocate cursors for Current, Queue, and Distinct. */ + regCurrent = ++pParse->nMem; + sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol); + if( pOrderBy ){ + KeyInfo *pKeyInfo = multiSelectOrderByKeyInfo(pParse, p, 1); + sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0, + (char*)pKeyInfo, P4_KEYINFO); + destQueue.pOrderBy = pOrderBy; + }else{ + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol); + } + VdbeComment((v, "Queue table")); + if( iDistinct ){ + p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0); + p->selFlags |= SF_UsesEphemeral; + } + + /* Detach the ORDER BY clause from the compound SELECT */ + p->pOrderBy = 0; + + /* Store the results of the setup-query in Queue. */ + rc = sqlite3Select(pParse, pSetup, &destQueue); + if( rc ) goto end_of_recursive_query; + + /* Find the next row in the Queue and output that row */ + addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak); + + /* Transfer the next row in Queue over to Current */ + sqlite3VdbeAddOp1(v, OP_NullRow, iCurrent); /* To reset column cache */ + if( pOrderBy ){ + sqlite3VdbeAddOp3(v, OP_Column, iQueue, pOrderBy->nExpr+1, regCurrent); + }else{ + sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent); + } + sqlite3VdbeAddOp1(v, OP_Delete, iQueue); + + /* Output the single row in Current */ + addrCont = sqlite3VdbeMakeLabel(v); + codeOffset(v, regOffset, addrCont); + selectInnerLoop(pParse, p, p->pEList, iCurrent, + 0, 0, pDest, addrCont, addrBreak); + if( regLimit ) sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1); + sqlite3VdbeResolveLabel(v, addrCont); + + /* Execute the recursive SELECT taking the single row in Current as + ** the value for the recursive-table. Store the results in the Queue. + */ + p->pPrior = 0; + sqlite3Select(pParse, p, &destQueue); + assert( p->pPrior==0 ); + p->pPrior = pSetup; + + /* Keep running the loop until the Queue is empty */ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); + sqlite3VdbeResolveLabel(v, addrBreak); + +end_of_recursive_query: + p->pOrderBy = pOrderBy; + p->pLimit = pLimit; + p->pOffset = pOffset; + return; +} +#endif /* SQLITE_OMIT_CTE */ + +/* Forward references */ static int multiSelectOrderBy( Parse *pParse, /* Parsing context */ Select *p, /* The right-most of SELECTs to be coded */ @@ -99942,7 +101389,6 @@ static int multiSelectOrderBy( ); -#ifndef SQLITE_OMIT_COMPOUND_SELECT /* ** This routine is called to process a compound query form from ** two or more separate queries using UNION, UNION ALL, EXCEPT, or @@ -99986,14 +101432,15 @@ static int multiSelect( Select *pDelete = 0; /* Chain of simple selects to delete */ sqlite3 *db; /* Database connection */ #ifndef SQLITE_OMIT_EXPLAIN - int iSub1; /* EQP id of left-hand query */ - int iSub2; /* EQP id of right-hand query */ + int iSub1 = 0; /* EQP id of left-hand query */ + int iSub2 = 0; /* EQP id of right-hand query */ #endif /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. */ assert( p && p->pPrior ); /* Calling function guarantees this much */ + assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION ); db = pParse->db; pPrior = p->pPrior; assert( pPrior->pRightmost!=pPrior ); @@ -100039,11 +101486,17 @@ static int multiSelect( goto multi_select_end; } +#ifndef SQLITE_OMIT_CTE + if( p->selFlags & SF_Recursive ){ + generateWithRecursiveQuery(pParse, p, &dest); + }else +#endif + /* Compound SELECTs that have an ORDER BY clause are handled separately. */ if( p->pOrderBy ){ return multiSelectOrderBy(pParse, p, pDest); - } + }else /* Generate code for the left and right SELECT statements. */ @@ -100178,7 +101631,7 @@ static int multiSelect( computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); iStart = sqlite3VdbeCurrentAddr(v); - selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr, + selectInnerLoop(pParse, p, p->pEList, unionTab, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); @@ -100256,7 +101709,7 @@ static int multiSelect( iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1); sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); sqlite3ReleaseTempReg(pParse, r1); - selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr, + selectInnerLoop(pParse, p, p->pEList, tab1, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); @@ -100287,7 +101740,7 @@ static int multiSelect( assert( p->pRightmost==p ); nCol = p->pEList->nExpr; - pKeyInfo = sqlite3KeyInfoAlloc(db, nCol); + pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); if( !pKeyInfo ){ rc = SQLITE_NOMEM; goto multi_select_end; @@ -100309,11 +101762,12 @@ static int multiSelect( break; } sqlite3VdbeChangeP2(v, addr, nCol); - sqlite3VdbeChangeP4(v, addr, (char*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo), + P4_KEYINFO); pLoop->addrOpenEphm[i] = -1; } } - sqlite3DbFree(db, pKeyInfo); + sqlite3KeyInfoUnref(pKeyInfo); } multi_select_end: @@ -100352,7 +101806,6 @@ static int generateOutputSubroutine( int regReturn, /* The return address register */ int regPrev, /* Previous result register. No uniqueness if 0 */ KeyInfo *pKeyInfo, /* For comparing with previous entry */ - int p4type, /* The p4 type for pKeyInfo */ int iBreak /* Jump here if we hit the LIMIT */ ){ Vdbe *v = pParse->pVdbe; @@ -100368,7 +101821,7 @@ static int generateOutputSubroutine( int j1, j2; j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst, - (char*)pKeyInfo, p4type); + (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); sqlite3VdbeJumpHere(v, j1); sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1); @@ -100378,7 +101831,7 @@ static int generateOutputSubroutine( /* Suppress the first OFFSET entries if there is an OFFSET clause */ - codeOffset(v, p, iContinue); + codeOffset(v, p->iOffset, iContinue); switch( pDest->eDest ){ /* Store the result as data using a unique key. @@ -100638,8 +102091,8 @@ static int multiSelectOrderBy( for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){ struct ExprList_item *pItem; for(j=0, pItem=pOrderBy->a; jiOrderByCol>0 ); - if( pItem->iOrderByCol==i ) break; + assert( pItem->u.x.iOrderByCol>0 ); + if( pItem->u.x.iOrderByCol==i ) break; } if( j==nOrderBy ){ Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); @@ -100647,7 +102100,7 @@ static int multiSelectOrderBy( pNew->flags |= EP_IntValue; pNew->u.iValue = i; pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); - if( pOrderBy ) pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i; + if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i; } } } @@ -100663,26 +102116,11 @@ static int multiSelectOrderBy( if( aPermute ){ struct ExprList_item *pItem; for(i=0, pItem=pOrderBy->a; iiOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr ); - aPermute[i] = pItem->iOrderByCol - 1; - } - pKeyMerge = sqlite3KeyInfoAlloc(db, nOrderBy); - if( pKeyMerge ){ - for(i=0; ia[i].pExpr; - if( pTerm->flags & EP_Collate ){ - pColl = sqlite3ExprCollSeq(pParse, pTerm); - }else{ - pColl = multiSelectCollSeq(pParse, p, aPermute[i]); - if( pColl==0 ) pColl = db->pDfltColl; - pOrderBy->a[i].pExpr = - sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName); - } - pKeyMerge->aColl[i] = pColl; - pKeyMerge->aSortOrder[i] = pOrderBy->a[i].sortOrder; - } + assert( pItem->u.x.iOrderByCol>0 + && pItem->u.x.iOrderByCol<=p->pEList->nExpr ); + aPermute[i] = pItem->u.x.iOrderByCol - 1; } + pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1); }else{ pKeyMerge = 0; } @@ -100704,8 +102142,9 @@ static int multiSelectOrderBy( regPrev = pParse->nMem+1; pParse->nMem += nExpr+1; sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev); - pKeyDup = sqlite3KeyInfoAlloc(db, nExpr); + pKeyDup = sqlite3KeyInfoAlloc(db, nExpr, 1); if( pKeyDup ){ + assert( sqlite3KeyInfoIsWriteable(pKeyDup) ); for(i=0; iaColl[i] = multiSelectCollSeq(pParse, p, i); pKeyDup->aSortOrder[i] = 0; @@ -100787,7 +102226,7 @@ static int multiSelectOrderBy( VdbeNoopComment((v, "Output routine for A")); addrOutA = generateOutputSubroutine(pParse, p, &destA, pDest, regOutA, - regPrev, pKeyDup, P4_KEYINFO_HANDOFF, labelEnd); + regPrev, pKeyDup, labelEnd); /* Generate a subroutine that outputs the current row of the B ** select as the next output row of the compound select. @@ -100796,8 +102235,9 @@ static int multiSelectOrderBy( VdbeNoopComment((v, "Output routine for B")); addrOutB = generateOutputSubroutine(pParse, p, &destB, pDest, regOutB, - regPrev, pKeyDup, P4_KEYINFO_STATIC, labelEnd); + regPrev, pKeyDup, labelEnd); } + sqlite3KeyInfoUnref(pKeyDup); /* Generate a subroutine to run when the results from select A ** are exhausted and only data in select B remains. @@ -100876,7 +102316,7 @@ static int multiSelectOrderBy( sqlite3VdbeResolveLabel(v, labelCmpr); sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, - (char*)pKeyMerge, P4_KEYINFO_HANDOFF); + (char*)pKeyMerge, P4_KEYINFO); sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); @@ -101103,6 +102543,14 @@ static void substSelect( ** (21) The subquery does not use LIMIT or the outer query is not ** DISTINCT. (See ticket [752e1646fc]). ** +** (22) The subquery is not a recursive CTE. +** +** (23) The parent is not a recursive CTE, or the sub-query is not a +** compound query. This restriction is because transforming the +** parent to a compound query confuses the code that handles +** recursive queries in multiSelect(). +** +** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. @@ -101174,6 +102622,8 @@ static int flattenSubquery( if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){ return 0; /* Restriction (21) */ } + if( pSub->selFlags & SF_Recursive ) return 0; /* Restriction (22) */ + if( (p->selFlags & SF_Recursive) && pSub->pPrior ) return 0; /* (23) */ /* OBSOLETE COMMENT 1: ** Restriction 3: If the subquery is a join, make sure the subquery is @@ -101241,7 +102691,7 @@ static int flattenSubquery( if( p->pOrderBy ){ int ii; for(ii=0; iipOrderBy->nExpr; ii++){ - if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0; + if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0; } } } @@ -101655,6 +103105,197 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ return WRC_Continue; } +#ifndef SQLITE_OMIT_CTE +/* +** Argument pWith (which may be NULL) points to a linked list of nested +** WITH contexts, from inner to outermost. If the table identified by +** FROM clause element pItem is really a common-table-expression (CTE) +** then return a pointer to the CTE definition for that table. Otherwise +** return NULL. +** +** If a non-NULL value is returned, set *ppContext to point to the With +** object that the returned CTE belongs to. +*/ +static struct Cte *searchWith( + With *pWith, /* Current outermost WITH clause */ + struct SrcList_item *pItem, /* FROM clause element to resolve */ + With **ppContext /* OUT: WITH clause return value belongs to */ +){ + const char *zName; + if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){ + With *p; + for(p=pWith; p; p=p->pOuter){ + int i; + for(i=0; inCte; i++){ + if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){ + *ppContext = p; + return &p->a[i]; + } + } + } + } + return 0; +} + +/* The code generator maintains a stack of active WITH clauses +** with the inner-most WITH clause being at the top of the stack. +** +** This routine pushes the WITH clause passed as the second argument +** onto the top of the stack. If argument bFree is true, then this +** WITH clause will never be popped from the stack. In this case it +** should be freed along with the Parse object. In other cases, when +** bFree==0, the With object will be freed along with the SELECT +** statement with which it is associated. +*/ +SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ + assert( bFree==0 || pParse->pWith==0 ); + if( pWith ){ + pWith->pOuter = pParse->pWith; + pParse->pWith = pWith; + pParse->bFreeWith = bFree; + } +} + +/* +** This function checks if argument pFrom refers to a CTE declared by +** a WITH clause on the stack currently maintained by the parser. And, +** if currently processing a CTE expression, if it is a recursive +** reference to the current CTE. +** +** If pFrom falls into either of the two categories above, pFrom->pTab +** and other fields are populated accordingly. The caller should check +** (pFrom->pTab!=0) to determine whether or not a successful match +** was found. +** +** Whether or not a match is found, SQLITE_OK is returned if no error +** occurs. If an error does occur, an error message is stored in the +** parser and some error code other than SQLITE_OK returned. +*/ +static int withExpand( + Walker *pWalker, + struct SrcList_item *pFrom +){ + Parse *pParse = pWalker->pParse; + sqlite3 *db = pParse->db; + struct Cte *pCte; /* Matched CTE (or NULL if no match) */ + With *pWith; /* WITH clause that pCte belongs to */ + + assert( pFrom->pTab==0 ); + + pCte = searchWith(pParse->pWith, pFrom, &pWith); + if( pCte ){ + Table *pTab; + ExprList *pEList; + Select *pSel; + Select *pLeft; /* Left-most SELECT statement */ + int bMayRecursive; /* True if compound joined by UNION [ALL] */ + With *pSavedWith; /* Initial value of pParse->pWith */ + + /* If pCte->zErr is non-NULL at this point, then this is an illegal + ** recursive reference to CTE pCte. Leave an error in pParse and return + ** early. If pCte->zErr is NULL, then this is not a recursive reference. + ** In this case, proceed. */ + if( pCte->zErr ){ + sqlite3ErrorMsg(pParse, pCte->zErr, pCte->zName); + return SQLITE_ERROR; + } + + assert( pFrom->pTab==0 ); + pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); + if( pTab==0 ) return WRC_Abort; + pTab->nRef = 1; + pTab->zName = sqlite3DbStrDup(db, pCte->zName); + pTab->iPKey = -1; + pTab->nRowEst = 1048576; + pTab->tabFlags |= TF_Ephemeral; + pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); + if( db->mallocFailed ) return SQLITE_NOMEM; + assert( pFrom->pSelect ); + + /* Check if this is a recursive CTE. */ + pSel = pFrom->pSelect; + bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION ); + if( bMayRecursive ){ + int i; + SrcList *pSrc = pFrom->pSelect->pSrc; + for(i=0; inSrc; i++){ + struct SrcList_item *pItem = &pSrc->a[i]; + if( pItem->zDatabase==0 + && pItem->zName!=0 + && 0==sqlite3StrICmp(pItem->zName, pCte->zName) + ){ + pItem->pTab = pTab; + pItem->isRecursive = 1; + pTab->nRef++; + pSel->selFlags |= SF_Recursive; + } + } + } + + /* Only one recursive reference is permitted. */ + if( pTab->nRef>2 ){ + sqlite3ErrorMsg( + pParse, "multiple references to recursive table: %s", pCte->zName + ); + return SQLITE_ERROR; + } + assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 )); + + pCte->zErr = "circular reference: %s"; + pSavedWith = pParse->pWith; + pParse->pWith = pWith; + sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel); + + for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); + pEList = pLeft->pEList; + if( pCte->pCols ){ + if( pEList->nExpr!=pCte->pCols->nExpr ){ + sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns", + pCte->zName, pEList->nExpr, pCte->pCols->nExpr + ); + pParse->pWith = pSavedWith; + return SQLITE_ERROR; + } + pEList = pCte->pCols; + } + + selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol); + if( bMayRecursive ){ + if( pSel->selFlags & SF_Recursive ){ + pCte->zErr = "multiple recursive references: %s"; + }else{ + pCte->zErr = "recursive reference in a subquery: %s"; + } + sqlite3WalkSelect(pWalker, pSel); + } + pCte->zErr = 0; + pParse->pWith = pSavedWith; + } + + return SQLITE_OK; +} +#endif + +#ifndef SQLITE_OMIT_CTE +/* +** If the SELECT passed as the second argument has an associated WITH +** clause, pop it from the stack stored as part of the Parse object. +** +** This function is used as the xSelectCallback2() callback by +** sqlite3SelectExpand() when walking a SELECT tree to resolve table +** names and other FROM clause elements. +*/ +static void selectPopWith(Walker *pWalker, Select *p){ + Parse *pParse = pWalker->pParse; + if( p->pWith ){ + assert( pParse->pWith==p->pWith ); + pParse->pWith = p->pWith->pOuter; + } +} +#else +#define selectPopWith 0 +#endif + /* ** This routine is a Walker callback for "expanding" a SELECT statement. ** "Expanding" means to do the following: @@ -101698,6 +103339,7 @@ static int selectExpander(Walker *pWalker, Select *p){ } pTabList = p->pSrc; pEList = p->pEList; + sqlite3WithPush(pParse, p->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. @@ -101710,12 +103352,21 @@ static int selectExpander(Walker *pWalker, Select *p){ */ for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab; + assert( pFrom->isRecursive==0 || pFrom->pTab ); + if( pFrom->isRecursive ) continue; if( pFrom->pTab!=0 ){ /* This statement has already been prepared. There is no need ** to go further. */ assert( i==0 ); +#ifndef SQLITE_OMIT_CTE + selectPopWith(pWalker, p); +#endif return WRC_Prune; } +#ifndef SQLITE_OMIT_CTE + if( withExpand(pWalker, pFrom) ) return WRC_Abort; + if( pFrom->pTab ) {} else +#endif if( pFrom->zName==0 ){ #ifndef SQLITE_OMIT_SUBQUERY Select *pSel = pFrom->pSelect; @@ -101978,6 +103629,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; + w.xSelectCallback2 = selectPopWith; sqlite3WalkSelect(&w, pSelect); } @@ -101996,7 +103648,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ ** at that point because identifiers had not yet been resolved. This ** routine is called after identifier resolution. */ -static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ +static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ Parse *pParse; int i; SrcList *pTabList; @@ -102012,13 +103664,13 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){ /* A sub-query in the FROM clause of a SELECT */ Select *pSel = pFrom->pSelect; - assert( pSel ); - while( pSel->pPrior ) pSel = pSel->pPrior; - selectAddColumnTypeAndCollation(pParse, pTab, pSel); + if( pSel ){ + while( pSel->pPrior ) pSel = pSel->pPrior; + selectAddColumnTypeAndCollation(pParse, pTab, pSel); + } } } } - return WRC_Continue; } #endif @@ -102034,10 +103686,9 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ #ifndef SQLITE_OMIT_SUBQUERY Walker w; memset(&w, 0, sizeof(w)); - w.xSelectCallback = selectAddSubqueryTypeInfo; + w.xSelectCallback2 = selectAddSubqueryTypeInfo; w.xExprCallback = exprWalkNoop; w.pParse = pParse; - w.bSelectDepthFirst = 1; sqlite3WalkSelect(&w, pSelect); #endif } @@ -102084,14 +103735,23 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ Vdbe *v = pParse->pVdbe; int i; struct AggInfo_func *pFunc; - if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){ - return; - } + int nReg = pAggInfo->nFunc + pAggInfo->nColumn; + if( nReg==0 ) return; +#ifdef SQLITE_DEBUG + /* Verify that all AggInfo registers are within the range specified by + ** AggInfo.mnReg..AggInfo.mxReg */ + assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 ); for(i=0; inColumn; i++){ - sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem); + assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg + && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg ); } + for(i=0; inFunc; i++){ + assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg + && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg ); + } +#endif + sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); for(pFunc=pAggInfo->aFunc, i=0; inFunc; i++, pFunc++){ - sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem); if( pFunc->iDistinct>=0 ){ Expr *pE = pFunc->pExpr; assert( !ExprHasProperty(pE, EP_xIsSelect) ); @@ -102100,9 +103760,9 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ "argument"); pFunc->iDistinct = -1; }else{ - KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList); + KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0, - (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + (char*)pKeyInfo, P4_KEYINFO); } } } @@ -102137,7 +103797,6 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ struct AggInfo_col *pC; pAggInfo->directMode = 1; - sqlite3ExprCacheClear(pParse); for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ int nArg; int addrNext = 0; @@ -102147,7 +103806,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); - sqlite3ExprCodeExprList(pParse, pList, regAgg, 1); + sqlite3ExprCodeExprList(pParse, pList, regAgg, SQLITE_ECEL_DUP); }else{ nArg = 0; regAgg = 0; @@ -102234,50 +103893,8 @@ static void explainSimpleCount( /* ** Generate code for the SELECT statement given in the p argument. ** -** The results are distributed in various ways depending on the -** contents of the SelectDest structure pointed to by argument pDest -** as follows: -** -** pDest->eDest Result -** ------------ ------------------------------------------- -** SRT_Output Generate a row of output (using the OP_ResultRow -** opcode) for each row in the result set. -** -** SRT_Mem Only valid if the result is a single column. -** Store the first column of the first result row -** in register pDest->iSDParm then abandon the rest -** of the query. This destination implies "LIMIT 1". -** -** SRT_Set The result must be a single column. Store each -** row of result as the key in table pDest->iSDParm. -** Apply the affinity pDest->affSdst before storing -** results. Used to implement "IN (SELECT ...)". -** -** SRT_Union Store results as a key in a temporary table -** identified by pDest->iSDParm. -** -** SRT_Except Remove results from the temporary table pDest->iSDParm. -** -** SRT_Table Store results in temporary table pDest->iSDParm. -** This is like SRT_EphemTab except that the table -** is assumed to already be open. -** -** SRT_EphemTab Create an temporary table pDest->iSDParm and store -** the result there. The cursor is left open after -** returning. This is like SRT_Table except that -** this destination uses OP_OpenEphemeral to create -** the table first. -** -** SRT_Coroutine Generate a co-routine that returns a new row of -** results each time it is invoked. The entry point -** of the co-routine is stored in register pDest->iSDParm. -** -** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result -** set is not empty. -** -** SRT_Discard Throw the results away. This is used by SELECT -** statements within triggers whose only purpose is -** the side-effects of functions. +** The results are returned according to the SelectDest structure. +** See comments in sqliteInt.h for further information. ** ** This routine returns the number of errors. If any errors are ** encountered, then an appropriate error message is left in @@ -102552,12 +104169,12 @@ SQLITE_PRIVATE int sqlite3Select( */ if( pOrderBy ){ KeyInfo *pKeyInfo; - pKeyInfo = keyInfoFromExprList(pParse, pOrderBy); + pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 0); pOrderBy->iECursor = pParse->nTab++; p->addrOpenEphm[2] = addrSortIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pOrderBy->iECursor, pOrderBy->nExpr+2, 0, - (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + (char*)pKeyInfo, P4_KEYINFO); }else{ addrSortIndex = -1; } @@ -102584,8 +104201,8 @@ SQLITE_PRIVATE int sqlite3Select( sDistinct.tabTnct = pParse->nTab++; sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, sDistinct.tabTnct, 0, 0, - (char*)keyInfoFromExprList(pParse, p->pEList), - P4_KEYINFO_HANDOFF); + (char*)keyInfoFromExprList(pParse, p->pEList, 0), + P4_KEYINFO); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED; }else{ @@ -102618,7 +104235,7 @@ SQLITE_PRIVATE int sqlite3Select( } /* Use the standard inner loop. */ - selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, &sDistinct, pDest, + selectInnerLoop(pParse, p, pEList, -1, pOrderBy, &sDistinct, pDest, sqlite3WhereContinueLabel(pWInfo), sqlite3WhereBreakLabel(pWInfo)); @@ -102648,10 +104265,10 @@ SQLITE_PRIVATE int sqlite3Select( struct ExprList_item *pItem; /* For looping over expression in a list */ for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){ - pItem->iAlias = 0; + pItem->u.x.iAlias = 0; } for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ - pItem->iAlias = 0; + pItem->u.x.iAlias = 0; } if( p->nSelectRow>100 ) p->nSelectRow = 100; }else{ @@ -102670,6 +104287,7 @@ SQLITE_PRIVATE int sqlite3Select( sNC.pParse = pParse; sNC.pSrcList = pTabList; sNC.pAggInfo = &sAggInfo; + sAggInfo.mnReg = pParse->nMem+1; sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0; sAggInfo.pGroupBy = pGroupBy; sqlite3ExprAnalyzeAggList(&sNC, pEList); @@ -102684,6 +104302,7 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList); sNC.ncFlags &= ~NC_InAggFunc; } + sAggInfo.mxReg = pParse->nMem; if( db->mallocFailed ) goto select_end; /* Processing for aggregates with GROUP BY is very different and @@ -102706,10 +104325,10 @@ SQLITE_PRIVATE int sqlite3Select( ** will be converted into a Noop. */ sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy); + pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0); addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, - 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); + 0, (char*)pKeyInfo, P4_KEYINFO); /* Initialize memory locations used by GROUP BY aggregate processing */ @@ -102823,7 +104442,7 @@ SQLITE_PRIVATE int sqlite3Select( } } sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, - (char*)pKeyInfo, P4_KEYINFO); + (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); j1 = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); @@ -102888,7 +104507,7 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); finalizeAggFunctions(pParse, &sAggInfo); sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL); - selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy, + selectInnerLoop(pParse, p, p->pEList, -1, pOrderBy, &sDistinct, pDest, addrOutputRow+1, addrSetAbort); sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); @@ -102933,11 +104552,12 @@ SQLITE_PRIVATE int sqlite3Select( ** ** (2011-04-15) Do not do a full scan of an unordered index. ** - ** (2013-10-03) Do not count the entires in a partial index. + ** (2013-10-03) Do not count the entries in a partial index. ** ** In practice the KeyInfo structure will not be used. It is only ** passed to keep OP_OpenRead happy. */ + if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->bUnordered==0 && pIdx->szIdxRowszTabRow @@ -102949,13 +104569,13 @@ SQLITE_PRIVATE int sqlite3Select( } if( pBest ){ iRoot = pBest->tnum; - pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest); + pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); } /* Open a read-only cursor, execute the OP_Count, close the cursor. */ - sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb); + sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1); if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF); + sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); } sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); @@ -103030,7 +104650,7 @@ SQLITE_PRIVATE int sqlite3Select( pOrderBy = 0; sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL); - selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, 0, + selectInnerLoop(pParse, p, p->pEList, -1, 0, 0, pDest, addrEnd, addrEnd); sqlite3ExprListDelete(db, pDel); } @@ -103774,25 +105394,21 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep( sqlite3 *db, /* The database connection */ Token *pTableName, /* Name of the table into which we insert */ IdList *pColumn, /* List of columns in pTableName to insert into */ - ExprList *pEList, /* The VALUE clause: a list of values to be inserted */ Select *pSelect, /* A SELECT statement that supplies values */ u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ ){ TriggerStep *pTriggerStep; - assert(pEList == 0 || pSelect == 0); - assert(pEList != 0 || pSelect != 0 || db->mallocFailed); + assert(pSelect != 0 || db->mallocFailed); pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName); if( pTriggerStep ){ pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); pTriggerStep->pIdList = pColumn; - pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); pTriggerStep->orconf = orconf; }else{ sqlite3IdListDelete(db, pColumn); } - sqlite3ExprListDelete(db, pEList); sqlite3SelectDelete(db, pSelect); return pTriggerStep; @@ -104130,7 +105746,6 @@ static int codeTriggerProgram( case TK_INSERT: { sqlite3Insert(pParse, targetSrcList(pParse, pStep), - sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3SelectDup(db, pStep->pSelect, 0), sqlite3IdListDup(db, pStep->pIdList), pParse->eOrconf @@ -104161,7 +105776,7 @@ static int codeTriggerProgram( return 0; } -#ifdef SQLITE_DEBUG +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS /* ** This function is used to add VdbeComment() annotations to a VDBE ** program. It is not used in production code, only for debugging. @@ -104301,6 +105916,7 @@ static TriggerPrg *codeRowTrigger( assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); + sqlite3ParserReset(pSubParse); sqlite3StackFree(db, pSubParse); return pPrg; @@ -104586,7 +106202,7 @@ SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); } #ifndef SQLITE_OMIT_FLOATING_POINT - if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ + if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); } #endif @@ -104609,25 +106225,32 @@ SQLITE_PRIVATE void sqlite3Update( ){ int i, j; /* Loop counters */ Table *pTab; /* The table to be updated */ - int addr = 0; /* VDBE instruction address of the start of the loop */ + int addrTop = 0; /* VDBE instruction address of the start of the loop */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Vdbe *v; /* The virtual database engine */ Index *pIdx; /* For looping over indices */ + Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */ int nIdx; /* Number of indices that need updating */ - int iCur; /* VDBE Cursor number of pTab */ + int iBaseCur; /* Base cursor number */ + int iDataCur; /* Cursor for the canonical data btree */ + int iIdxCur; /* Cursor for the first index */ sqlite3 *db; /* The database structure */ int *aRegIdx = 0; /* One register assigned to each index to be updated */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ - int chngRowid; /* True if the record number is being changed */ + u8 *aToOpen; /* 1 for tables and indices to be opened */ + u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */ + u8 chngRowid; /* Rowid changed in a normal table */ + u8 chngKey; /* Either chngPk or chngRowid */ Expr *pRowidExpr = 0; /* Expression defining the new record number */ - int openAll = 0; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ int okOnePass; /* True for one-pass algorithm without the FIFO */ int hasFK; /* True if foreign key processing is required */ + int labelBreak; /* Jump here to break out of UPDATE loop */ + int labelContinue; /* Jump here to continue next step of UPDATE loop */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True when updating a view (INSTEAD OF trigger) */ @@ -104635,6 +106258,9 @@ SQLITE_PRIVATE void sqlite3Update( int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ #endif int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ + int iEph = 0; /* Ephemeral table holding all primary key values */ + int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */ + int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ @@ -104643,6 +106269,7 @@ SQLITE_PRIVATE void sqlite3Update( int regNew; /* Content of the NEW.* table in triggers */ int regOld = 0; /* Content of OLD.* table in triggers */ int regRowSet = 0; /* Rowset of rows to be updated */ + int regKey = 0; /* composite PRIMARY KEY value */ memset(&sContext, 0, sizeof(sContext)); db = pParse->db; @@ -104680,20 +106307,34 @@ SQLITE_PRIVATE void sqlite3Update( if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ goto update_cleanup; } - aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); - if( aXRef==0 ) goto update_cleanup; - for(i=0; inCol; i++) aXRef[i] = -1; /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and ** allocate enough space, just in case. */ - pTabList->a[0].iCursor = iCur = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++; + iIdxCur = iDataCur+1; + pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ + if( pIdx->autoIndex==2 && pPk!=0 ){ + iDataCur = pParse->nTab; + pTabList->a[0].iCursor = iDataCur; + } pParse->nTab++; } + /* Allocate space for aXRef[], aRegIdx[], and aToOpen[]. + ** Initialize aXRef[] and aToOpen[] to their default values. + */ + aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 ); + if( aXRef==0 ) goto update_cleanup; + aRegIdx = aXRef+pTab->nCol; + aToOpen = (u8*)(aRegIdx+nIdx); + memset(aToOpen, 1, nIdx+1); + aToOpen[nIdx+1] = 0; + for(i=0; inCol; i++) aXRef[i] = -1; + /* Initialize the name-context */ memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; @@ -104705,7 +106346,7 @@ SQLITE_PRIVATE void sqlite3Update( ** column to be updated, make sure we have authorization to change ** that column. */ - chngRowid = 0; + chngRowid = chngPk = 0; for(i=0; inExpr; i++){ if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ goto update_cleanup; @@ -104715,13 +106356,15 @@ SQLITE_PRIVATE void sqlite3Update( if( j==pTab->iPKey ){ chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; + }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ + chngPk = 1; } aXRef[j] = i; break; } } if( j>=pTab->nCol ){ - if( sqlite3IsRowid(pChanges->a[i].zName) ){ + if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){ j = -1; chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; @@ -104745,32 +106388,36 @@ SQLITE_PRIVATE void sqlite3Update( } #endif } + assert( (chngRowid & chngPk)==0 ); + assert( chngRowid==0 || chngRowid==1 ); + assert( chngPk==0 || chngPk==1 ); + chngKey = chngRowid + chngPk; - hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid); - - /* Allocate memory for the array aRegIdx[]. There is one entry in the - ** array for each index associated with table being updated. Fill in - ** the value with a register number for indices that are to be used - ** and with zero for unused indices. + /* The SET expressions are not actually used inside the WHERE loop. + ** So reset the colUsed mask + */ + pTabList->a[0].colUsed = 0; + + hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey); + + /* There is one entry in the aRegIdx[] array for each index on the table + ** being updated. Fill in aRegIdx[] with a register number that will hold + ** the key for accessing each index. */ - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} - if( nIdx>0 ){ - aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx ); - if( aRegIdx==0 ) goto update_cleanup; - } for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; - if( hasFK || chngRowid || pIdx->pPartIdxWhere ){ + if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){ reg = ++pParse->nMem; }else{ reg = 0; - for(i=0; inColumn; i++){ + for(i=0; inKeyCol; i++){ if( aXRef[pIdx->aiColumn[i]]>=0 ){ reg = ++pParse->nMem; break; } } } + if( reg==0 ) aToOpen[j+1] = 0; aRegIdx[j] = reg; } @@ -104794,11 +106441,11 @@ SQLITE_PRIVATE void sqlite3Update( /* Allocate required registers. */ regRowSet = ++pParse->nMem; regOldRowid = regNewRowid = ++pParse->nMem; - if( pTrigger || hasFK ){ + if( chngPk || pTrigger || hasFK ){ regOld = pParse->nMem + 1; pParse->nMem += pTab->nCol; } - if( chngRowid || pTrigger || hasFK ){ + if( chngKey || pTrigger || hasFK ){ regNewRowid = ++pParse->nMem; } regNew = pParse->nMem + 1; @@ -104814,7 +106461,7 @@ SQLITE_PRIVATE void sqlite3Update( */ #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) if( isView ){ - sqlite3MaterializeView(pParse, pTab, pWhere, iCur); + sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur); } #endif @@ -104827,24 +106474,58 @@ SQLITE_PRIVATE void sqlite3Update( /* Begin the database scan */ - sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); - pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0 - ); - if( pWInfo==0 ) goto update_cleanup; - okOnePass = sqlite3WhereOkOnePass(pWInfo); + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); + pWInfo = sqlite3WhereBegin( + pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur + ); + if( pWInfo==0 ) goto update_cleanup; + okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + + /* Remember the rowid of every item to be updated. + */ + sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); + if( !okOnePass ){ + sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); + } + + /* End the database scan loop. + */ + sqlite3WhereEnd(pWInfo); + }else{ + int iPk; /* First of nPk memory cells holding PRIMARY KEY value */ + i16 nPk; /* Number of components of the PRIMARY KEY */ + int addrOpen; /* Address of the OpenEphemeral instruction */ - /* Remember the rowid of every item to be updated. - */ - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid); - if( !okOnePass ){ - sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); + assert( pPk!=0 ); + nPk = pPk->nKeyCol; + iPk = pParse->nMem+1; + pParse->nMem += nPk; + regKey = ++pParse->nMem; + iEph = pParse->nTab++; + sqlite3VdbeAddOp2(v, OP_Null, 0, iPk); + addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); + sqlite3VdbeSetP4KeyInfo(pParse, pPk); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, + WHERE_ONEPASS_DESIRED, iIdxCur); + if( pWInfo==0 ) goto update_cleanup; + okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + for(i=0; iaiColumn[i], + iPk+i); + } + if( okOnePass ){ + sqlite3VdbeChangeToNoop(v, addrOpen); + nKey = nPk; + regKey = iPk; + }else{ + sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey, + sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey); + } + sqlite3WhereEnd(pWInfo); } - /* End the database scan loop. - */ - sqlite3WhereEnd(pWInfo); - /* Initialize the count of updated rows */ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ @@ -104852,6 +106533,7 @@ SQLITE_PRIVATE void sqlite3Update( sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } + labelBreak = sqlite3VdbeMakeLabel(v); if( !isView ){ /* ** Open every index that needs updating. Note that if any @@ -104859,68 +106541,72 @@ SQLITE_PRIVATE void sqlite3Update( ** action, then we need to open all indices because we might need ** to be deleting some records. */ - if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); if( onError==OE_Replace ){ - openAll = 1; + memset(aToOpen, 1, nIdx+1); }else{ - openAll = 0; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->onError==OE_Replace ){ - openAll = 1; + memset(aToOpen, 1, nIdx+1); break; } } } - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); - assert( pParse->nTab>iCur+i+1 ); - } + if( okOnePass ){ + if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0; + if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0; } + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen, + 0, 0); } /* Top of the update loop */ if( okOnePass ){ - int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid); - addr = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, a1); + if( aToOpen[iDataCur-iBaseCur] ){ + assert( pPk!=0 ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey); + } + labelContinue = labelBreak; + sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); + }else if( pPk ){ + labelContinue = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); + addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0); }else{ - addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); + labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak, + regOldRowid); + sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); } - /* Make cursor iCur point to the record that is being updated. If - ** this record does not exist for some reason (deleted by a trigger, - ** for example, then jump to the next iteration of the RowSet loop. */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - /* If the record number will change, set register regNewRowid to ** contain the new value. If the record number is not being modified, ** then regNewRowid is the same register as regOldRowid, which is ** already populated. */ - assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid ); + assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid ); if( chngRowid ){ sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); } - /* If there are triggers on this table, populate an array of registers - ** with the required old.* column data. */ - if( hasFK || pTrigger ){ + /* Compute the old pre-UPDATE content of the row being changed, if that + ** information is needed */ + if( chngPk || hasFK || pTrigger ){ u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; inCol; i++){ - if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 + ){ + testcase( oldmask!=0xffffffff && i==31 ); + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i); } } - if( chngRowid==0 ){ + if( chngRowid==0 && pPk==0 ){ sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } } @@ -104941,15 +106627,15 @@ SQLITE_PRIVATE void sqlite3Update( newmask = sqlite3TriggerColmask( pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError ); - sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1); + /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/ for(i=0; inCol; i++){ if( i==pTab->iPKey ){ - /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/ + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); }else{ j = aXRef[i]; if( j>=0 ){ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); - }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<31 || (newmask & MASKBIT32(i)) ){ /* This branch loads the value of a column that will not be changed ** into a register. This is done if there are no BEFORE triggers, or ** if there are one or more BEFORE triggers that use this value via @@ -104957,8 +106643,9 @@ SQLITE_PRIVATE void sqlite3Update( */ testcase( i==31 ); testcase( i==32 ); - sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); - sqlite3ColumnDefault(v, pTab, i, regNew+i); + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); + }else{ + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); } } } @@ -104970,7 +106657,7 @@ SQLITE_PRIVATE void sqlite3Update( sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol); sqlite3TableAffinityStr(v, pTab); sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); + TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue); /* The row-trigger may have deleted the row being updated. In this ** case, jump to the next row. No updates or AFTER triggers are @@ -104978,7 +106665,11 @@ SQLITE_PRIVATE void sqlite3Update( ** is deleted or renamed by a BEFORE trigger - is left undefined in the ** documentation. */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); + if( pPk ){ + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey); + }else{ + sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); + } /* If it did not delete it, the row-trigger may still have modified ** some of the columns of the row being updated. Load the values for @@ -104987,46 +106678,56 @@ SQLITE_PRIVATE void sqlite3Update( */ for(i=0; inCol; i++){ if( aXRef[i]<0 && i!=pTab->iPKey ){ - sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); - sqlite3ColumnDefault(v, pTab, i, regNew+i); + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i); } } } if( !isView ){ - int j1; /* Address of jump instruction */ + int j1 = 0; /* Address of jump instruction */ + int bReplace = 0; /* True if REPLACE conflict resolution might happen */ /* Do constraint checks. */ - sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, - aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0); + assert( regOldRowid>0 ); + sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, + regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace); /* Do FK constraint checks. */ if( hasFK ){ - sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngRowid); + sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey); } /* Delete the index entries associated with the current record. */ - j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); - sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); + if( bReplace || chngKey ){ + if( pPk ){ + j1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey); + }else{ + j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid); + } + } + sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx); /* If changing the record number, delete the old record. */ - if( hasFK || chngRowid ){ - sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); + if( hasFK || chngKey || pPk!=0 ){ + sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0); + } + if( bReplace || chngKey ){ + sqlite3VdbeJumpHere(v, j1); } - sqlite3VdbeJumpHere(v, j1); if( hasFK ){ - sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngRowid); + sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey); } /* Insert the new index entries and the new record. */ - sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0); + sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, + regNewRowid, aRegIdx, 1, 0, 0); /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just updated. */ if( hasFK ){ - sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngRowid); + sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey); } } @@ -105037,22 +106738,29 @@ SQLITE_PRIVATE void sqlite3Update( } sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_AFTER, pTab, regOldRowid, onError, addr); + TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); - sqlite3VdbeJumpHere(v, addr); + if( okOnePass ){ + /* Nothing to do at end-of-loop for a single-pass */ + }else if( pPk ){ + sqlite3VdbeResolveLabel(v, labelContinue); + sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue); + } + sqlite3VdbeResolveLabel(v, labelBreak); /* Close all tables */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ - sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0); + if( aToOpen[i+1] ){ + sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0); } } - sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); + if( iDataCur0" + " AND coalesce(rootpage,1)>0" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execExecSql(db, pzErrMsg, @@ -105445,7 +107152,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ "|| ' SELECT * FROM main.' || quote(name) || ';'" "FROM main.sqlite_master " "WHERE type = 'table' AND name!='sqlite_sequence' " - " AND rootpage>0" + " AND coalesce(rootpage,1)>0" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; @@ -106299,6 +108006,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3VdbeFinalize(pParse->pVdbe); } sqlite3DeleteTable(db, pParse->pNewTable); + sqlite3ParserReset(pParse); sqlite3StackFree(db, pParse); } @@ -106676,7 +108384,24 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". */ - +/************** Include whereInt.h in the middle of where.c ******************/ +/************** Begin file whereInt.h ****************************************/ +/* +** 2013-11-12 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains structure and macro definitions for the query +** planner logic in "where.c". These definitions are broken out into +** a separate source file for easier editing. +*/ /* ** Trace output macros @@ -106728,6 +108453,7 @@ struct WhereLevel { int iIdxCur; /* The VDBE cursor used to access pIdx */ int addrBrk; /* Jump here to break out of the loop */ int addrNxt; /* Jump here to start the next IN combination */ + int addrSkip; /* Jump here for next iteration of skip-scan */ int addrCont; /* Jump here to continue with the next loop cycle */ int addrFirst; /* First instruction of interior of the loop */ int addrBody; /* Beginning of the body of this loop */ @@ -106776,7 +108502,8 @@ struct WhereLoop { LogEst nOut; /* Estimated number of output rows */ union { struct { /* Information for internal btree tables */ - int nEq; /* Number of equality constraints */ + u16 nEq; /* Number of equality constraints */ + u16 nSkip; /* Number of initial index columns to skip */ Index *pIndex; /* Index used, or NULL */ } btree; struct { /* Information for virtual tables */ @@ -107068,6 +108795,7 @@ struct WhereInfo { int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ + int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ WhereClause sWC; /* Decomposition of the WHERE clause */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ @@ -107116,6 +108844,10 @@ struct WhereInfo { #define WHERE_ONEROW 0x00001000 /* Selects no more than one row */ #define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */ #define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */ +#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */ + +/************** End of whereInt.h ********************************************/ +/************** Continuing where we left off in where.c **********************/ /* ** Return the estimated number of output rows from a WHERE clause @@ -107160,8 +108892,19 @@ SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo *pWInfo){ ** Return TRUE if an UPDATE or DELETE statement can operate directly on ** the rowids returned by a WHERE clause. Return FALSE if doing an ** UPDATE or DELETE might change subsequent WHERE clause results. +** +** If the ONEPASS optimization is used (if this routine returns true) +** then also write the indices of open cursors used by ONEPASS +** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data +** table and iaCur[1] gets the cursor used by an auxiliary index. +** Either value may be -1, indicating that cursor is not used. +** Any cursors returned will have been opened for writing. +** +** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is +** unable to use the ONEPASS optimization. */ -SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo *pWInfo){ +SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){ + memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2); return pWInfo->okOnePass; } @@ -107537,7 +109280,10 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ iColumn = pScan->aEquiv[pScan->iEquiv-1]; while( (pWC = pScan->pWC)!=0 ){ for(pTerm=pWC->a+k; knTerm; k++, pTerm++){ - if( pTerm->leftCursor==iCur && pTerm->u.leftColumn==iColumn ){ + if( pTerm->leftCursor==iCur + && pTerm->u.leftColumn==iColumn + && (pScan->iEquiv<=2 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) + ){ if( (pTerm->eOperator & WO_EQUIV)!=0 && pScan->nEquivaEquiv) ){ @@ -107627,7 +109373,7 @@ static WhereTerm *whereScanInit( if( pIdx && iColumn>=0 ){ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ - if( NEVER(j>=pIdx->nColumn) ) return 0; + if( NEVER(j>=pIdx->nKeyCol) ) return 0; } pScan->zCollName = pIdx->azColl[j]; }else{ @@ -107751,11 +109497,8 @@ static int isLikeOrGlob( } assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */ - pRight = pList->a[0].pExpr; + pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr); op = pRight->op; - if( op==TK_REGISTER ){ - op = pRight->op2; - } if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; int iCol = pRight->iColumn; @@ -108557,16 +110300,16 @@ static int isDistinctRedundant( */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->onError==OE_None ) continue; - for(i=0; inColumn; i++){ - int iCol = pIdx->aiColumn[i]; + for(i=0; inKeyCol; i++){ + i16 iCol = pIdx->aiColumn[i]; if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx) ){ int iIdxCol = findIndexCol(pParse, pDistinct, iBase, pIdx, i); - if( iIdxCol<0 || pTab->aCol[pIdx->aiColumn[i]].notNull==0 ){ + if( iIdxCol<0 || pTab->aCol[iCol].notNull==0 ){ break; } } } - if( i==pIdx->nColumn ){ + if( i==pIdx->nKeyCol ){ /* This index implies that the DISTINCT qualifier is redundant. */ return 1; } @@ -108622,6 +110365,7 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){ sqlite3DebugPrintf(" idxStr=%s\n", p->idxStr); sqlite3DebugPrintf(" orderByConsumed=%d\n", p->orderByConsumed); sqlite3DebugPrintf(" estimatedCost=%g\n", p->estimatedCost); + sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); } #else #define TRACE_IDX_INPUTS(A) @@ -108664,15 +110408,13 @@ static void constructAutomaticIndex( Bitmask notReady, /* Mask of cursors that are not available */ WhereLevel *pLevel /* Write new index here */ ){ - int nColumn; /* Number of columns in the constructed index */ + int nKeyCol; /* Number of columns in the constructed index */ WhereTerm *pTerm; /* A single term of the WHERE clause */ WhereTerm *pWCEnd; /* End of pWC->a[] */ - int nByte; /* Byte of memory needed for pIdx */ Index *pIdx; /* Object describing the transient index */ Vdbe *v; /* Prepared statement under construction */ int addrInit; /* Address of the initialization bypass jump */ Table *pTable; /* The table being indexed */ - KeyInfo *pKeyinfo; /* Key information for the index */ int addrTop; /* Top of the index fill loop */ int regRecord; /* Register holding an index record */ int n; /* Column counter */ @@ -108680,6 +110422,7 @@ static void constructAutomaticIndex( int mxBitCol; /* Maximum column in pSrc->colUsed */ CollSeq *pColl; /* Collating sequence to on a column */ WhereLoop *pLoop; /* The Loop object */ + char *zNotUsed; /* Extra space on the end of pIdx */ Bitmask idxCols; /* Bitmap of columns used for indexing */ Bitmask extraCols; /* Bitmap of additional columns */ u8 sentWarning = 0; /* True if a warnning has been issued */ @@ -108692,7 +110435,7 @@ static void constructAutomaticIndex( /* Count the number of columns that will be added to the index ** and used to match WHERE clause constraints */ - nColumn = 0; + nKeyCol = 0; pTable = pSrc->pTab; pWCEnd = &pWC->a[pWC->nTerm]; pLoop = pLevel->pWLoop; @@ -108710,14 +110453,14 @@ static void constructAutomaticIndex( sentWarning = 1; } if( (idxCols & cMask)==0 ){ - if( whereLoopResize(pParse->db, pLoop, nColumn+1) ) return; - pLoop->aLTerm[nColumn++] = pTerm; + if( whereLoopResize(pParse->db, pLoop, nKeyCol+1) ) return; + pLoop->aLTerm[nKeyCol++] = pTerm; idxCols |= cMask; } } } - assert( nColumn>0 ); - pLoop->u.btree.nEq = pLoop->nLTerm = nColumn; + assert( nKeyCol>0 ); + pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol; pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED | WHERE_AUTO_INDEX; @@ -108734,26 +110477,18 @@ static void constructAutomaticIndex( testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); for(i=0; icolUsed & MASKBIT(BMS-1) ){ - nColumn += pTable->nCol - BMS + 1; + nKeyCol += pTable->nCol - BMS + 1; } pLoop->wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY; /* Construct the Index object to describe this index */ - nByte = sizeof(Index); - nByte += nColumn*sizeof(int); /* Index.aiColumn */ - nByte += nColumn*sizeof(char*); /* Index.azColl */ - nByte += nColumn; /* Index.aSortOrder */ - pIdx = sqlite3DbMallocZero(pParse->db, nByte); + pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed); if( pIdx==0 ) return; pLoop->u.btree.pIndex = pIdx; - pIdx->azColl = (char**)&pIdx[1]; - pIdx->aiColumn = (int*)&pIdx->azColl[nColumn]; - pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn]; pIdx->zName = "auto-index"; - pIdx->nColumn = nColumn; pIdx->pTable = pTable; n = 0; idxCols = 0; @@ -108791,20 +110526,21 @@ static void constructAutomaticIndex( n++; } } - assert( n==nColumn ); + assert( n==nKeyCol ); + pIdx->aiColumn[n] = -1; + pIdx->azColl[n] = "BINARY"; /* Create the automatic index */ - pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx); assert( pLevel->iIdxCur>=0 ); pLevel->iIdxCur = pParse->nTab++; - sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0, - (char*)pKeyinfo, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); /* Fill the automatic index with content */ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); regRecord = sqlite3GetTempReg(pParse); - sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1, 0); + sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); @@ -108845,7 +110581,8 @@ static sqlite3_index_info *allocateIndexInfo( assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); - if( pTerm->eOperator & (WO_ISNULL) ) continue; + testcase( pTerm->eOperator & WO_ALL ); + if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; nTerm++; } @@ -108897,7 +110634,8 @@ static sqlite3_index_info *allocateIndexInfo( assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); - if( pTerm->eOperator & (WO_ISNULL) ) continue; + testcase( pTerm->eOperator & WO_ALL ); + if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; @@ -108999,8 +110737,7 @@ static void whereKeyStats( #ifndef SQLITE_DEBUG UNUSED_PARAMETER( pParse ); #endif - assert( pRec!=0 || pParse->db->mallocFailed ); - if( pRec==0 ) return; + assert( pRec!=0 ); iCol = pRec->nField - 1; assert( pIdx->nSample>0 ); assert( pRec->nField>0 && iColnSampleCol ); @@ -109051,7 +110788,7 @@ static void whereKeyStats( iUpper = i>=pIdx->nSample ? pIdx->aiRowEst[0] : aSample[i].anLt[iCol]; iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol]; } - aStat[1] = (pIdx->nColumn>iCol ? pIdx->aAvgEq[iCol] : 1); + aStat[1] = (pIdx->nKeyCol>iCol ? pIdx->aAvgEq[iCol] : 1); if( iLower>=iUpper ){ iGap = 0; }else{ @@ -109151,7 +110888,7 @@ static int whereRangeScanEst( tRowcnt iLower; tRowcnt iUpper; - if( nEq==p->nColumn ){ + if( nEq==p->nKeyCol ){ aff = SQLITE_AFF_INTEGER; }else{ aff = p->pTable->aCol[p->aiColumn[nEq]].affinity; @@ -109209,7 +110946,7 @@ static int whereRangeScanEst( nOut = nNew; } pLoop->nOut = (LogEst)nOut; - WHERETRACE(0x100, ("range scan regions: %u..%u est=%d\n", + WHERETRACE(0x10, ("range scan regions: %u..%u est=%d\n", (u32)iLower, (u32)iUpper, nOut)); return SQLITE_OK; } @@ -109269,7 +111006,7 @@ static int whereEqualScanEst( int bOk; assert( nEq>=1 ); - assert( nEq<=(p->nColumn+1) ); + assert( nEq<=(p->nKeyCol+1) ); assert( p->aSample!=0 ); assert( p->nSample>0 ); assert( pBuilder->nRecValidp->nColumn ){ + if( nEq>p->nKeyCol ){ *pnRow = 1; return SQLITE_OK; } @@ -109295,7 +111032,7 @@ static int whereEqualScanEst( pBuilder->nRecValid = nEq; whereKeyStats(pParse, p, pRec, 0, a); - WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1])); + WHERETRACE(0x10,("equality scan regions: %d\n", (int)a[1])); *pnRow = a[1]; return rc; @@ -109343,7 +111080,7 @@ static int whereInScanEst( if( rc==SQLITE_OK ){ if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; *pnRow = nRowEst; - WHERETRACE(0x100,("IN row estimate: est=%g\n", nRowEst)); + WHERETRACE(0x10,("IN row estimate: est=%g\n", nRowEst)); } assert( pBuilder->nRecValid==nRecValid ); return rc; @@ -109500,7 +111237,7 @@ static int codeEqualityTerm( }else{ pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg); } - pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; + pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen; sqlite3VdbeAddOp1(v, OP_IsNull, iReg); }else{ pLevel->u.in.nIn = 0; @@ -109513,7 +111250,7 @@ static int codeEqualityTerm( /* ** Generate code that will evaluate all == and IN constraints for an -** index. +** index scan. ** ** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c). ** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10 @@ -109528,9 +111265,15 @@ static int codeEqualityTerm( ** The only thing it does is allocate the pLevel->iMem memory cell and ** compute the affinity string. ** -** This routine always allocates at least one memory cell and returns -** the index of that memory cell. The code that -** calls this routine will use that memory cell to store the termination +** The nExtraReg parameter is 0 or 1. It is 0 if all WHERE clause constraints +** are == or IN and are covered by the nEq. nExtraReg is 1 if there is +** an inequality constraint (such as the "c>=5 AND c<10" in the example) that +** occurs after the nEq quality constraints. +** +** This routine allocates a range of nEq+nExtraReg memory cells and returns +** the index of the first memory cell in that range. The code that +** calls this routine will use that memory range to store keys for +** start and termination conditions of the loop. ** key value of the loop. If one or more IN operators appear, then ** this routine allocates an additional nEq memory cells for internal ** use. @@ -109557,7 +111300,8 @@ static int codeAllEqualityTerms( int nExtraReg, /* Number of extra registers to allocate */ char **pzAff /* OUT: Set to point to affinity string */ ){ - int nEq; /* The number of == or IN constraints to code */ + u16 nEq; /* The number of == or IN constraints to code */ + u16 nSkip; /* Number of left-most columns to skip */ Vdbe *v = pParse->pVdbe; /* The vm under construction */ Index *pIdx; /* The index being used for this loop */ WhereTerm *pTerm; /* A single constraint term */ @@ -109571,6 +111315,7 @@ static int codeAllEqualityTerms( pLoop = pLevel->pWLoop; assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ); nEq = pLoop->u.btree.nEq; + nSkip = pLoop->u.btree.nSkip; pIdx = pLoop->u.btree.pIndex; assert( pIdx!=0 ); @@ -109585,14 +111330,29 @@ static int codeAllEqualityTerms( pParse->db->mallocFailed = 1; } + if( nSkip ){ + int iIdxCur = pLevel->iIdxCur; + sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur); + VdbeComment((v, "begin skip-scan on %s", pIdx->zName)); + j = sqlite3VdbeAddOp0(v, OP_Goto); + pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLt:OP_SeekGt), + iIdxCur, 0, regBase, nSkip); + sqlite3VdbeJumpHere(v, j); + for(j=0; jaiColumn[j]>=0 ); + VdbeComment((v, "%s", pIdx->pTable->aCol[pIdx->aiColumn[j]].zName)); + } + } + /* Evaluate the equality constraints */ assert( zAff==0 || (int)strlen(zAff)>=nEq ); - for(j=0; jaLTerm[j]; assert( pTerm!=0 ); - /* The following true for indices with redundant columns. + /* The following testcase is true for indices with redundant columns. ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */ testcase( (pTerm->wtFlags & TERM_CODED)!=0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); @@ -109640,7 +111400,7 @@ static void explainAppendTerm( const char *zOp /* Name of the operator */ ){ if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5); - sqlite3StrAccumAppend(pStr, zColumn, -1); + sqlite3StrAccumAppendAll(pStr, zColumn); sqlite3StrAccumAppend(pStr, zOp, 1); sqlite3StrAccumAppend(pStr, "?", 1); } @@ -109666,10 +111426,11 @@ static void explainAppendTerm( */ static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){ Index *pIndex = pLoop->u.btree.pIndex; - int nEq = pLoop->u.btree.nEq; + u16 nEq = pLoop->u.btree.nEq; + u16 nSkip = pLoop->u.btree.nSkip; int i, j; Column *aCol = pTab->aCol; - int *aiColumn = pIndex->aiColumn; + i16 *aiColumn = pIndex->aiColumn; StrAccum txt; if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){ @@ -109679,17 +111440,24 @@ static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){ txt.db = db; sqlite3StrAccumAppend(&txt, " (", 2); for(i=0; inColumn ) ? "rowid" : aCol[aiColumn[i]].zName; - explainAppendTerm(&txt, i, z, "="); + char *z = (i==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[i]].zName; + if( i>=nSkip ){ + explainAppendTerm(&txt, i, z, "="); + }else{ + if( i ) sqlite3StrAccumAppend(&txt, " AND ", 5); + sqlite3StrAccumAppend(&txt, "ANY(", 4); + sqlite3StrAccumAppendAll(&txt, z); + sqlite3StrAccumAppend(&txt, ")", 1); + } } j = i; if( pLoop->wsFlags&WHERE_BTM_LIMIT ){ - char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName; + char *z = (j==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[j]].zName; explainAppendTerm(&txt, i++, z, ">"); } if( pLoop->wsFlags&WHERE_TOP_LIMIT ){ - char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName; + char *z = (j==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[j]].zName; explainAppendTerm(&txt, i, z, "<"); } sqlite3StrAccumAppend(&txt, ")", 1); @@ -109710,7 +111478,10 @@ static void explainOneScan( int iFrom, /* Value for "from" column of output */ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ){ - if( pParse->explain==2 ){ +#ifndef SQLITE_DEBUG + if( pParse->explain==2 ) +#endif + { struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlite3 *db = pParse->db; /* Database handle */ @@ -109816,7 +111587,7 @@ static Bitmask codeOneLoopStart( bRev = (pWInfo->revMask>>iLevel)&1; omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0; - VdbeNoopComment((v, "Begin Join Loop %d", iLevel)); + VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); /* Create labels for the "break" and "continue" instructions ** for the current loop. Jump to addrBrk to break out of a loop. @@ -110043,8 +111814,8 @@ static Bitmask codeOneLoopStart( OP_IdxGE, /* 1: (end_constraints && !bRev) */ OP_IdxLT /* 2: (end_constraints && bRev) */ }; - int nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */ - int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ + u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */ + int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ int regBase; /* Base register holding constraint values */ int r1; /* Temp register */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ @@ -110058,10 +111829,11 @@ static Bitmask codeOneLoopStart( int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ char *zStartAff; /* Affinity for start of range constraint */ - char *zEndAff; /* Affinity for end of range constraint */ + char cEndAff = 0; /* Affinity for end of range constraint */ pIdx = pLoop->u.btree.pIndex; iIdxCur = pLevel->iIdxCur; + assert( nEq>=pLoop->u.btree.nSkip ); /* If this loop satisfies a sort order (pOrderBy) request that ** was passed to this function to implement a "SELECT min(x) ..." @@ -110073,10 +111845,9 @@ static Bitmask codeOneLoopStart( */ if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0 && (pWInfo->bOBSat!=0) - && (pIdx->nColumn>nEq) + && (pIdx->nKeyCol>nEq) ){ - /* assert( pOrderBy->nExpr==1 ); */ - /* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */ + assert( pLoop->u.btree.nSkip==0 ); isMinQuery = 1; nExtraReg = 1; } @@ -110099,15 +111870,16 @@ static Bitmask codeOneLoopStart( ** starting at regBase. */ regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff); - zEndAff = sqlite3DbStrDup(db, zStartAff); + assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq ); + if( zStartAff ) cEndAff = zStartAff[nEq]; addrNxt = pLevel->addrNxt; /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). */ - if( (nEqnColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) - || (bRev && pIdx->nColumn==nEq) + if( (nEqnKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) + || (bRev && pIdx->nKeyCol==nEq) ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); } @@ -110169,23 +111941,15 @@ static Bitmask codeOneLoopStart( if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); } - if( zEndAff ){ - if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ - /* Since the comparison is to be performed with no conversions - ** applied to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_NONE. */ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - if( sqlite3ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){ - zEndAff[nEq] = SQLITE_AFF_NONE; - } - } - codeApplyAffinity(pParse, regBase, nEq+1, zEndAff); + if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_NONE + && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff) + ){ + codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff); + } nConstraint++; testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); } sqlite3DbFree(db, zStartAff); - sqlite3DbFree(db, zEndAff); /* Top of the loop body */ pLevel->p2 = sqlite3VdbeCurrentAddr(v); @@ -110207,8 +111971,13 @@ static Bitmask codeOneLoopStart( r1 = sqlite3GetTempReg(pParse); testcase( pLoop->wsFlags & WHERE_BTM_LIMIT ); testcase( pLoop->wsFlags & WHERE_TOP_LIMIT ); - if( (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){ + if( (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 + && (j = pIdx->aiColumn[nEq])>=0 + && pIdx->pTable->aCol[j].notNull==0 + && (nEq || (pLoop->wsFlags & WHERE_BTM_LIMIT)==0) + ){ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); + VdbeComment((v, "%s", pIdx->pTable->aCol[j].zName)); sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont); } sqlite3ReleaseTempReg(pParse, r1); @@ -110216,11 +111985,22 @@ static Bitmask codeOneLoopStart( /* Seek the table cursor, if required */ disableTerm(pLevel, pRangeStart); disableTerm(pLevel, pRangeEnd); - if( !omitTable ){ + if( omitTable ){ + /* pIdx is a covering index. No need to access the main table. */ + }else if( HasRowid(pIdx->pTable) ){ iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ + }else{ + Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); + iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol); + for(j=0; jnKeyCol; j++){ + k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]); + sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j); + } + sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont, + iRowidReg, pPk->nKeyCol); } /* Record the instruction used to terminate the loop. Disable @@ -110364,7 +112144,9 @@ static Bitmask codeOneLoopStart( Expr *pExpr = pWC->a[iTerm].pExpr; if( &pWC->a[iTerm] == pTerm ) continue; if( ExprHasProperty(pExpr, EP_FromJoin) ) continue; - if( pWC->a[iTerm].wtFlags & (TERM_ORINFO) ) continue; + testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); + testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL ); + if( pWC->a[iTerm].wtFlags & (TERM_ORINFO|TERM_VIRTUAL) ) continue; if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue; pExpr = sqlite3ExprDup(db, pExpr, 0); pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr); @@ -110460,10 +112242,16 @@ static Bitmask codeOneLoopStart( static const u8 aStep[] = { OP_Next, OP_Prev }; static const u8 aStart[] = { OP_Rewind, OP_Last }; assert( bRev==0 || bRev==1 ); - pLevel->op = aStep[bRev]; - pLevel->p1 = iCur; - pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + if( pTabItem->isRecursive ){ + /* Tables marked isRecursive have only a single row that is stored in + ** a pseudo-cursor. No need to Rewind or Next such cursors. */ + pLevel->op = OP_Noop; + }else{ + pLevel->op = aStep[bRev]; + pLevel->p1 = iCur; + pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); + pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + } } /* Insert code to test every subexpression that can be completely @@ -110512,7 +112300,7 @@ static Bitmask codeOneLoopStart( if( pAlt->wtFlags & (TERM_CODED) ) continue; testcase( pAlt->eOperator & WO_EQ ); testcase( pAlt->eOperator & WO_IN ); - VdbeNoopComment((v, "begin transitive constraint")); + VdbeModuleComment((v, "begin transitive constraint")); pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt)); if( pEAlt ){ *pEAlt = *pAlt->pExpr; @@ -110548,22 +112336,38 @@ static Bitmask codeOneLoopStart( return pLevel->notReady; } +#if defined(WHERETRACE_ENABLED) && defined(SQLITE_ENABLE_TREE_EXPLAIN) +/* +** Generate "Explanation" text for a WhereTerm. +*/ +static void whereExplainTerm(Vdbe *v, WhereTerm *pTerm){ + char zType[4]; + memcpy(zType, "...", 4); + if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; + if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; + if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L'; + sqlite3ExplainPrintf(v, "%s ", zType); + sqlite3ExplainExpr(v, pTerm->pExpr); +} +#endif /* WHERETRACE_ENABLED && SQLITE_ENABLE_TREE_EXPLAIN */ + + #ifdef WHERETRACE_ENABLED /* ** Print a WhereLoop object for debugging purposes */ -static void whereLoopPrint(WhereLoop *p, SrcList *pTabList){ - int nb = 1+(pTabList->nSrc+7)/8; - struct SrcList_item *pItem = pTabList->a + p->iTab; +static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){ + WhereInfo *pWInfo = pWC->pWInfo; + int nb = 1+(pWInfo->pTabList->nSrc+7)/8; + struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pTab; sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, p->iTab, nb, p->maskSelf, nb, p->prereq); sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias : pTab->zName); if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ - if( p->u.btree.pIndex ){ - const char *zName = p->u.btree.pIndex->zName; - if( zName==0 ) zName = "ipk"; + const char *zName; + if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){ if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){ int i = sqlite3Strlen30(zName) - 1; while( zName[i]!='_' ) i--; @@ -110586,6 +112390,27 @@ static void whereLoopPrint(WhereLoop *p, SrcList *pTabList){ } sqlite3DebugPrintf(" f %04x N %d", p->wsFlags, p->nLTerm); sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); +#ifdef SQLITE_ENABLE_TREE_EXPLAIN + /* If the 0x100 bit of wheretracing is set, then show all of the constraint + ** expressions in the WhereLoop.aLTerm[] array. + */ + if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){ /* WHERETRACE 0x100 */ + int i; + Vdbe *v = pWInfo->pParse->pVdbe; + sqlite3ExplainBegin(v); + for(i=0; inLTerm; i++){ + WhereTerm *pTerm = p->aLTerm[i]; + if( pTerm==0 ) continue; + sqlite3ExplainPrintf(v, " (%d) #%-2d ", i+1, (int)(pTerm-pWC->a)); + sqlite3ExplainPush(v); + whereExplainTerm(v, pTerm); + sqlite3ExplainPop(v); + sqlite3ExplainNL(v); + } + sqlite3ExplainFinish(v); + sqlite3DebugPrintf("%s", sqlite3VdbeExplanation(v)); + } +#endif } #endif @@ -110611,6 +112436,7 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){ p->u.vtab.idxStr = 0; }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ sqlite3DbFree(db, p->u.btree.pIndex->zColAff); + sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo); sqlite3DbFree(db, p->u.btree.pIndex); p->u.btree.pIndex = 0; } @@ -110725,10 +112551,10 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ #endif whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun, pTemplate->nOut); -#if WHERETRACE_ENABLED +#if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n); - whereLoopPrint(pTemplate, pWInfo->pTabList); + whereLoopPrint(pTemplate, pBuilder->pWC); } #endif return SQLITE_OK; @@ -110798,14 +112624,14 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new ** WhereLoop and insert it. */ -#if WHERETRACE_ENABLED +#if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ if( p!=0 ){ sqlite3DebugPrintf("ins-del: "); - whereLoopPrint(p, pWInfo->pTabList); + whereLoopPrint(p, pBuilder->pWC); } sqlite3DebugPrintf("ins-new: "); - whereLoopPrint(pTemplate, pWInfo->pTabList); + whereLoopPrint(pTemplate, pBuilder->pWC); } #endif if( p==0 ){ @@ -110826,10 +112652,10 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ /* Jump here if the insert is a no-op */ whereLoopInsert_noop: -#if WHERETRACE_ENABLED +#if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ sqlite3DebugPrintf("ins-noop: "); - whereLoopPrint(pTemplate, pWInfo->pTabList); + whereLoopPrint(pTemplate, pBuilder->pWC); } #endif return SQLITE_OK; @@ -110858,6 +112684,7 @@ static void whereLoopOutputAdjust(WhereClause *pWC, WhereLoop *pLoop){ if( (pTerm->prereqAll & notAllowed)!=0 ) continue; for(j=pLoop->nLTerm-1; j>=0; j--){ pX = pLoop->aLTerm[j]; + if( pX==0 ) continue; if( pX==pTerm ) break; if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break; } @@ -110887,7 +112714,8 @@ static int whereLoopAddBtreeIndex( WhereScan scan; /* Iterator for WHERE terms */ Bitmask saved_prereq; /* Original value of pNew->prereq */ u16 saved_nLTerm; /* Original value of pNew->nLTerm */ - int saved_nEq; /* Original value of pNew->u.btree.nEq */ + u16 saved_nEq; /* Original value of pNew->u.btree.nEq */ + u16 saved_nSkip; /* Original value of pNew->u.btree.nSkip */ u32 saved_wsFlags; /* Original value of pNew->wsFlags */ LogEst saved_nOut; /* Original value of pNew->nOut */ int iCol; /* Index of the column in the table */ @@ -110910,8 +112738,8 @@ static int whereLoopAddBtreeIndex( } if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); - assert( pNew->u.btree.nEq<=pProbe->nColumn ); - if( pNew->u.btree.nEq < pProbe->nColumn ){ + assert( pNew->u.btree.nEq<=pProbe->nKeyCol ); + if( pNew->u.btree.nEq < pProbe->nKeyCol ){ iCol = pProbe->aiColumn[pNew->u.btree.nEq]; nRowEst = sqlite3LogEst(pProbe->aiRowEst[pNew->u.btree.nEq+1]); if( nRowEst==0 && pProbe->onError==OE_None ) nRowEst = 1; @@ -110922,12 +112750,34 @@ static int whereLoopAddBtreeIndex( pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol, opMask, pProbe); saved_nEq = pNew->u.btree.nEq; + saved_nSkip = pNew->u.btree.nSkip; saved_nLTerm = pNew->nLTerm; saved_wsFlags = pNew->wsFlags; saved_prereq = pNew->prereq; saved_nOut = pNew->nOut; pNew->rSetup = 0; rLogSize = estLog(sqlite3LogEst(pProbe->aiRowEst[0])); + + /* Consider using a skip-scan if there are no WHERE clause constraints + ** available for the left-most terms of the index, and if the average + ** number of repeats in the left-most terms is at least 18. The magic + ** number 18 was found by experimentation to be the payoff point where + ** skip-scan become faster than a full-scan. + */ + if( pTerm==0 + && saved_nEq==saved_nSkip + && saved_nEq+1nKeyCol + && pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */ + && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK + ){ + LogEst nIter; + pNew->u.btree.nEq++; + pNew->u.btree.nSkip++; + pNew->aLTerm[pNew->nLTerm++] = 0; + pNew->wsFlags |= WHERE_SKIPSCAN; + nIter = sqlite3LogEst(pProbe->aiRowEst[0]/pProbe->aiRowEst[saved_nEq+1]); + whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter); + } for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){ int nIn = 0; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 @@ -110963,12 +112813,14 @@ static int whereLoopAddBtreeIndex( pNew->u.btree.nEq++; pNew->nOut = nRowEst + nInMul + nIn; }else if( pTerm->eOperator & (WO_EQ) ){ - assert( (pNew->wsFlags & (WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 - || nInMul==0 ); + assert( + (pNew->wsFlags & (WHERE_COLUMN_NULL|WHERE_COLUMN_IN|WHERE_SKIPSCAN))!=0 + || nInMul==0 + ); pNew->wsFlags |= WHERE_COLUMN_EQ; if( iCol<0 || (pProbe->onError!=OE_None && nInMul==0 - && pNew->u.btree.nEq==pProbe->nColumn-1) + && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){ assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 ); pNew->wsFlags |= WHERE_ONEROW; @@ -111034,7 +112886,7 @@ static int whereLoopAddBtreeIndex( whereLoopOutputAdjust(pBuilder->pWC, pNew); rc = whereLoopInsert(pBuilder, pNew); if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 - && pNew->u.btree.nEq<(pProbe->nColumn + (pProbe->zName!=0)) + && pNew->u.btree.nEq<(pProbe->nKeyCol + (pProbe->zName!=0)) ){ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); } @@ -111045,6 +112897,7 @@ static int whereLoopAddBtreeIndex( } pNew->prereq = saved_prereq; pNew->u.btree.nEq = saved_nEq; + pNew->u.btree.nSkip = saved_nSkip; pNew->wsFlags = saved_wsFlags; pNew->nOut = saved_nOut; pNew->nLTerm = saved_nLTerm; @@ -111073,7 +112926,7 @@ static int indexMightHelpWithOrderBy( Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr); if( pExpr->op!=TK_COLUMN ) return 0; if( pExpr->iTable==iCursor ){ - for(jj=0; jjnColumn; jj++){ + for(jj=0; jjnKeyCol; jj++){ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; } } @@ -111090,10 +112943,11 @@ static Bitmask columnsInIndex(Index *pIdx){ int j; for(j=pIdx->nColumn-1; j>=0; j--){ int x = pIdx->aiColumn[j]; - assert( x>=0 ); - testcase( x==BMS-1 ); - testcase( x==BMS-2 ); - if( x=0 ){ + testcase( x==BMS-1 ); + testcase( x==BMS-2 ); + if( xpIndex ){ /* An INDEXED BY clause specifies a particular index to use */ pProbe = pSrc->pIndex; + }else if( !HasRowid(pTab) ){ + pProbe = pTab->pIndex; }else{ /* There is no INDEXED BY clause. Create a fake Index object in local ** variable sPk to represent the rowid primary key index. Make this @@ -111153,7 +113009,7 @@ static int whereLoopAddBtree( ** indices to follow */ Index *pFirst; /* First of real indices on the table */ memset(&sPk, 0, sizeof(Index)); - sPk.nColumn = 1; + sPk.nKeyCol = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowEst = aiRowEstPk; sPk.onError = OE_Replace; @@ -111178,7 +113034,9 @@ static int whereLoopAddBtree( && pSrc->pIndex==0 && !pSrc->viaCoroutine && !pSrc->notIndexed + && HasRowid(pTab) && !pSrc->isCorrelated + && !pSrc->isRecursive ){ /* Generate auto-index WhereLoops */ WhereTerm *pTerm; @@ -111187,6 +113045,7 @@ static int whereLoopAddBtree( if( pTerm->prereqRight & pNew->maskSelf ) continue; if( termCanDriveIndex(pTerm, pSrc, 0) ){ pNew->u.btree.nEq = 1; + pNew->u.btree.nSkip = 0; pNew->u.btree.pIndex = 0; pNew->nLTerm = 1; pNew->aLTerm[0] = pTerm; @@ -111216,6 +113075,7 @@ static int whereLoopAddBtree( continue; /* Partial index inappropriate for this query */ } pNew->u.btree.nEq = 0; + pNew->u.btree.nSkip = 0; pNew->nLTerm = 0; pNew->iSortIdx = 0; pNew->rSetup = 0; @@ -111240,14 +113100,21 @@ static int whereLoopAddBtree( pNew->nOut = rSize; if( rc ) break; }else{ - Bitmask m = pSrc->colUsed & ~columnsInIndex(pProbe); - pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; + Bitmask m; + if( pProbe->isCovering ){ + pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; + m = 0; + }else{ + m = pSrc->colUsed & ~columnsInIndex(pProbe); + pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; + } /* Full scan via index */ if( b + || !HasRowid(pTab) || ( m==0 && pProbe->bUnordered==0 - && pProbe->szIdxRowszTabRow + && (pProbe->szIdxRowszTabRow) && (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 && sqlite3GlobalConfig.bUseCis && OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan) @@ -111263,7 +113130,6 @@ static int whereLoopAddBtree( pNew->rRun = sqlite3LogEstAdd(rSize,rLogSize) + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow; }else{ - assert( b!=0 ); /* TUNING: Cost of scanning a non-covering index is (N+1)*log2(N) ** which we will simplify to just N*log2(N) */ pNew->rRun = rSize + rLogSize; @@ -111295,7 +113161,8 @@ static int whereLoopAddBtree( ** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. */ static int whereLoopAddVirtual( - WhereLoopBuilder *pBuilder /* WHERE clause information */ + WhereLoopBuilder *pBuilder, /* WHERE clause information */ + Bitmask mExtra ){ WhereInfo *pWInfo; /* WHERE analysis context */ Parse *pParse; /* The parsing context */ @@ -111381,10 +113248,11 @@ static int whereLoopAddVirtual( pIdxInfo->needToFreeIdxStr = 0; pIdxInfo->orderByConsumed = 0; pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; + pIdxInfo->estimatedRows = 25; rc = vtabBestIndex(pParse, pTab, pIdxInfo); if( rc ) goto whereLoopAddVtab_exit; pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - pNew->prereq = 0; + pNew->prereq = mExtra; mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); for(i=0; iaLTerm[i] = 0; @@ -111440,8 +113308,7 @@ static int whereLoopAddVirtual( && pIdxInfo->orderByConsumed); pNew->rSetup = 0; pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); - /* TUNING: Every virtual table query returns 25 rows */ - pNew->nOut = 46; assert( 46==sqlite3LogEst(25) ); + pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); whereLoopInsert(pBuilder, pNew); if( pNew->u.vtab.needFree ){ sqlite3_free(pNew->u.vtab.idxStr); @@ -111479,6 +113346,7 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){ pNew = pBuilder->pNew; memset(&sSum, 0, sizeof(sSum)); pItem = pWInfo->pTabList->a + pNew->iTab; + if( !HasRowid(pItem->pTab) ) return SQLITE_OK; iCur = pItem->iCursor; for(pTerm=pWC->a; pTermpTab) ){ - rc = whereLoopAddVirtual(&sSubBuild); - for(i=0; ijointype; if( IsVirtual(pItem->pTab) ){ - rc = whereLoopAddVirtual(pBuilder); + rc = whereLoopAddVirtual(pBuilder, mExtra); }else{ rc = whereLoopAddBtree(pBuilder, mExtra); } @@ -111628,7 +113495,8 @@ static int wherePathSatisfiesOrderBy( u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */ u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */ u8 isMatch; /* iColumn matches a term of the ORDER BY clause */ - u16 nColumn; /* Number of columns in pIndex */ + u16 nKeyCol; /* Number of key columns in pIndex */ + u16 nColumn; /* Total number of ordered columns in the index */ u16 nOrderBy; /* Number terms in the ORDER BY clause */ int iLoop; /* Index of WhereLoop in pPath being processed */ int i, j; /* Loop counters */ @@ -111720,11 +113588,15 @@ static int wherePathSatisfiesOrderBy( if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){ if( pLoop->wsFlags & WHERE_IPK ){ pIndex = 0; - nColumn = 0; + nKeyCol = 0; + nColumn = 1; }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){ return 0; }else{ + nKeyCol = pIndex->nKeyCol; nColumn = pIndex->nColumn; + assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) ); + assert( pIndex->aiColumn[nColumn-1]==(-1) || !HasRowid(pIndex->pTable)); isOrderDistinct = pIndex->onError!=OE_None; } @@ -111733,11 +113605,12 @@ static int wherePathSatisfiesOrderBy( */ rev = revSet = 0; distinctColumns = 0; - for(j=0; j<=nColumn; j++){ + for(j=0; ju.btree.nEq + && pLoop->u.btree.nSkip==0 && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0 ){ if( i & WO_ISNULL ){ @@ -111750,20 +113623,17 @@ static int wherePathSatisfiesOrderBy( /* Get the column number in the table (iColumn) and sort order ** (revIdx) for the j-th column of the index. */ - if( jaiColumn[j]; revIdx = pIndex->aSortOrder[j]; if( iColumn==pIndex->pTable->iPKey ) iColumn = -1; }else{ - /* The ROWID column at the end */ - assert( j==nColumn ); iColumn = -1; revIdx = 0; } /* An unconstrained column that might be NULL means that this - ** WhereLoop is not well-ordered + ** WhereLoop is not well-ordered */ if( isOrderDistinct && iColumn>=0 @@ -111814,7 +113684,7 @@ static int wherePathSatisfiesOrderBy( } }else{ /* No match found */ - if( j==0 || j=nTo ){ if( nTo>=mxChoice && rCost>=mxCost ){ -#ifdef WHERETRACE_ENABLED +#ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("Skip %s cost=%-3d,%3d order=%c\n", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, @@ -112005,7 +113875,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ jj = mxI; } pTo = &aTo[jj]; -#ifdef WHERETRACE_ENABLED +#ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("New %s cost=%-3d,%3d order=%c\n", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, @@ -112014,7 +113884,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ #endif }else{ if( pTo->rCost<=rCost && pTo->nRow<=nOut ){ -#ifdef WHERETRACE_ENABLED +#ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Skip %s cost=%-3d,%3d order=%c", @@ -112030,7 +113900,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } testcase( pTo->rCost==rCost+1 ); /* A new and better score for a previously created equivalent path */ -#ifdef WHERETRACE_ENABLED +#ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Update %s cost=%-3d,%3d order=%c", @@ -112066,7 +113936,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } } -#ifdef WHERETRACE_ENABLED +#ifdef WHERETRACE_ENABLED /* >=2 */ if( sqlite3WhereTrace>=2 ){ sqlite3DebugPrintf("---- after round %d ----\n", iLoop); for(ii=0, pTo=aTo; iisWC; pLoop = pBuilder->pNew; pLoop->wsFlags = 0; + pLoop->u.btree.nSkip = 0; pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0); if( pTerm ){ pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; @@ -112180,16 +114051,16 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ assert( ArraySize(pLoop->aLTermSpace)==4 ); if( pIdx->onError==OE_None || pIdx->pPartIdxWhere!=0 - || pIdx->nColumn>ArraySize(pLoop->aLTermSpace) + || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace) ) continue; - for(j=0; jnColumn; j++){ + for(j=0; jnKeyCol; j++){ pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx); if( pTerm==0 ) break; pLoop->aLTerm[j] = pTerm; } - if( j!=pIdx->nColumn ) continue; + if( j!=pIdx->nKeyCol ) continue; pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED; - if( (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){ + if( pIdx->isCovering || (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){ pLoop->wsFlags |= WHERE_IDX_ONLY; } pLoop->nLTerm = j; @@ -112297,6 +114168,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ ** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement ** if there is one. If there is no ORDER BY clause or if this routine ** is called from an UPDATE or DELETE statement, then pOrderBy is NULL. +** +** The iIdxCur parameter is the cursor number of an index. If +** WHERE_ONETABLE_ONLY is set, iIdxCur is the cursor number of an index +** to use for OR clause processing. The WHERE clause should use this +** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is +** the first cursor in an array of cursors for all indices. iIdxCur should +** be used to compute the appropriate cursor depending on which index is +** used. */ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ @@ -112362,6 +114241,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( pWInfo = 0; goto whereBeginError; } + pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; pWInfo->nLevel = nTabList; pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; @@ -112385,16 +114265,18 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( */ initMaskSet(pMaskSet); whereClauseInit(&pWInfo->sWC, pWInfo); - sqlite3ExprCodeConstants(pParse, pWhere); whereSplit(&pWInfo->sWC, pWhere, TK_AND); sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. */ - if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){ - sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL); - pWhere = 0; + for(ii=0; iinTerm; ii++){ + if( nTabList==0 || sqlite3ExprIsConstantNotJoin(sWLB.pWC->a[ii].pExpr) ){ + sqlite3ExprIfFalse(pParse, sWLB.pWC->a[ii].pExpr, pWInfo->iBreak, + SQLITE_JUMPIFNULL); + sWLB.pWC->a[ii].wtFlags |= TERM_CODED; + } } /* Special case: No FROM clause @@ -112475,12 +114357,29 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( /* Construct the WhereLoop objects */ WHERETRACE(0xffff,("*** Optimizer Start ***\n")); + /* Display all terms of the WHERE clause */ +#if defined(WHERETRACE_ENABLED) && defined(SQLITE_ENABLE_TREE_EXPLAIN) + if( sqlite3WhereTrace & 0x100 ){ + int i; + Vdbe *v = pParse->pVdbe; + sqlite3ExplainBegin(v); + for(i=0; inTerm; i++){ + sqlite3ExplainPrintf(v, "#%-2d ", i); + sqlite3ExplainPush(v); + whereExplainTerm(v, &sWLB.pWC->a[i]); + sqlite3ExplainPop(v); + sqlite3ExplainNL(v); + } + sqlite3ExplainFinish(v); + sqlite3DebugPrintf("%s", sqlite3VdbeExplanation(v)); + } +#endif if( nTabList!=1 || whereShortCut(&sWLB)==0 ){ rc = whereLoopAddAll(&sWLB); if( rc ) goto whereBeginError; /* Display all of the WhereLoop objects if wheretrace is enabled */ -#ifdef WHERETRACE_ENABLED +#ifdef WHERETRACE_ENABLED /* !=0 */ if( sqlite3WhereTrace ){ WhereLoop *p; int i; @@ -112488,7 +114387,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){ p->cId = zLabel[i%sizeof(zLabel)]; - whereLoopPrint(p, pTabList); + whereLoopPrint(p, sWLB.pWC); } } #endif @@ -112506,7 +114405,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( pParse->nErr || NEVER(db->mallocFailed) ){ goto whereBeginError; } -#ifdef WHERETRACE_ENABLED +#ifdef WHERETRACE_ENABLED /* !=0 */ if( sqlite3WhereTrace ){ int ii; sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); @@ -112529,7 +114428,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( } sqlite3DebugPrintf("\n"); for(ii=0; iinLevel; ii++){ - whereLoopPrint(pWInfo->a[ii].pWLoop, pTabList); + whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC); } } #endif @@ -112576,7 +114475,9 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 && (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){ pWInfo->okOnePass = 1; - pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY; + if( HasRowid(pTabList->a[0].pTab) ){ + pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY; + } } /* Open all tables in the pTabList and any indices selected for @@ -112606,11 +114507,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( #endif if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ - int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; + int op = OP_OpenRead; + if( pWInfo->okOnePass ){ + op = OP_OpenWrite; + pWInfo->aiCurOnePass[0] = pTabItem->iCursor; + }; sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); + assert( pTabItem->iCursor==pLevel->iTabCur ); testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 ); testcase( !pWInfo->okOnePass && pTab->nCol==BMS ); - if( !pWInfo->okOnePass && pTab->nColokOnePass && pTab->nColcolUsed; int n = 0; for(; b; b=b>>1, n++){} @@ -112623,13 +114529,30 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( } if( pLoop->wsFlags & WHERE_INDEXED ){ Index *pIx = pLoop->u.btree.pIndex; - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx); - /* FIXME: As an optimization use pTabItem->iCursor if WHERE_IDX_ONLY */ - int iIndexCur = pLevel->iIdxCur = iIdxCur ? iIdxCur : pParse->nTab++; + int iIndexCur; + int op = OP_OpenRead; + /* iIdxCur is always set if to a positive value if ONEPASS is possible */ + assert( iIdxCur!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 ); + if( pWInfo->okOnePass ){ + Index *pJ = pTabItem->pTab->pIndex; + iIndexCur = iIdxCur; + assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); + while( ALWAYS(pJ) && pJ!=pIx ){ + iIndexCur++; + pJ = pJ->pNext; + } + op = OP_OpenWrite; + pWInfo->aiCurOnePass[1] = iIndexCur; + }else if( iIdxCur && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){ + iIndexCur = iIdxCur; + }else{ + iIndexCur = pParse->nTab++; + } + pLevel->iIdxCur = iIndexCur; assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); - sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb, - (char*)pKey, P4_KEYINFO_HANDOFF); + sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIx); VdbeComment((v, "%s", pIx->zName)); } sqlite3CodeVerifySchema(pParse, iDb); @@ -112659,6 +114582,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( } /* Done. */ + VdbeModuleComment((v, "Begin WHERE-core")); return pWInfo; /* Jump here if malloc fails */ @@ -112685,8 +114609,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ /* Generate loop termination code. */ + VdbeModuleComment((v, "End WHERE-core")); sqlite3ExprCacheClear(pParse); for(i=pWInfo->nLevel-1; i>=0; i--){ + int addr; pLevel = &pWInfo->a[i]; pLoop = pLevel->pWLoop; sqlite3VdbeResolveLabel(v, pLevel->addrCont); @@ -112706,8 +114632,13 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3DbFree(db, pLevel->u.in.aInLoop); } sqlite3VdbeResolveLabel(v, pLevel->addrBrk); + if( pLevel->addrSkip ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrSkip); + VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName)); + sqlite3VdbeJumpHere(v, pLevel->addrSkip); + sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); + } if( pLevel->iLeftJoin ){ - int addr; addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || (pLoop->wsFlags & WHERE_INDEXED)!=0 ); @@ -112724,6 +114655,8 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ } sqlite3VdbeJumpHere(v, addr); } + VdbeModuleComment((v, "End WHERE-loop%d: %s", i, + pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } /* The "break" point is here, just past the end of the outer loop. @@ -112731,8 +114664,6 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ */ sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - /* Close all of the cursors that were opened by sqlite3WhereBegin. - */ assert( pWInfo->nLevel<=pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ Index *pIdx = 0; @@ -112740,6 +114671,12 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ Table *pTab = pTabItem->pTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; + + /* Close all of the cursors that were opened by sqlite3WhereBegin. + ** Except, do not close cursors that will be reused by the OR optimization + ** (WHERE_OMIT_OPEN_CLOSE). And do not close the OP_OpenWrite cursors + ** created for the ONEPASS optimization. + */ if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 @@ -112748,7 +114685,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } - if( (ws & WHERE_INDEXED)!=0 && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 ){ + if( (ws & WHERE_INDEXED)!=0 + && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 + && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] + ){ sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); } } @@ -112770,7 +114710,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ pIdx = pLevel->u.pCovidx; } if( pIdx && !db->mallocFailed ){ - int k, j, last; + int k, last; VdbeOp *pOp; last = sqlite3VdbeCurrentAddr(v); @@ -112779,14 +114719,18 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ for(; kp1!=pLevel->iTabCur ) continue; if( pOp->opcode==OP_Column ){ - for(j=0; jnColumn; j++){ - if( pOp->p2==pIdx->aiColumn[j] ){ - pOp->p2 = j; - pOp->p1 = pLevel->iIdxCur; - break; - } + int x = pOp->p2; + assert( pIdx->pTable==pTab ); + if( !HasRowid(pTab) ){ + Index *pPk = sqlite3PrimaryKeyIndex(pTab); + x = pPk->aiColumn[x]; } - assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || jnColumn ); + x = sqlite3ColumnOfIndex(pIdx, x); + if( x>=0 ){ + pOp->p2 = x; + pOp->p1 = pLevel->iIdxCur; + } + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; @@ -112863,14 +114807,6 @@ struct TrigEvent { int a; IdList * b; }; */ struct AttachKey { int type; Token key; }; -/* -** One or more VALUES claues -*/ -struct ValueList { - ExprList *pList; - Select *pSelect; -}; - /* This is a utility routine used to set the ExprSpan.zStart and ** ExprSpan.zEnd values of pOut so that the span covers the complete @@ -112994,28 +114930,28 @@ struct ValueList { ** defined, then do no error processing. */ #define YYCODETYPE unsigned char -#define YYNOCODE 251 +#define YYNOCODE 254 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 67 +#define YYWILDCARD 70 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - struct LimitVal yy64; - Expr* yy122; - Select* yy159; - IdList* yy180; - struct {int value; int mask;} yy207; - u8 yy258; - u16 yy305; - struct LikeOp yy318; - TriggerStep* yy327; - ExprSpan yy342; - SrcList* yy347; - int yy392; - struct TrigEvent yy410; - ExprList* yy442; - struct ValueList yy487; + Select* yy3; + ExprList* yy14; + With* yy59; + SrcList* yy65; + struct LikeOp yy96; + Expr* yy132; + u8 yy186; + int yy328; + ExprSpan yy346; + struct TrigEvent yy378; + u16 yy381; + IdList* yy408; + struct {int value; int mask;} yy429; + TriggerStep* yy473; + struct LimitVal yy476; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -113024,7 +114960,7 @@ typedef union { #define sqlite3ParserARG_PDECL ,Parse *pParse #define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse #define sqlite3ParserARG_STORE yypParser->pParse = pParse -#define YYNSTATE 628 +#define YYNSTATE 642 #define YYNRULE 327 #define YYFALLBACK 1 #define YY_NO_ACTION (YYNSTATE+YYNRULE+2) @@ -113095,474 +115031,463 @@ static const YYMINORTYPE yyzerominor = { 0 }; ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. */ -#define YY_ACTTAB_COUNT (1564) +#define YY_ACTTAB_COUNT (1497) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 310, 956, 184, 418, 2, 171, 625, 595, 56, 56, - /* 10 */ 56, 56, 49, 54, 54, 54, 54, 53, 53, 52, - /* 20 */ 52, 52, 51, 233, 621, 620, 299, 621, 620, 234, - /* 30 */ 588, 582, 56, 56, 56, 56, 19, 54, 54, 54, - /* 40 */ 54, 53, 53, 52, 52, 52, 51, 233, 606, 57, - /* 50 */ 58, 48, 580, 579, 581, 581, 55, 55, 56, 56, - /* 60 */ 56, 56, 542, 54, 54, 54, 54, 53, 53, 52, - /* 70 */ 52, 52, 51, 233, 310, 595, 326, 196, 195, 194, - /* 80 */ 33, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 90 */ 51, 233, 618, 617, 165, 618, 617, 381, 378, 377, - /* 100 */ 408, 533, 577, 577, 588, 582, 304, 423, 376, 59, - /* 110 */ 53, 53, 52, 52, 52, 51, 233, 50, 47, 146, - /* 120 */ 575, 546, 65, 57, 58, 48, 580, 579, 581, 581, - /* 130 */ 55, 55, 56, 56, 56, 56, 213, 54, 54, 54, - /* 140 */ 54, 53, 53, 52, 52, 52, 51, 233, 310, 223, - /* 150 */ 540, 421, 170, 176, 138, 281, 384, 276, 383, 168, - /* 160 */ 490, 552, 410, 669, 621, 620, 272, 439, 410, 439, - /* 170 */ 551, 605, 67, 483, 508, 619, 600, 413, 588, 582, - /* 180 */ 601, 484, 619, 413, 619, 599, 91, 440, 441, 440, - /* 190 */ 336, 599, 73, 670, 222, 267, 481, 57, 58, 48, - /* 200 */ 580, 579, 581, 581, 55, 55, 56, 56, 56, 56, - /* 210 */ 671, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 220 */ 51, 233, 310, 280, 232, 231, 1, 132, 200, 386, - /* 230 */ 621, 620, 618, 617, 279, 436, 290, 564, 175, 263, - /* 240 */ 410, 265, 438, 498, 437, 166, 442, 569, 337, 569, - /* 250 */ 201, 538, 588, 582, 600, 413, 165, 595, 601, 381, - /* 260 */ 378, 377, 598, 599, 92, 524, 619, 570, 570, 593, - /* 270 */ 376, 57, 58, 48, 580, 579, 581, 581, 55, 55, - /* 280 */ 56, 56, 56, 56, 598, 54, 54, 54, 54, 53, - /* 290 */ 53, 52, 52, 52, 51, 233, 310, 464, 618, 617, - /* 300 */ 591, 591, 591, 174, 273, 397, 410, 273, 410, 549, - /* 310 */ 398, 621, 620, 68, 327, 621, 620, 621, 620, 619, - /* 320 */ 547, 413, 619, 413, 472, 595, 588, 582, 473, 599, - /* 330 */ 92, 599, 92, 52, 52, 52, 51, 233, 514, 513, - /* 340 */ 206, 323, 364, 465, 221, 57, 58, 48, 580, 579, - /* 350 */ 581, 581, 55, 55, 56, 56, 56, 56, 530, 54, - /* 360 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233, - /* 370 */ 310, 397, 410, 397, 598, 373, 387, 531, 348, 618, - /* 380 */ 617, 576, 202, 618, 617, 618, 617, 413, 621, 620, - /* 390 */ 145, 255, 347, 254, 578, 599, 74, 352, 45, 490, - /* 400 */ 588, 582, 235, 189, 465, 545, 167, 297, 187, 470, - /* 410 */ 480, 67, 62, 39, 619, 547, 598, 346, 574, 57, - /* 420 */ 58, 48, 580, 579, 581, 581, 55, 55, 56, 56, - /* 430 */ 56, 56, 6, 54, 54, 54, 54, 53, 53, 52, - /* 440 */ 52, 52, 51, 233, 310, 563, 559, 408, 529, 577, - /* 450 */ 577, 345, 255, 347, 254, 182, 618, 617, 504, 505, - /* 460 */ 315, 410, 558, 235, 166, 272, 410, 353, 565, 181, - /* 470 */ 408, 547, 577, 577, 588, 582, 413, 538, 557, 562, - /* 480 */ 518, 413, 619, 249, 599, 16, 7, 36, 468, 599, - /* 490 */ 92, 517, 619, 57, 58, 48, 580, 579, 581, 581, - /* 500 */ 55, 55, 56, 56, 56, 56, 542, 54, 54, 54, - /* 510 */ 54, 53, 53, 52, 52, 52, 51, 233, 310, 328, - /* 520 */ 573, 572, 526, 559, 561, 395, 872, 246, 410, 248, - /* 530 */ 171, 393, 595, 219, 408, 410, 577, 577, 503, 558, - /* 540 */ 365, 145, 511, 413, 408, 229, 577, 577, 588, 582, - /* 550 */ 413, 599, 92, 382, 270, 557, 166, 401, 599, 69, - /* 560 */ 502, 420, 946, 199, 946, 198, 547, 57, 58, 48, - /* 570 */ 580, 579, 581, 581, 55, 55, 56, 56, 56, 56, - /* 580 */ 569, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 590 */ 51, 233, 310, 318, 420, 945, 509, 945, 309, 598, - /* 600 */ 595, 566, 491, 212, 173, 247, 424, 616, 615, 614, - /* 610 */ 324, 197, 143, 406, 573, 572, 490, 66, 50, 47, - /* 620 */ 146, 595, 588, 582, 232, 231, 560, 428, 67, 556, - /* 630 */ 15, 619, 186, 544, 304, 422, 35, 206, 433, 424, - /* 640 */ 553, 57, 58, 48, 580, 579, 581, 581, 55, 55, - /* 650 */ 56, 56, 56, 56, 205, 54, 54, 54, 54, 53, - /* 660 */ 53, 52, 52, 52, 51, 233, 310, 570, 570, 261, - /* 670 */ 269, 598, 12, 374, 569, 166, 410, 314, 410, 421, - /* 680 */ 410, 474, 474, 366, 619, 50, 47, 146, 598, 595, - /* 690 */ 256, 413, 166, 413, 352, 413, 588, 582, 32, 599, - /* 700 */ 94, 599, 97, 599, 95, 628, 626, 330, 142, 50, - /* 710 */ 47, 146, 334, 350, 359, 57, 58, 48, 580, 579, - /* 720 */ 581, 581, 55, 55, 56, 56, 56, 56, 410, 54, - /* 730 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233, - /* 740 */ 310, 410, 389, 413, 410, 22, 566, 405, 212, 363, - /* 750 */ 390, 599, 104, 360, 410, 156, 413, 410, 604, 413, - /* 760 */ 538, 332, 570, 570, 599, 103, 494, 599, 105, 413, - /* 770 */ 588, 582, 413, 261, 550, 619, 11, 599, 106, 522, - /* 780 */ 599, 133, 169, 458, 457, 170, 35, 602, 619, 57, - /* 790 */ 58, 48, 580, 579, 581, 581, 55, 55, 56, 56, - /* 800 */ 56, 56, 410, 54, 54, 54, 54, 53, 53, 52, - /* 810 */ 52, 52, 51, 233, 310, 410, 260, 413, 410, 50, - /* 820 */ 47, 146, 358, 319, 356, 599, 134, 528, 353, 338, - /* 830 */ 413, 410, 357, 413, 358, 410, 358, 619, 599, 98, - /* 840 */ 129, 599, 102, 619, 588, 582, 413, 21, 235, 619, - /* 850 */ 413, 619, 211, 143, 599, 101, 30, 167, 599, 93, - /* 860 */ 351, 536, 203, 57, 58, 48, 580, 579, 581, 581, - /* 870 */ 55, 55, 56, 56, 56, 56, 410, 54, 54, 54, - /* 880 */ 54, 53, 53, 52, 52, 52, 51, 233, 310, 410, - /* 890 */ 527, 413, 410, 426, 215, 306, 598, 552, 141, 599, - /* 900 */ 100, 40, 410, 38, 413, 410, 551, 413, 410, 228, - /* 910 */ 220, 315, 599, 77, 501, 599, 96, 413, 588, 582, - /* 920 */ 413, 339, 253, 413, 218, 599, 137, 380, 599, 136, - /* 930 */ 28, 599, 135, 271, 716, 210, 482, 57, 58, 48, - /* 940 */ 580, 579, 581, 581, 55, 55, 56, 56, 56, 56, - /* 950 */ 410, 54, 54, 54, 54, 53, 53, 52, 52, 52, - /* 960 */ 51, 233, 310, 410, 273, 413, 410, 316, 147, 598, - /* 970 */ 273, 627, 2, 599, 76, 209, 410, 127, 413, 619, - /* 980 */ 126, 413, 410, 622, 235, 619, 599, 90, 375, 599, - /* 990 */ 89, 413, 588, 582, 27, 261, 351, 413, 619, 599, - /* 1000 */ 75, 322, 542, 542, 125, 599, 88, 321, 279, 598, - /* 1010 */ 619, 57, 46, 48, 580, 579, 581, 581, 55, 55, - /* 1020 */ 56, 56, 56, 56, 410, 54, 54, 54, 54, 53, - /* 1030 */ 53, 52, 52, 52, 51, 233, 310, 410, 451, 413, - /* 1040 */ 164, 285, 283, 273, 610, 425, 305, 599, 87, 371, - /* 1050 */ 410, 478, 413, 410, 609, 410, 608, 603, 619, 619, - /* 1060 */ 599, 99, 587, 586, 122, 413, 588, 582, 413, 619, - /* 1070 */ 413, 619, 619, 599, 86, 367, 599, 17, 599, 85, - /* 1080 */ 320, 185, 520, 519, 584, 583, 58, 48, 580, 579, - /* 1090 */ 581, 581, 55, 55, 56, 56, 56, 56, 410, 54, - /* 1100 */ 54, 54, 54, 53, 53, 52, 52, 52, 51, 233, - /* 1110 */ 310, 585, 410, 413, 410, 261, 261, 261, 409, 592, - /* 1120 */ 475, 599, 84, 170, 410, 467, 519, 413, 121, 413, - /* 1130 */ 619, 619, 619, 619, 619, 599, 83, 599, 72, 413, - /* 1140 */ 588, 582, 51, 233, 626, 330, 471, 599, 71, 258, - /* 1150 */ 159, 120, 14, 463, 157, 158, 117, 261, 449, 448, - /* 1160 */ 447, 48, 580, 579, 581, 581, 55, 55, 56, 56, - /* 1170 */ 56, 56, 619, 54, 54, 54, 54, 53, 53, 52, - /* 1180 */ 52, 52, 51, 233, 44, 404, 261, 3, 410, 460, - /* 1190 */ 261, 414, 620, 118, 399, 10, 25, 24, 555, 349, - /* 1200 */ 217, 619, 407, 413, 410, 619, 4, 44, 404, 619, - /* 1210 */ 3, 599, 82, 619, 414, 620, 456, 543, 115, 413, - /* 1220 */ 539, 402, 537, 275, 507, 407, 251, 599, 81, 216, - /* 1230 */ 274, 564, 619, 243, 454, 619, 154, 619, 619, 619, - /* 1240 */ 450, 417, 624, 110, 402, 619, 410, 236, 64, 123, - /* 1250 */ 488, 41, 42, 532, 564, 204, 410, 268, 43, 412, - /* 1260 */ 411, 413, 266, 593, 108, 619, 107, 435, 333, 599, - /* 1270 */ 80, 413, 619, 264, 41, 42, 444, 619, 410, 599, - /* 1280 */ 70, 43, 412, 411, 434, 262, 593, 149, 619, 598, - /* 1290 */ 257, 237, 188, 413, 591, 591, 591, 590, 589, 13, - /* 1300 */ 619, 599, 18, 329, 235, 619, 44, 404, 361, 3, - /* 1310 */ 419, 462, 340, 414, 620, 227, 124, 591, 591, 591, - /* 1320 */ 590, 589, 13, 619, 407, 410, 619, 410, 139, 34, - /* 1330 */ 404, 388, 3, 148, 623, 313, 414, 620, 312, 331, - /* 1340 */ 413, 461, 413, 402, 180, 354, 413, 407, 599, 79, - /* 1350 */ 599, 78, 250, 564, 599, 9, 619, 613, 612, 611, - /* 1360 */ 619, 8, 453, 443, 242, 416, 402, 619, 239, 235, - /* 1370 */ 179, 238, 429, 41, 42, 289, 564, 619, 619, 619, - /* 1380 */ 43, 412, 411, 619, 144, 593, 619, 619, 177, 61, - /* 1390 */ 619, 597, 392, 621, 620, 288, 41, 42, 415, 619, - /* 1400 */ 294, 30, 394, 43, 412, 411, 293, 619, 593, 31, - /* 1410 */ 619, 396, 292, 60, 230, 37, 591, 591, 591, 590, - /* 1420 */ 589, 13, 214, 554, 183, 291, 172, 302, 301, 300, - /* 1430 */ 178, 298, 596, 564, 452, 29, 286, 391, 541, 591, - /* 1440 */ 591, 591, 590, 589, 13, 284, 521, 535, 150, 534, - /* 1450 */ 241, 282, 385, 192, 191, 325, 516, 515, 277, 240, - /* 1460 */ 511, 524, 308, 512, 128, 593, 510, 225, 226, 487, - /* 1470 */ 486, 224, 152, 492, 465, 307, 485, 163, 153, 372, - /* 1480 */ 479, 151, 162, 259, 370, 161, 368, 208, 476, 477, - /* 1490 */ 26, 160, 469, 466, 362, 140, 591, 591, 591, 116, - /* 1500 */ 119, 455, 344, 155, 114, 343, 113, 112, 446, 111, - /* 1510 */ 131, 109, 432, 317, 130, 431, 23, 20, 430, 427, - /* 1520 */ 190, 63, 255, 342, 244, 607, 295, 287, 311, 594, - /* 1530 */ 278, 508, 496, 235, 493, 571, 497, 568, 495, 403, - /* 1540 */ 459, 379, 355, 245, 193, 303, 567, 296, 341, 5, - /* 1550 */ 445, 548, 506, 207, 525, 500, 335, 489, 252, 369, - /* 1560 */ 400, 499, 523, 233, + /* 0 */ 306, 212, 432, 955, 639, 191, 955, 295, 559, 88, + /* 10 */ 88, 88, 88, 81, 86, 86, 86, 86, 85, 85, + /* 20 */ 84, 84, 84, 83, 330, 185, 184, 183, 635, 635, + /* 30 */ 292, 606, 606, 88, 88, 88, 88, 683, 86, 86, + /* 40 */ 86, 86, 85, 85, 84, 84, 84, 83, 330, 16, + /* 50 */ 436, 597, 89, 90, 80, 600, 599, 601, 601, 87, + /* 60 */ 87, 88, 88, 88, 88, 684, 86, 86, 86, 86, + /* 70 */ 85, 85, 84, 84, 84, 83, 330, 306, 559, 84, + /* 80 */ 84, 84, 83, 330, 65, 86, 86, 86, 86, 85, + /* 90 */ 85, 84, 84, 84, 83, 330, 635, 635, 634, 633, + /* 100 */ 182, 682, 550, 379, 376, 375, 17, 322, 606, 606, + /* 110 */ 371, 198, 479, 91, 374, 82, 79, 165, 85, 85, + /* 120 */ 84, 84, 84, 83, 330, 598, 635, 635, 107, 89, + /* 130 */ 90, 80, 600, 599, 601, 601, 87, 87, 88, 88, + /* 140 */ 88, 88, 186, 86, 86, 86, 86, 85, 85, 84, + /* 150 */ 84, 84, 83, 330, 306, 594, 594, 142, 328, 327, + /* 160 */ 484, 249, 344, 238, 635, 635, 634, 633, 585, 448, + /* 170 */ 526, 525, 229, 388, 1, 394, 450, 584, 449, 635, + /* 180 */ 635, 635, 635, 319, 395, 606, 606, 199, 157, 273, + /* 190 */ 382, 268, 381, 187, 635, 635, 634, 633, 311, 555, + /* 200 */ 266, 593, 593, 266, 347, 588, 89, 90, 80, 600, + /* 210 */ 599, 601, 601, 87, 87, 88, 88, 88, 88, 478, + /* 220 */ 86, 86, 86, 86, 85, 85, 84, 84, 84, 83, + /* 230 */ 330, 306, 272, 536, 634, 633, 146, 610, 197, 310, + /* 240 */ 575, 182, 482, 271, 379, 376, 375, 506, 21, 634, + /* 250 */ 633, 634, 633, 635, 635, 374, 611, 574, 548, 440, + /* 260 */ 111, 563, 606, 606, 634, 633, 324, 479, 608, 608, + /* 270 */ 608, 300, 435, 573, 119, 407, 210, 162, 562, 883, + /* 280 */ 592, 592, 306, 89, 90, 80, 600, 599, 601, 601, + /* 290 */ 87, 87, 88, 88, 88, 88, 506, 86, 86, 86, + /* 300 */ 86, 85, 85, 84, 84, 84, 83, 330, 620, 111, + /* 310 */ 635, 635, 361, 606, 606, 358, 249, 349, 248, 433, + /* 320 */ 243, 479, 586, 634, 633, 195, 611, 93, 119, 221, + /* 330 */ 575, 497, 534, 534, 89, 90, 80, 600, 599, 601, + /* 340 */ 601, 87, 87, 88, 88, 88, 88, 574, 86, 86, + /* 350 */ 86, 86, 85, 85, 84, 84, 84, 83, 330, 306, + /* 360 */ 77, 429, 638, 573, 589, 530, 240, 230, 242, 105, + /* 370 */ 249, 349, 248, 515, 588, 208, 460, 529, 564, 173, + /* 380 */ 634, 633, 970, 144, 430, 2, 424, 228, 380, 557, + /* 390 */ 606, 606, 190, 153, 159, 158, 514, 51, 632, 631, + /* 400 */ 630, 71, 536, 432, 954, 196, 610, 954, 614, 45, + /* 410 */ 18, 89, 90, 80, 600, 599, 601, 601, 87, 87, + /* 420 */ 88, 88, 88, 88, 261, 86, 86, 86, 86, 85, + /* 430 */ 85, 84, 84, 84, 83, 330, 306, 608, 608, 608, + /* 440 */ 542, 424, 402, 385, 241, 506, 451, 320, 211, 543, + /* 450 */ 164, 436, 386, 293, 451, 587, 108, 496, 111, 334, + /* 460 */ 391, 591, 424, 614, 27, 452, 453, 606, 606, 72, + /* 470 */ 257, 70, 259, 452, 339, 342, 564, 582, 68, 415, + /* 480 */ 469, 328, 327, 62, 614, 45, 110, 393, 89, 90, + /* 490 */ 80, 600, 599, 601, 601, 87, 87, 88, 88, 88, + /* 500 */ 88, 152, 86, 86, 86, 86, 85, 85, 84, 84, + /* 510 */ 84, 83, 330, 306, 110, 499, 520, 538, 402, 389, + /* 520 */ 424, 110, 566, 500, 593, 593, 454, 82, 79, 165, + /* 530 */ 424, 591, 384, 564, 340, 615, 188, 162, 424, 350, + /* 540 */ 616, 424, 614, 44, 606, 606, 445, 582, 300, 434, + /* 550 */ 151, 19, 614, 9, 568, 580, 348, 615, 469, 567, + /* 560 */ 614, 26, 616, 614, 45, 89, 90, 80, 600, 599, + /* 570 */ 601, 601, 87, 87, 88, 88, 88, 88, 411, 86, + /* 580 */ 86, 86, 86, 85, 85, 84, 84, 84, 83, 330, + /* 590 */ 306, 579, 110, 578, 521, 282, 433, 398, 400, 255, + /* 600 */ 486, 82, 79, 165, 487, 164, 82, 79, 165, 488, + /* 610 */ 488, 364, 387, 424, 544, 544, 509, 350, 362, 155, + /* 620 */ 191, 606, 606, 559, 642, 640, 333, 82, 79, 165, + /* 630 */ 305, 564, 507, 312, 357, 614, 45, 329, 596, 595, + /* 640 */ 194, 337, 89, 90, 80, 600, 599, 601, 601, 87, + /* 650 */ 87, 88, 88, 88, 88, 424, 86, 86, 86, 86, + /* 660 */ 85, 85, 84, 84, 84, 83, 330, 306, 20, 323, + /* 670 */ 150, 263, 211, 543, 421, 596, 595, 614, 22, 424, + /* 680 */ 193, 424, 284, 424, 391, 424, 509, 424, 577, 424, + /* 690 */ 186, 335, 424, 559, 424, 313, 120, 546, 606, 606, + /* 700 */ 67, 614, 47, 614, 50, 614, 48, 614, 100, 614, + /* 710 */ 99, 614, 101, 576, 614, 102, 614, 109, 326, 89, + /* 720 */ 90, 80, 600, 599, 601, 601, 87, 87, 88, 88, + /* 730 */ 88, 88, 424, 86, 86, 86, 86, 85, 85, 84, + /* 740 */ 84, 84, 83, 330, 306, 424, 311, 424, 585, 54, + /* 750 */ 424, 516, 517, 590, 614, 112, 424, 584, 424, 572, + /* 760 */ 424, 195, 424, 571, 424, 67, 424, 614, 94, 614, + /* 770 */ 98, 424, 614, 97, 264, 606, 606, 195, 614, 46, + /* 780 */ 614, 96, 614, 30, 614, 49, 614, 115, 614, 114, + /* 790 */ 418, 229, 388, 614, 113, 306, 89, 90, 80, 600, + /* 800 */ 599, 601, 601, 87, 87, 88, 88, 88, 88, 424, + /* 810 */ 86, 86, 86, 86, 85, 85, 84, 84, 84, 83, + /* 820 */ 330, 119, 424, 590, 110, 372, 606, 606, 195, 53, + /* 830 */ 250, 614, 29, 195, 472, 438, 729, 190, 302, 498, + /* 840 */ 14, 523, 641, 2, 614, 43, 306, 89, 90, 80, + /* 850 */ 600, 599, 601, 601, 87, 87, 88, 88, 88, 88, + /* 860 */ 424, 86, 86, 86, 86, 85, 85, 84, 84, 84, + /* 870 */ 83, 330, 424, 613, 964, 964, 354, 606, 606, 420, + /* 880 */ 312, 64, 614, 42, 391, 355, 283, 437, 301, 255, + /* 890 */ 414, 410, 495, 492, 614, 28, 471, 306, 89, 90, + /* 900 */ 80, 600, 599, 601, 601, 87, 87, 88, 88, 88, + /* 910 */ 88, 424, 86, 86, 86, 86, 85, 85, 84, 84, + /* 920 */ 84, 83, 330, 424, 110, 110, 110, 110, 606, 606, + /* 930 */ 110, 254, 13, 614, 41, 532, 531, 283, 481, 531, + /* 940 */ 457, 284, 119, 561, 356, 614, 40, 284, 306, 89, + /* 950 */ 78, 80, 600, 599, 601, 601, 87, 87, 88, 88, + /* 960 */ 88, 88, 424, 86, 86, 86, 86, 85, 85, 84, + /* 970 */ 84, 84, 83, 330, 110, 424, 341, 220, 555, 606, + /* 980 */ 606, 351, 555, 318, 614, 95, 413, 255, 83, 330, + /* 990 */ 284, 284, 255, 640, 333, 356, 255, 614, 39, 306, + /* 1000 */ 356, 90, 80, 600, 599, 601, 601, 87, 87, 88, + /* 1010 */ 88, 88, 88, 424, 86, 86, 86, 86, 85, 85, + /* 1020 */ 84, 84, 84, 83, 330, 424, 317, 316, 141, 465, + /* 1030 */ 606, 606, 219, 619, 463, 614, 10, 417, 462, 255, + /* 1040 */ 189, 510, 553, 351, 207, 363, 161, 614, 38, 315, + /* 1050 */ 218, 255, 255, 80, 600, 599, 601, 601, 87, 87, + /* 1060 */ 88, 88, 88, 88, 424, 86, 86, 86, 86, 85, + /* 1070 */ 85, 84, 84, 84, 83, 330, 76, 419, 255, 3, + /* 1080 */ 878, 461, 424, 247, 331, 331, 614, 37, 217, 76, + /* 1090 */ 419, 390, 3, 216, 215, 422, 4, 331, 331, 424, + /* 1100 */ 547, 12, 424, 545, 614, 36, 424, 541, 422, 424, + /* 1110 */ 540, 424, 214, 424, 408, 424, 539, 403, 605, 605, + /* 1120 */ 237, 614, 25, 119, 614, 24, 588, 408, 614, 45, + /* 1130 */ 118, 614, 35, 614, 34, 614, 33, 614, 23, 588, + /* 1140 */ 60, 223, 603, 602, 513, 378, 73, 74, 140, 139, + /* 1150 */ 424, 110, 265, 75, 426, 425, 59, 424, 610, 73, + /* 1160 */ 74, 549, 402, 404, 424, 373, 75, 426, 425, 604, + /* 1170 */ 138, 610, 614, 11, 392, 76, 419, 181, 3, 614, + /* 1180 */ 32, 271, 369, 331, 331, 493, 614, 31, 149, 608, + /* 1190 */ 608, 608, 607, 15, 422, 365, 614, 8, 137, 489, + /* 1200 */ 136, 190, 608, 608, 608, 607, 15, 485, 176, 135, + /* 1210 */ 7, 252, 477, 408, 174, 133, 175, 474, 57, 56, + /* 1220 */ 132, 130, 119, 76, 419, 588, 3, 468, 245, 464, + /* 1230 */ 171, 331, 331, 125, 123, 456, 447, 122, 446, 104, + /* 1240 */ 336, 231, 422, 166, 154, 73, 74, 332, 116, 431, + /* 1250 */ 121, 309, 75, 426, 425, 222, 106, 610, 308, 637, + /* 1260 */ 204, 408, 629, 627, 628, 6, 200, 428, 427, 290, + /* 1270 */ 203, 622, 201, 588, 62, 63, 289, 66, 419, 399, + /* 1280 */ 3, 401, 288, 92, 143, 331, 331, 287, 608, 608, + /* 1290 */ 608, 607, 15, 73, 74, 227, 422, 325, 69, 416, + /* 1300 */ 75, 426, 425, 612, 412, 610, 192, 61, 569, 209, + /* 1310 */ 396, 226, 278, 225, 383, 408, 527, 558, 276, 533, + /* 1320 */ 552, 528, 321, 523, 370, 508, 180, 588, 494, 179, + /* 1330 */ 366, 117, 253, 269, 522, 503, 608, 608, 608, 607, + /* 1340 */ 15, 551, 502, 58, 274, 524, 178, 73, 74, 304, + /* 1350 */ 501, 368, 303, 206, 75, 426, 425, 491, 360, 610, + /* 1360 */ 213, 177, 483, 131, 345, 298, 297, 296, 202, 294, + /* 1370 */ 480, 490, 466, 134, 172, 129, 444, 346, 470, 128, + /* 1380 */ 314, 459, 103, 127, 126, 148, 124, 167, 443, 235, + /* 1390 */ 608, 608, 608, 607, 15, 442, 439, 623, 234, 299, + /* 1400 */ 145, 583, 291, 377, 581, 160, 119, 156, 270, 636, + /* 1410 */ 971, 169, 279, 626, 520, 625, 473, 624, 170, 621, + /* 1420 */ 618, 119, 168, 55, 409, 423, 537, 609, 286, 285, + /* 1430 */ 405, 570, 560, 556, 5, 52, 458, 554, 147, 267, + /* 1440 */ 519, 504, 518, 406, 262, 239, 260, 512, 343, 511, + /* 1450 */ 258, 353, 565, 256, 224, 251, 359, 277, 275, 476, + /* 1460 */ 475, 246, 352, 244, 467, 455, 236, 233, 232, 307, + /* 1470 */ 441, 281, 205, 163, 397, 280, 535, 505, 330, 617, + /* 1480 */ 971, 971, 971, 971, 367, 971, 971, 971, 971, 971, + /* 1490 */ 971, 971, 971, 971, 971, 971, 338, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 19, 142, 143, 144, 145, 24, 1, 26, 77, 78, - /* 10 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, - /* 20 */ 89, 90, 91, 92, 26, 27, 15, 26, 27, 197, - /* 30 */ 49, 50, 77, 78, 79, 80, 204, 82, 83, 84, - /* 40 */ 85, 86, 87, 88, 89, 90, 91, 92, 23, 68, - /* 50 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 60 */ 79, 80, 166, 82, 83, 84, 85, 86, 87, 88, - /* 70 */ 89, 90, 91, 92, 19, 94, 19, 105, 106, 107, - /* 80 */ 25, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 90 */ 91, 92, 94, 95, 96, 94, 95, 99, 100, 101, - /* 100 */ 112, 205, 114, 115, 49, 50, 22, 23, 110, 54, - /* 110 */ 86, 87, 88, 89, 90, 91, 92, 221, 222, 223, - /* 120 */ 23, 120, 25, 68, 69, 70, 71, 72, 73, 74, - /* 130 */ 75, 76, 77, 78, 79, 80, 22, 82, 83, 84, - /* 140 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 92, - /* 150 */ 23, 67, 25, 96, 97, 98, 99, 100, 101, 102, - /* 160 */ 150, 32, 150, 118, 26, 27, 109, 150, 150, 150, - /* 170 */ 41, 161, 162, 180, 181, 165, 113, 165, 49, 50, - /* 180 */ 117, 188, 165, 165, 165, 173, 174, 170, 171, 170, - /* 190 */ 171, 173, 174, 118, 184, 16, 186, 68, 69, 70, - /* 200 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 210 */ 118, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 220 */ 91, 92, 19, 98, 86, 87, 22, 24, 160, 88, - /* 230 */ 26, 27, 94, 95, 109, 97, 224, 66, 118, 60, - /* 240 */ 150, 62, 104, 23, 106, 25, 229, 230, 229, 230, - /* 250 */ 160, 150, 49, 50, 113, 165, 96, 26, 117, 99, - /* 260 */ 100, 101, 194, 173, 174, 94, 165, 129, 130, 98, - /* 270 */ 110, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 280 */ 77, 78, 79, 80, 194, 82, 83, 84, 85, 86, - /* 290 */ 87, 88, 89, 90, 91, 92, 19, 11, 94, 95, - /* 300 */ 129, 130, 131, 118, 150, 215, 150, 150, 150, 25, - /* 310 */ 220, 26, 27, 22, 213, 26, 27, 26, 27, 165, - /* 320 */ 25, 165, 165, 165, 30, 94, 49, 50, 34, 173, - /* 330 */ 174, 173, 174, 88, 89, 90, 91, 92, 7, 8, - /* 340 */ 160, 187, 48, 57, 187, 68, 69, 70, 71, 72, - /* 350 */ 73, 74, 75, 76, 77, 78, 79, 80, 23, 82, - /* 360 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - /* 370 */ 19, 215, 150, 215, 194, 19, 220, 88, 220, 94, - /* 380 */ 95, 23, 160, 94, 95, 94, 95, 165, 26, 27, - /* 390 */ 95, 105, 106, 107, 113, 173, 174, 217, 22, 150, - /* 400 */ 49, 50, 116, 119, 57, 120, 50, 158, 22, 21, - /* 410 */ 161, 162, 232, 136, 165, 120, 194, 237, 23, 68, - /* 420 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 430 */ 79, 80, 22, 82, 83, 84, 85, 86, 87, 88, - /* 440 */ 89, 90, 91, 92, 19, 23, 12, 112, 23, 114, - /* 450 */ 115, 63, 105, 106, 107, 23, 94, 95, 97, 98, - /* 460 */ 104, 150, 28, 116, 25, 109, 150, 150, 23, 23, - /* 470 */ 112, 25, 114, 115, 49, 50, 165, 150, 44, 11, - /* 480 */ 46, 165, 165, 16, 173, 174, 76, 136, 100, 173, - /* 490 */ 174, 57, 165, 68, 69, 70, 71, 72, 73, 74, - /* 500 */ 75, 76, 77, 78, 79, 80, 166, 82, 83, 84, - /* 510 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 169, - /* 520 */ 170, 171, 23, 12, 23, 214, 138, 60, 150, 62, - /* 530 */ 24, 215, 26, 216, 112, 150, 114, 115, 36, 28, - /* 540 */ 213, 95, 103, 165, 112, 205, 114, 115, 49, 50, - /* 550 */ 165, 173, 174, 51, 23, 44, 25, 46, 173, 174, - /* 560 */ 58, 22, 23, 22, 25, 160, 120, 68, 69, 70, - /* 570 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 580 */ 230, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 590 */ 91, 92, 19, 215, 22, 23, 23, 25, 163, 194, - /* 600 */ 94, 166, 167, 168, 25, 138, 67, 7, 8, 9, - /* 610 */ 108, 206, 207, 169, 170, 171, 150, 22, 221, 222, - /* 620 */ 223, 26, 49, 50, 86, 87, 23, 161, 162, 23, - /* 630 */ 22, 165, 24, 120, 22, 23, 25, 160, 241, 67, - /* 640 */ 176, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 650 */ 77, 78, 79, 80, 160, 82, 83, 84, 85, 86, - /* 660 */ 87, 88, 89, 90, 91, 92, 19, 129, 130, 150, - /* 670 */ 23, 194, 35, 23, 230, 25, 150, 155, 150, 67, - /* 680 */ 150, 105, 106, 107, 165, 221, 222, 223, 194, 94, - /* 690 */ 23, 165, 25, 165, 217, 165, 49, 50, 25, 173, - /* 700 */ 174, 173, 174, 173, 174, 0, 1, 2, 118, 221, - /* 710 */ 222, 223, 193, 219, 237, 68, 69, 70, 71, 72, - /* 720 */ 73, 74, 75, 76, 77, 78, 79, 80, 150, 82, - /* 730 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - /* 740 */ 19, 150, 19, 165, 150, 24, 166, 167, 168, 227, - /* 750 */ 27, 173, 174, 231, 150, 25, 165, 150, 172, 165, - /* 760 */ 150, 242, 129, 130, 173, 174, 180, 173, 174, 165, - /* 770 */ 49, 50, 165, 150, 176, 165, 35, 173, 174, 165, - /* 780 */ 173, 174, 35, 23, 23, 25, 25, 173, 165, 68, - /* 790 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 800 */ 79, 80, 150, 82, 83, 84, 85, 86, 87, 88, - /* 810 */ 89, 90, 91, 92, 19, 150, 193, 165, 150, 221, - /* 820 */ 222, 223, 150, 213, 19, 173, 174, 23, 150, 97, - /* 830 */ 165, 150, 27, 165, 150, 150, 150, 165, 173, 174, - /* 840 */ 22, 173, 174, 165, 49, 50, 165, 52, 116, 165, - /* 850 */ 165, 165, 206, 207, 173, 174, 126, 50, 173, 174, - /* 860 */ 128, 27, 160, 68, 69, 70, 71, 72, 73, 74, - /* 870 */ 75, 76, 77, 78, 79, 80, 150, 82, 83, 84, - /* 880 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 150, - /* 890 */ 23, 165, 150, 23, 216, 25, 194, 32, 39, 173, - /* 900 */ 174, 135, 150, 137, 165, 150, 41, 165, 150, 52, - /* 910 */ 238, 104, 173, 174, 29, 173, 174, 165, 49, 50, - /* 920 */ 165, 219, 238, 165, 238, 173, 174, 52, 173, 174, - /* 930 */ 22, 173, 174, 23, 23, 160, 25, 68, 69, 70, - /* 940 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - /* 950 */ 150, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 960 */ 91, 92, 19, 150, 150, 165, 150, 245, 246, 194, - /* 970 */ 150, 144, 145, 173, 174, 160, 150, 22, 165, 165, - /* 980 */ 22, 165, 150, 150, 116, 165, 173, 174, 52, 173, - /* 990 */ 174, 165, 49, 50, 22, 150, 128, 165, 165, 173, - /* 1000 */ 174, 187, 166, 166, 22, 173, 174, 187, 109, 194, - /* 1010 */ 165, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 1020 */ 77, 78, 79, 80, 150, 82, 83, 84, 85, 86, - /* 1030 */ 87, 88, 89, 90, 91, 92, 19, 150, 193, 165, - /* 1040 */ 102, 205, 205, 150, 150, 247, 248, 173, 174, 19, - /* 1050 */ 150, 20, 165, 150, 150, 150, 150, 150, 165, 165, - /* 1060 */ 173, 174, 49, 50, 104, 165, 49, 50, 165, 165, - /* 1070 */ 165, 165, 165, 173, 174, 43, 173, 174, 173, 174, - /* 1080 */ 187, 24, 190, 191, 71, 72, 69, 70, 71, 72, - /* 1090 */ 73, 74, 75, 76, 77, 78, 79, 80, 150, 82, - /* 1100 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - /* 1110 */ 19, 98, 150, 165, 150, 150, 150, 150, 150, 150, - /* 1120 */ 59, 173, 174, 25, 150, 190, 191, 165, 53, 165, - /* 1130 */ 165, 165, 165, 165, 165, 173, 174, 173, 174, 165, - /* 1140 */ 49, 50, 91, 92, 1, 2, 53, 173, 174, 138, - /* 1150 */ 104, 22, 5, 1, 35, 118, 127, 150, 193, 193, - /* 1160 */ 193, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 1170 */ 79, 80, 165, 82, 83, 84, 85, 86, 87, 88, - /* 1180 */ 89, 90, 91, 92, 19, 20, 150, 22, 150, 27, - /* 1190 */ 150, 26, 27, 108, 150, 22, 76, 76, 150, 25, - /* 1200 */ 193, 165, 37, 165, 150, 165, 22, 19, 20, 165, - /* 1210 */ 22, 173, 174, 165, 26, 27, 23, 150, 119, 165, - /* 1220 */ 150, 56, 150, 150, 150, 37, 16, 173, 174, 193, - /* 1230 */ 150, 66, 165, 193, 1, 165, 121, 165, 165, 165, - /* 1240 */ 20, 146, 147, 119, 56, 165, 150, 152, 16, 154, - /* 1250 */ 150, 86, 87, 88, 66, 160, 150, 150, 93, 94, - /* 1260 */ 95, 165, 150, 98, 108, 165, 127, 23, 65, 173, - /* 1270 */ 174, 165, 165, 150, 86, 87, 128, 165, 150, 173, - /* 1280 */ 174, 93, 94, 95, 23, 150, 98, 15, 165, 194, - /* 1290 */ 150, 140, 22, 165, 129, 130, 131, 132, 133, 134, - /* 1300 */ 165, 173, 174, 3, 116, 165, 19, 20, 150, 22, - /* 1310 */ 4, 150, 217, 26, 27, 179, 179, 129, 130, 131, - /* 1320 */ 132, 133, 134, 165, 37, 150, 165, 150, 164, 19, - /* 1330 */ 20, 150, 22, 246, 149, 249, 26, 27, 249, 244, - /* 1340 */ 165, 150, 165, 56, 6, 150, 165, 37, 173, 174, - /* 1350 */ 173, 174, 150, 66, 173, 174, 165, 149, 149, 13, - /* 1360 */ 165, 25, 150, 150, 150, 149, 56, 165, 150, 116, - /* 1370 */ 151, 150, 150, 86, 87, 150, 66, 165, 165, 165, - /* 1380 */ 93, 94, 95, 165, 150, 98, 165, 165, 151, 22, - /* 1390 */ 165, 194, 150, 26, 27, 150, 86, 87, 159, 165, - /* 1400 */ 199, 126, 123, 93, 94, 95, 200, 165, 98, 124, - /* 1410 */ 165, 122, 201, 125, 225, 135, 129, 130, 131, 132, - /* 1420 */ 133, 134, 5, 157, 157, 202, 118, 10, 11, 12, - /* 1430 */ 13, 14, 203, 66, 17, 104, 210, 121, 211, 129, - /* 1440 */ 130, 131, 132, 133, 134, 210, 175, 211, 31, 211, - /* 1450 */ 33, 210, 104, 86, 87, 47, 175, 183, 175, 42, - /* 1460 */ 103, 94, 178, 177, 22, 98, 175, 92, 228, 175, - /* 1470 */ 175, 228, 55, 183, 57, 178, 175, 156, 61, 18, - /* 1480 */ 157, 64, 156, 235, 157, 156, 45, 157, 236, 157, - /* 1490 */ 135, 156, 199, 189, 157, 68, 129, 130, 131, 22, - /* 1500 */ 189, 199, 157, 156, 192, 18, 192, 192, 199, 192, - /* 1510 */ 218, 189, 40, 157, 218, 157, 240, 240, 157, 38, - /* 1520 */ 196, 243, 105, 106, 107, 153, 198, 209, 111, 166, - /* 1530 */ 176, 181, 166, 116, 166, 230, 176, 230, 176, 226, - /* 1540 */ 199, 177, 239, 209, 185, 148, 166, 195, 209, 196, - /* 1550 */ 199, 208, 182, 233, 173, 182, 139, 186, 239, 234, - /* 1560 */ 191, 182, 173, 92, + /* 0 */ 19, 22, 22, 23, 1, 24, 26, 15, 27, 80, + /* 10 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 20 */ 91, 92, 93, 94, 95, 108, 109, 110, 27, 28, + /* 30 */ 23, 50, 51, 80, 81, 82, 83, 122, 85, 86, + /* 40 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 22, + /* 50 */ 70, 23, 71, 72, 73, 74, 75, 76, 77, 78, + /* 60 */ 79, 80, 81, 82, 83, 122, 85, 86, 87, 88, + /* 70 */ 89, 90, 91, 92, 93, 94, 95, 19, 97, 91, + /* 80 */ 92, 93, 94, 95, 26, 85, 86, 87, 88, 89, + /* 90 */ 90, 91, 92, 93, 94, 95, 27, 28, 97, 98, + /* 100 */ 99, 122, 211, 102, 103, 104, 79, 19, 50, 51, + /* 110 */ 19, 122, 59, 55, 113, 224, 225, 226, 89, 90, + /* 120 */ 91, 92, 93, 94, 95, 23, 27, 28, 26, 71, + /* 130 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + /* 140 */ 82, 83, 51, 85, 86, 87, 88, 89, 90, 91, + /* 150 */ 92, 93, 94, 95, 19, 132, 133, 58, 89, 90, + /* 160 */ 21, 108, 109, 110, 27, 28, 97, 98, 33, 100, + /* 170 */ 7, 8, 119, 120, 22, 19, 107, 42, 109, 27, + /* 180 */ 28, 27, 28, 95, 28, 50, 51, 99, 100, 101, + /* 190 */ 102, 103, 104, 105, 27, 28, 97, 98, 107, 152, + /* 200 */ 112, 132, 133, 112, 65, 69, 71, 72, 73, 74, + /* 210 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 11, + /* 220 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + /* 230 */ 95, 19, 101, 97, 97, 98, 24, 101, 122, 157, + /* 240 */ 12, 99, 103, 112, 102, 103, 104, 152, 22, 97, + /* 250 */ 98, 97, 98, 27, 28, 113, 27, 29, 91, 164, + /* 260 */ 165, 124, 50, 51, 97, 98, 219, 59, 132, 133, + /* 270 */ 134, 22, 23, 45, 66, 47, 212, 213, 124, 140, + /* 280 */ 132, 133, 19, 71, 72, 73, 74, 75, 76, 77, + /* 290 */ 78, 79, 80, 81, 82, 83, 152, 85, 86, 87, + /* 300 */ 88, 89, 90, 91, 92, 93, 94, 95, 164, 165, + /* 310 */ 27, 28, 230, 50, 51, 233, 108, 109, 110, 70, + /* 320 */ 16, 59, 23, 97, 98, 26, 97, 22, 66, 185, + /* 330 */ 12, 187, 27, 28, 71, 72, 73, 74, 75, 76, + /* 340 */ 77, 78, 79, 80, 81, 82, 83, 29, 85, 86, + /* 350 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 19, + /* 360 */ 22, 148, 149, 45, 23, 47, 62, 154, 64, 156, + /* 370 */ 108, 109, 110, 37, 69, 23, 163, 59, 26, 26, + /* 380 */ 97, 98, 144, 145, 146, 147, 152, 200, 52, 23, + /* 390 */ 50, 51, 26, 22, 89, 90, 60, 210, 7, 8, + /* 400 */ 9, 138, 97, 22, 23, 26, 101, 26, 174, 175, + /* 410 */ 197, 71, 72, 73, 74, 75, 76, 77, 78, 79, + /* 420 */ 80, 81, 82, 83, 16, 85, 86, 87, 88, 89, + /* 430 */ 90, 91, 92, 93, 94, 95, 19, 132, 133, 134, + /* 440 */ 23, 152, 208, 209, 140, 152, 152, 111, 195, 196, + /* 450 */ 98, 70, 163, 160, 152, 23, 22, 164, 165, 246, + /* 460 */ 207, 27, 152, 174, 175, 171, 172, 50, 51, 137, + /* 470 */ 62, 139, 64, 171, 172, 222, 124, 27, 138, 24, + /* 480 */ 163, 89, 90, 130, 174, 175, 197, 163, 71, 72, + /* 490 */ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + /* 500 */ 83, 22, 85, 86, 87, 88, 89, 90, 91, 92, + /* 510 */ 93, 94, 95, 19, 197, 181, 182, 23, 208, 209, + /* 520 */ 152, 197, 26, 189, 132, 133, 232, 224, 225, 226, + /* 530 */ 152, 97, 91, 26, 232, 116, 212, 213, 152, 222, + /* 540 */ 121, 152, 174, 175, 50, 51, 243, 97, 22, 23, + /* 550 */ 22, 234, 174, 175, 177, 23, 239, 116, 163, 177, + /* 560 */ 174, 175, 121, 174, 175, 71, 72, 73, 74, 75, + /* 570 */ 76, 77, 78, 79, 80, 81, 82, 83, 24, 85, + /* 580 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* 590 */ 19, 23, 197, 11, 23, 227, 70, 208, 220, 152, + /* 600 */ 31, 224, 225, 226, 35, 98, 224, 225, 226, 108, + /* 610 */ 109, 110, 115, 152, 117, 118, 27, 222, 49, 123, + /* 620 */ 24, 50, 51, 27, 0, 1, 2, 224, 225, 226, + /* 630 */ 166, 124, 168, 169, 239, 174, 175, 170, 171, 172, + /* 640 */ 22, 194, 71, 72, 73, 74, 75, 76, 77, 78, + /* 650 */ 79, 80, 81, 82, 83, 152, 85, 86, 87, 88, + /* 660 */ 89, 90, 91, 92, 93, 94, 95, 19, 22, 208, + /* 670 */ 24, 23, 195, 196, 170, 171, 172, 174, 175, 152, + /* 680 */ 26, 152, 152, 152, 207, 152, 97, 152, 23, 152, + /* 690 */ 51, 244, 152, 97, 152, 247, 248, 23, 50, 51, + /* 700 */ 26, 174, 175, 174, 175, 174, 175, 174, 175, 174, + /* 710 */ 175, 174, 175, 23, 174, 175, 174, 175, 188, 71, + /* 720 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + /* 730 */ 82, 83, 152, 85, 86, 87, 88, 89, 90, 91, + /* 740 */ 92, 93, 94, 95, 19, 152, 107, 152, 33, 24, + /* 750 */ 152, 100, 101, 27, 174, 175, 152, 42, 152, 23, + /* 760 */ 152, 26, 152, 23, 152, 26, 152, 174, 175, 174, + /* 770 */ 175, 152, 174, 175, 23, 50, 51, 26, 174, 175, + /* 780 */ 174, 175, 174, 175, 174, 175, 174, 175, 174, 175, + /* 790 */ 163, 119, 120, 174, 175, 19, 71, 72, 73, 74, + /* 800 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 152, + /* 810 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + /* 820 */ 95, 66, 152, 97, 197, 23, 50, 51, 26, 53, + /* 830 */ 23, 174, 175, 26, 23, 23, 23, 26, 26, 26, + /* 840 */ 36, 106, 146, 147, 174, 175, 19, 71, 72, 73, + /* 850 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + /* 860 */ 152, 85, 86, 87, 88, 89, 90, 91, 92, 93, + /* 870 */ 94, 95, 152, 196, 119, 120, 19, 50, 51, 168, + /* 880 */ 169, 26, 174, 175, 207, 28, 152, 249, 250, 152, + /* 890 */ 163, 163, 163, 163, 174, 175, 163, 19, 71, 72, + /* 900 */ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + /* 910 */ 83, 152, 85, 86, 87, 88, 89, 90, 91, 92, + /* 920 */ 93, 94, 95, 152, 197, 197, 197, 197, 50, 51, + /* 930 */ 197, 194, 36, 174, 175, 191, 192, 152, 191, 192, + /* 940 */ 163, 152, 66, 124, 152, 174, 175, 152, 19, 71, + /* 950 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + /* 960 */ 82, 83, 152, 85, 86, 87, 88, 89, 90, 91, + /* 970 */ 92, 93, 94, 95, 197, 152, 100, 188, 152, 50, + /* 980 */ 51, 152, 152, 188, 174, 175, 252, 152, 94, 95, + /* 990 */ 152, 152, 152, 1, 2, 152, 152, 174, 175, 19, + /* 1000 */ 152, 72, 73, 74, 75, 76, 77, 78, 79, 80, + /* 1010 */ 81, 82, 83, 152, 85, 86, 87, 88, 89, 90, + /* 1020 */ 91, 92, 93, 94, 95, 152, 188, 188, 22, 194, + /* 1030 */ 50, 51, 240, 173, 194, 174, 175, 252, 194, 152, + /* 1040 */ 36, 181, 28, 152, 23, 219, 122, 174, 175, 219, + /* 1050 */ 221, 152, 152, 73, 74, 75, 76, 77, 78, 79, + /* 1060 */ 80, 81, 82, 83, 152, 85, 86, 87, 88, 89, + /* 1070 */ 90, 91, 92, 93, 94, 95, 19, 20, 152, 22, + /* 1080 */ 23, 194, 152, 240, 27, 28, 174, 175, 240, 19, + /* 1090 */ 20, 26, 22, 194, 194, 38, 22, 27, 28, 152, + /* 1100 */ 23, 22, 152, 116, 174, 175, 152, 23, 38, 152, + /* 1110 */ 23, 152, 221, 152, 57, 152, 23, 163, 50, 51, + /* 1120 */ 194, 174, 175, 66, 174, 175, 69, 57, 174, 175, + /* 1130 */ 40, 174, 175, 174, 175, 174, 175, 174, 175, 69, + /* 1140 */ 22, 53, 74, 75, 30, 53, 89, 90, 22, 22, + /* 1150 */ 152, 197, 23, 96, 97, 98, 22, 152, 101, 89, + /* 1160 */ 90, 91, 208, 209, 152, 53, 96, 97, 98, 101, + /* 1170 */ 22, 101, 174, 175, 152, 19, 20, 105, 22, 174, + /* 1180 */ 175, 112, 19, 27, 28, 20, 174, 175, 24, 132, + /* 1190 */ 133, 134, 135, 136, 38, 44, 174, 175, 107, 61, + /* 1200 */ 54, 26, 132, 133, 134, 135, 136, 54, 107, 22, + /* 1210 */ 5, 140, 1, 57, 36, 111, 122, 28, 79, 79, + /* 1220 */ 131, 123, 66, 19, 20, 69, 22, 1, 16, 20, + /* 1230 */ 125, 27, 28, 123, 111, 120, 23, 131, 23, 16, + /* 1240 */ 68, 142, 38, 15, 22, 89, 90, 3, 167, 4, + /* 1250 */ 248, 251, 96, 97, 98, 180, 180, 101, 251, 151, + /* 1260 */ 6, 57, 151, 13, 151, 26, 25, 151, 161, 202, + /* 1270 */ 153, 162, 153, 69, 130, 128, 203, 19, 20, 127, + /* 1280 */ 22, 126, 204, 129, 22, 27, 28, 205, 132, 133, + /* 1290 */ 134, 135, 136, 89, 90, 231, 38, 95, 137, 179, + /* 1300 */ 96, 97, 98, 206, 179, 101, 122, 107, 159, 159, + /* 1310 */ 125, 231, 216, 228, 107, 57, 184, 217, 216, 176, + /* 1320 */ 217, 176, 48, 106, 18, 184, 158, 69, 159, 158, + /* 1330 */ 46, 71, 237, 176, 176, 176, 132, 133, 134, 135, + /* 1340 */ 136, 217, 176, 137, 216, 178, 158, 89, 90, 179, + /* 1350 */ 176, 159, 179, 159, 96, 97, 98, 159, 159, 101, + /* 1360 */ 5, 158, 202, 22, 18, 10, 11, 12, 13, 14, + /* 1370 */ 190, 238, 17, 190, 158, 193, 41, 159, 202, 193, + /* 1380 */ 159, 202, 245, 193, 193, 223, 190, 32, 159, 34, + /* 1390 */ 132, 133, 134, 135, 136, 159, 39, 155, 43, 150, + /* 1400 */ 223, 177, 201, 178, 177, 186, 66, 199, 177, 152, + /* 1410 */ 253, 56, 215, 152, 182, 152, 202, 152, 63, 152, + /* 1420 */ 152, 66, 67, 242, 229, 152, 174, 152, 152, 152, + /* 1430 */ 152, 152, 152, 152, 199, 242, 202, 152, 198, 152, + /* 1440 */ 152, 152, 183, 192, 152, 215, 152, 183, 215, 183, + /* 1450 */ 152, 241, 214, 152, 211, 152, 152, 211, 211, 152, + /* 1460 */ 152, 241, 152, 152, 152, 152, 152, 152, 152, 114, + /* 1470 */ 152, 152, 235, 152, 152, 152, 174, 187, 95, 174, + /* 1480 */ 253, 253, 253, 253, 236, 253, 253, 253, 253, 253, + /* 1490 */ 253, 253, 253, 253, 253, 253, 141, }; -#define YY_SHIFT_USE_DFLT (-70) -#define YY_SHIFT_COUNT (417) -#define YY_SHIFT_MIN (-69) -#define YY_SHIFT_MAX (1487) +#define YY_SHIFT_USE_DFLT (-86) +#define YY_SHIFT_COUNT (429) +#define YY_SHIFT_MIN (-85) +#define YY_SHIFT_MAX (1383) static const short yy_shift_ofst[] = { - /* 0 */ 1143, 1188, 1417, 1188, 1287, 1287, 138, 138, -2, -19, - /* 10 */ 1287, 1287, 1287, 1287, 347, 362, 129, 129, 795, 1165, - /* 20 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - /* 30 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - /* 40 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1310, 1287, - /* 50 */ 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - /* 60 */ 1287, 1287, 286, 362, 362, 538, 538, 231, 1253, 55, - /* 70 */ 721, 647, 573, 499, 425, 351, 277, 203, 869, 869, - /* 80 */ 869, 869, 869, 869, 869, 869, 869, 869, 869, 869, - /* 90 */ 869, 869, 869, 943, 869, 1017, 1091, 1091, -69, -45, - /* 100 */ -45, -45, -45, -45, -1, 24, 245, 362, 362, 362, - /* 110 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 120 */ 362, 362, 362, 388, 356, 362, 362, 362, 362, 362, - /* 130 */ 732, 868, 231, 1051, 1471, -70, -70, -70, 1367, 57, - /* 140 */ 434, 434, 289, 291, 285, 1, 204, 572, 539, 362, - /* 150 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 160 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 170 */ 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, - /* 180 */ 362, 506, 506, 506, 705, 1253, 1253, 1253, -70, -70, - /* 190 */ -70, 171, 171, 160, 502, 502, 502, 446, 432, 511, - /* 200 */ 422, 358, 335, -12, -12, -12, -12, 576, 294, -12, - /* 210 */ -12, 295, 595, 141, 600, 730, 723, 723, 805, 730, - /* 220 */ 805, 439, 911, 231, 865, 231, 865, 807, 865, 723, - /* 230 */ 766, 633, 633, 231, 284, 63, 608, 1481, 1308, 1308, - /* 240 */ 1472, 1472, 1308, 1477, 1427, 1275, 1487, 1487, 1487, 1487, - /* 250 */ 1308, 1461, 1275, 1477, 1427, 1427, 1275, 1308, 1461, 1355, - /* 260 */ 1441, 1308, 1308, 1461, 1308, 1461, 1308, 1461, 1442, 1348, - /* 270 */ 1348, 1348, 1408, 1375, 1375, 1442, 1348, 1357, 1348, 1408, - /* 280 */ 1348, 1348, 1316, 1331, 1316, 1331, 1316, 1331, 1308, 1308, - /* 290 */ 1280, 1288, 1289, 1285, 1279, 1275, 1253, 1336, 1346, 1346, - /* 300 */ 1338, 1338, 1338, 1338, -70, -70, -70, -70, -70, -70, - /* 310 */ 1013, 467, 612, 84, 179, -28, 870, 410, 761, 760, - /* 320 */ 667, 650, 531, 220, 361, 331, 125, 127, 97, 1306, - /* 330 */ 1300, 1270, 1151, 1272, 1203, 1232, 1261, 1244, 1148, 1174, - /* 340 */ 1139, 1156, 1124, 1220, 1115, 1210, 1233, 1099, 1193, 1184, - /* 350 */ 1174, 1173, 1029, 1121, 1120, 1085, 1162, 1119, 1037, 1152, - /* 360 */ 1147, 1129, 1046, 1011, 1093, 1098, 1075, 1061, 1032, 960, - /* 370 */ 1057, 1031, 1030, 899, 938, 982, 936, 972, 958, 910, - /* 380 */ 955, 875, 885, 908, 857, 859, 867, 804, 590, 834, - /* 390 */ 747, 818, 513, 611, 741, 673, 637, 611, 606, 603, - /* 400 */ 579, 501, 541, 468, 386, 445, 395, 376, 281, 185, - /* 410 */ 120, 92, 75, 45, 114, 25, 11, 5, + /* 0 */ 992, 1057, 1355, 1156, 1204, 1204, 1, 262, -19, 135, + /* 10 */ 135, 776, 1204, 1204, 1204, 1204, 69, 69, 53, 208, + /* 20 */ 283, 755, 58, 725, 648, 571, 494, 417, 340, 263, + /* 30 */ 212, 827, 827, 827, 827, 827, 827, 827, 827, 827, + /* 40 */ 827, 827, 827, 827, 827, 827, 878, 827, 929, 980, + /* 50 */ 980, 1070, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, + /* 60 */ 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, + /* 70 */ 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, + /* 80 */ 1258, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, + /* 90 */ 1204, 1204, 1204, 1204, -71, -47, -47, -47, -47, -47, + /* 100 */ 0, 29, -12, 283, 283, 139, 91, 392, 392, 894, + /* 110 */ 672, 726, 1383, -86, -86, -86, 88, 318, 318, 99, + /* 120 */ 381, -20, 283, 283, 283, 283, 283, 283, 283, 283, + /* 130 */ 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + /* 140 */ 283, 283, 283, 283, 624, 876, 726, 672, 1340, 1340, + /* 150 */ 1340, 1340, 1340, 1340, -86, -86, -86, 305, 136, 136, + /* 160 */ 142, 167, 226, 154, 137, 152, 283, 283, 283, 283, + /* 170 */ 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + /* 180 */ 283, 283, 283, 336, 336, 336, 283, 283, 352, 283, + /* 190 */ 283, 283, 283, 283, 228, 283, 283, 283, 283, 283, + /* 200 */ 283, 283, 283, 283, 283, 501, 569, 596, 596, 596, + /* 210 */ 507, 497, 441, 391, 353, 156, 156, 857, 353, 857, + /* 220 */ 735, 813, 639, 715, 156, 332, 715, 715, 496, 419, + /* 230 */ 646, 1357, 1184, 1184, 1335, 1335, 1184, 1341, 1260, 1144, + /* 240 */ 1346, 1346, 1346, 1346, 1184, 1306, 1144, 1341, 1260, 1260, + /* 250 */ 1144, 1184, 1306, 1206, 1284, 1184, 1184, 1306, 1184, 1306, + /* 260 */ 1184, 1306, 1262, 1207, 1207, 1207, 1274, 1262, 1207, 1217, + /* 270 */ 1207, 1274, 1207, 1207, 1185, 1200, 1185, 1200, 1185, 1200, + /* 280 */ 1184, 1184, 1161, 1262, 1202, 1202, 1262, 1154, 1155, 1147, + /* 290 */ 1152, 1144, 1241, 1239, 1250, 1250, 1254, 1254, 1254, 1254, + /* 300 */ -86, -86, -86, -86, -86, -86, 1068, 304, 526, 249, + /* 310 */ 408, -83, 434, 812, 27, 811, 807, 802, 751, 589, + /* 320 */ 651, 163, 131, 674, 366, 450, 299, 148, 23, 102, + /* 330 */ 229, -21, 1245, 1244, 1222, 1099, 1228, 1172, 1223, 1215, + /* 340 */ 1213, 1115, 1106, 1123, 1110, 1209, 1105, 1212, 1226, 1098, + /* 350 */ 1089, 1140, 1139, 1104, 1189, 1178, 1094, 1211, 1205, 1187, + /* 360 */ 1101, 1071, 1153, 1175, 1146, 1138, 1151, 1091, 1164, 1165, + /* 370 */ 1163, 1069, 1072, 1148, 1112, 1134, 1127, 1129, 1126, 1092, + /* 380 */ 1114, 1118, 1088, 1090, 1093, 1087, 1084, 987, 1079, 1077, + /* 390 */ 1074, 1065, 924, 1021, 1014, 1004, 1006, 819, 739, 896, + /* 400 */ 855, 804, 739, 740, 736, 690, 654, 665, 618, 582, + /* 410 */ 568, 528, 554, 379, 532, 479, 455, 379, 432, 371, + /* 420 */ 341, 28, 338, 116, -11, -57, -85, 7, -8, 3, }; -#define YY_REDUCE_USE_DFLT (-169) -#define YY_REDUCE_COUNT (309) -#define YY_REDUCE_MIN (-168) -#define YY_REDUCE_MAX (1397) +#define YY_REDUCE_USE_DFLT (-110) +#define YY_REDUCE_COUNT (305) +#define YY_REDUCE_MIN (-109) +#define YY_REDUCE_MAX (1323) static const short yy_reduce_ofst[] = { - /* 0 */ -141, 90, 1095, 222, 158, 156, 19, 17, 10, -104, - /* 10 */ 378, 316, 311, 12, 180, 249, 598, 464, 397, 1181, - /* 20 */ 1177, 1175, 1128, 1106, 1096, 1054, 1038, 974, 964, 962, - /* 30 */ 948, 905, 903, 900, 887, 874, 832, 826, 816, 813, - /* 40 */ 800, 758, 755, 752, 742, 739, 726, 685, 681, 668, - /* 50 */ 665, 652, 607, 604, 594, 591, 578, 530, 528, 526, - /* 60 */ 385, 18, 477, 466, 519, 444, 350, 435, 405, 488, - /* 70 */ 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - /* 80 */ 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - /* 90 */ 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - /* 100 */ 488, 488, 488, 488, 488, 488, 488, 1040, 678, 1036, - /* 110 */ 1007, 967, 966, 965, 845, 686, 610, 684, 317, 672, - /* 120 */ 893, 327, 623, 522, -7, 820, 814, 157, 154, 101, - /* 130 */ 702, 494, 580, 488, 488, 488, 488, 488, 614, 586, - /* 140 */ 935, 892, 968, 1245, 1242, 1234, 1225, 798, 798, 1222, - /* 150 */ 1221, 1218, 1214, 1213, 1212, 1202, 1195, 1191, 1161, 1158, - /* 160 */ 1140, 1135, 1123, 1112, 1107, 1100, 1080, 1074, 1073, 1072, - /* 170 */ 1070, 1067, 1048, 1044, 969, 968, 907, 906, 904, 894, - /* 180 */ 833, 837, 836, 340, 827, 815, 775, 68, 722, 646, - /* 190 */ -168, 1389, 1381, 1371, 1379, 1373, 1370, 1343, 1352, 1369, - /* 200 */ 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1325, 1320, 1352, - /* 210 */ 1352, 1343, 1380, 1353, 1397, 1351, 1339, 1334, 1319, 1341, - /* 220 */ 1303, 1364, 1359, 1368, 1362, 1366, 1360, 1350, 1354, 1318, - /* 230 */ 1313, 1307, 1305, 1363, 1328, 1324, 1372, 1278, 1361, 1358, - /* 240 */ 1277, 1276, 1356, 1296, 1322, 1309, 1317, 1315, 1314, 1312, - /* 250 */ 1345, 1347, 1302, 1292, 1311, 1304, 1293, 1337, 1335, 1252, - /* 260 */ 1248, 1332, 1330, 1329, 1327, 1326, 1323, 1321, 1297, 1301, - /* 270 */ 1295, 1294, 1290, 1243, 1240, 1284, 1291, 1286, 1283, 1274, - /* 280 */ 1281, 1271, 1238, 1241, 1236, 1235, 1227, 1226, 1267, 1266, - /* 290 */ 1189, 1229, 1223, 1211, 1206, 1201, 1197, 1239, 1237, 1219, - /* 300 */ 1216, 1209, 1208, 1185, 1089, 1086, 1087, 1137, 1136, 1164, + /* 0 */ 238, 954, 213, 289, 310, 234, 144, 317, -109, 382, + /* 10 */ 377, 303, 461, 389, 378, 368, 302, 294, 253, 395, + /* 20 */ 293, 324, 403, 403, 403, 403, 403, 403, 403, 403, + /* 30 */ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, + /* 40 */ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, + /* 50 */ 403, 1022, 1012, 1005, 998, 963, 961, 959, 957, 950, + /* 60 */ 947, 930, 912, 873, 861, 823, 810, 771, 759, 720, + /* 70 */ 708, 670, 657, 619, 614, 612, 610, 608, 606, 604, + /* 80 */ 598, 595, 593, 580, 542, 540, 537, 535, 533, 531, + /* 90 */ 529, 527, 503, 386, 403, 403, 403, 403, 403, 403, + /* 100 */ 403, 403, 403, 95, 447, 82, 334, 504, 467, 403, + /* 110 */ 477, 464, 403, 403, 403, 403, 860, 747, 744, 785, + /* 120 */ 638, 638, 926, 891, 900, 899, 887, 844, 840, 835, + /* 130 */ 848, 830, 843, 829, 792, 839, 826, 737, 838, 795, + /* 140 */ 789, 47, 734, 530, 696, 777, 711, 677, 733, 730, + /* 150 */ 729, 728, 727, 627, 448, 64, 187, 1305, 1302, 1252, + /* 160 */ 1290, 1273, 1323, 1322, 1321, 1319, 1318, 1316, 1315, 1314, + /* 170 */ 1313, 1312, 1311, 1310, 1308, 1307, 1304, 1303, 1301, 1298, + /* 180 */ 1294, 1292, 1289, 1266, 1264, 1259, 1288, 1287, 1238, 1285, + /* 190 */ 1281, 1280, 1279, 1278, 1251, 1277, 1276, 1275, 1273, 1268, + /* 200 */ 1267, 1265, 1263, 1261, 1257, 1248, 1237, 1247, 1246, 1243, + /* 210 */ 1238, 1240, 1235, 1249, 1234, 1233, 1230, 1220, 1214, 1210, + /* 220 */ 1225, 1219, 1232, 1231, 1197, 1195, 1227, 1224, 1201, 1208, + /* 230 */ 1242, 1137, 1236, 1229, 1193, 1181, 1221, 1177, 1196, 1179, + /* 240 */ 1191, 1190, 1186, 1182, 1218, 1216, 1176, 1162, 1183, 1180, + /* 250 */ 1160, 1199, 1203, 1133, 1095, 1198, 1194, 1188, 1192, 1171, + /* 260 */ 1169, 1168, 1173, 1174, 1166, 1159, 1141, 1170, 1158, 1167, + /* 270 */ 1157, 1132, 1145, 1143, 1124, 1128, 1103, 1102, 1100, 1096, + /* 280 */ 1150, 1149, 1085, 1125, 1080, 1064, 1120, 1097, 1082, 1078, + /* 290 */ 1073, 1067, 1109, 1107, 1119, 1117, 1116, 1113, 1111, 1108, + /* 300 */ 1007, 1000, 1002, 1076, 1075, 1081, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 633, 867, 955, 955, 867, 867, 955, 955, 955, 757, - /* 10 */ 955, 955, 955, 865, 955, 955, 785, 785, 929, 955, - /* 20 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 30 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 40 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 50 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 60 */ 955, 955, 955, 955, 955, 955, 955, 672, 761, 791, - /* 70 */ 955, 955, 955, 955, 955, 955, 955, 955, 928, 930, - /* 80 */ 799, 798, 908, 772, 796, 789, 793, 868, 861, 862, - /* 90 */ 860, 864, 869, 955, 792, 828, 845, 827, 839, 844, - /* 100 */ 851, 843, 840, 830, 829, 831, 832, 955, 955, 955, - /* 110 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 120 */ 955, 955, 955, 659, 726, 955, 955, 955, 955, 955, - /* 130 */ 955, 955, 955, 833, 834, 848, 847, 846, 955, 664, - /* 140 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 150 */ 935, 933, 955, 880, 955, 955, 955, 955, 955, 955, - /* 160 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 170 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 180 */ 639, 757, 757, 757, 633, 955, 955, 955, 947, 761, - /* 190 */ 751, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 200 */ 955, 955, 955, 801, 740, 918, 920, 955, 901, 738, - /* 210 */ 661, 759, 674, 749, 641, 795, 774, 774, 913, 795, - /* 220 */ 913, 697, 720, 955, 785, 955, 785, 694, 785, 774, - /* 230 */ 863, 955, 955, 955, 758, 749, 955, 940, 765, 765, - /* 240 */ 932, 932, 765, 807, 730, 795, 737, 737, 737, 737, - /* 250 */ 765, 656, 795, 807, 730, 730, 795, 765, 656, 907, - /* 260 */ 905, 765, 765, 656, 765, 656, 765, 656, 873, 728, - /* 270 */ 728, 728, 712, 877, 877, 873, 728, 697, 728, 712, - /* 280 */ 728, 728, 778, 773, 778, 773, 778, 773, 765, 765, - /* 290 */ 955, 790, 779, 788, 786, 795, 955, 715, 649, 649, - /* 300 */ 638, 638, 638, 638, 952, 952, 947, 699, 699, 682, - /* 310 */ 955, 955, 955, 955, 955, 955, 955, 882, 955, 955, - /* 320 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 330 */ 634, 942, 955, 955, 939, 955, 955, 955, 955, 800, - /* 340 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 350 */ 917, 955, 955, 955, 955, 955, 955, 955, 911, 955, - /* 360 */ 955, 955, 955, 955, 955, 904, 903, 955, 955, 955, - /* 370 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 380 */ 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, - /* 390 */ 955, 955, 955, 787, 955, 780, 955, 866, 955, 955, - /* 400 */ 955, 955, 955, 955, 955, 955, 955, 955, 743, 816, - /* 410 */ 955, 815, 819, 814, 666, 955, 647, 955, 630, 635, - /* 420 */ 951, 954, 953, 950, 949, 948, 943, 941, 938, 937, - /* 430 */ 936, 934, 931, 927, 886, 884, 891, 890, 889, 888, - /* 440 */ 887, 885, 883, 881, 802, 797, 794, 926, 879, 739, - /* 450 */ 736, 735, 655, 944, 910, 919, 806, 805, 808, 916, - /* 460 */ 915, 914, 912, 909, 896, 804, 803, 731, 871, 870, - /* 470 */ 658, 900, 899, 898, 902, 906, 897, 767, 657, 654, - /* 480 */ 663, 718, 719, 727, 725, 724, 723, 722, 721, 717, - /* 490 */ 665, 673, 711, 696, 695, 876, 878, 875, 874, 704, - /* 500 */ 703, 709, 708, 707, 706, 705, 702, 701, 700, 693, - /* 510 */ 692, 698, 691, 714, 713, 710, 690, 734, 733, 732, - /* 520 */ 729, 689, 688, 687, 819, 686, 685, 825, 824, 812, - /* 530 */ 855, 754, 753, 752, 764, 763, 776, 775, 810, 809, - /* 540 */ 777, 762, 756, 755, 771, 770, 769, 768, 760, 750, - /* 550 */ 782, 784, 783, 781, 857, 766, 854, 925, 924, 923, - /* 560 */ 922, 921, 859, 858, 826, 823, 677, 678, 894, 893, - /* 570 */ 895, 892, 680, 679, 676, 675, 856, 745, 744, 852, - /* 580 */ 849, 841, 837, 853, 850, 842, 838, 836, 835, 821, - /* 590 */ 820, 818, 817, 813, 822, 668, 746, 742, 741, 811, - /* 600 */ 748, 747, 684, 683, 681, 662, 660, 653, 651, 650, - /* 610 */ 652, 648, 646, 645, 644, 643, 642, 671, 670, 669, - /* 620 */ 667, 666, 640, 637, 636, 632, 631, 629, + /* 0 */ 647, 964, 964, 964, 878, 878, 969, 964, 774, 802, + /* 10 */ 802, 938, 969, 969, 969, 876, 969, 969, 969, 964, + /* 20 */ 969, 778, 808, 969, 969, 969, 969, 969, 969, 969, + /* 30 */ 969, 937, 939, 816, 815, 918, 789, 813, 806, 810, + /* 40 */ 879, 872, 873, 871, 875, 880, 969, 809, 841, 856, + /* 50 */ 840, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 60 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 70 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 80 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 90 */ 969, 969, 969, 969, 850, 855, 862, 854, 851, 843, + /* 100 */ 842, 844, 845, 969, 969, 673, 739, 969, 969, 846, + /* 110 */ 969, 685, 847, 859, 858, 857, 680, 969, 969, 969, + /* 120 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 130 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 140 */ 969, 969, 969, 969, 647, 964, 969, 969, 964, 964, + /* 150 */ 964, 964, 964, 964, 956, 778, 768, 969, 969, 969, + /* 160 */ 969, 969, 969, 969, 969, 969, 969, 944, 942, 969, + /* 170 */ 891, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 180 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 190 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 200 */ 969, 969, 969, 969, 653, 969, 911, 774, 774, 774, + /* 210 */ 776, 754, 766, 655, 812, 791, 791, 923, 812, 923, + /* 220 */ 710, 733, 707, 802, 791, 874, 802, 802, 775, 766, + /* 230 */ 969, 949, 782, 782, 941, 941, 782, 821, 743, 812, + /* 240 */ 750, 750, 750, 750, 782, 670, 812, 821, 743, 743, + /* 250 */ 812, 782, 670, 917, 915, 782, 782, 670, 782, 670, + /* 260 */ 782, 670, 884, 741, 741, 741, 725, 884, 741, 710, + /* 270 */ 741, 725, 741, 741, 795, 790, 795, 790, 795, 790, + /* 280 */ 782, 782, 969, 884, 888, 888, 884, 807, 796, 805, + /* 290 */ 803, 812, 676, 728, 663, 663, 652, 652, 652, 652, + /* 300 */ 961, 961, 956, 712, 712, 695, 969, 969, 969, 969, + /* 310 */ 969, 969, 687, 969, 893, 969, 969, 969, 969, 969, + /* 320 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 330 */ 969, 828, 969, 648, 951, 969, 969, 948, 969, 969, + /* 340 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 350 */ 969, 969, 969, 969, 969, 969, 921, 969, 969, 969, + /* 360 */ 969, 969, 969, 914, 913, 969, 969, 969, 969, 969, + /* 370 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + /* 380 */ 969, 969, 969, 969, 969, 969, 969, 757, 969, 969, + /* 390 */ 969, 761, 969, 969, 969, 969, 969, 969, 804, 969, + /* 400 */ 797, 969, 877, 969, 969, 969, 969, 969, 969, 969, + /* 410 */ 969, 969, 969, 966, 969, 969, 969, 965, 969, 969, + /* 420 */ 969, 969, 969, 830, 969, 829, 833, 969, 661, 969, + /* 430 */ 644, 649, 960, 963, 962, 959, 958, 957, 952, 950, + /* 440 */ 947, 946, 945, 943, 940, 936, 897, 895, 902, 901, + /* 450 */ 900, 899, 898, 896, 894, 892, 818, 817, 814, 811, + /* 460 */ 753, 935, 890, 752, 749, 748, 669, 953, 920, 929, + /* 470 */ 928, 927, 822, 926, 925, 924, 922, 919, 906, 820, + /* 480 */ 819, 744, 882, 881, 672, 910, 909, 908, 912, 916, + /* 490 */ 907, 784, 751, 671, 668, 675, 679, 731, 732, 740, + /* 500 */ 738, 737, 736, 735, 734, 730, 681, 686, 724, 709, + /* 510 */ 708, 717, 716, 722, 721, 720, 719, 718, 715, 714, + /* 520 */ 713, 706, 705, 711, 704, 727, 726, 723, 703, 747, + /* 530 */ 746, 745, 742, 702, 701, 700, 833, 699, 698, 838, + /* 540 */ 837, 866, 826, 755, 759, 758, 762, 763, 771, 770, + /* 550 */ 769, 780, 781, 793, 792, 824, 823, 794, 779, 773, + /* 560 */ 772, 788, 787, 786, 785, 777, 767, 799, 798, 868, + /* 570 */ 783, 867, 865, 934, 933, 932, 931, 930, 870, 967, + /* 580 */ 968, 887, 889, 886, 801, 800, 885, 869, 839, 836, + /* 590 */ 690, 691, 905, 904, 903, 693, 692, 689, 688, 863, + /* 600 */ 860, 852, 864, 861, 853, 849, 848, 834, 832, 831, + /* 610 */ 827, 835, 760, 756, 825, 765, 764, 697, 696, 694, + /* 620 */ 678, 677, 674, 667, 665, 664, 666, 662, 660, 659, + /* 630 */ 658, 657, 656, 684, 683, 682, 654, 651, 650, 646, + /* 640 */ 645, 643, }; /* The next table maps tokens into fallback tokens. If a construct @@ -113579,71 +115504,74 @@ static const YYACTIONTYPE yy_default[] = { static const YYCODETYPE yyFallback[] = { 0, /* $ => nothing */ 0, /* SEMI => nothing */ - 26, /* EXPLAIN => ID */ - 26, /* QUERY => ID */ - 26, /* PLAN => ID */ - 26, /* BEGIN => ID */ + 27, /* EXPLAIN => ID */ + 27, /* QUERY => ID */ + 27, /* PLAN => ID */ + 27, /* BEGIN => ID */ 0, /* TRANSACTION => nothing */ - 26, /* DEFERRED => ID */ - 26, /* IMMEDIATE => ID */ - 26, /* EXCLUSIVE => ID */ + 27, /* DEFERRED => ID */ + 27, /* IMMEDIATE => ID */ + 27, /* EXCLUSIVE => ID */ 0, /* COMMIT => nothing */ - 26, /* END => ID */ - 26, /* ROLLBACK => ID */ - 26, /* SAVEPOINT => ID */ - 26, /* RELEASE => ID */ + 27, /* END => ID */ + 27, /* ROLLBACK => ID */ + 27, /* SAVEPOINT => ID */ + 27, /* RELEASE => ID */ 0, /* TO => nothing */ 0, /* TABLE => nothing */ 0, /* CREATE => nothing */ - 26, /* IF => ID */ + 27, /* IF => ID */ 0, /* NOT => nothing */ 0, /* EXISTS => nothing */ - 26, /* TEMP => ID */ + 27, /* TEMP => ID */ 0, /* LP => nothing */ 0, /* RP => nothing */ 0, /* AS => nothing */ + 27, /* WITHOUT => ID */ 0, /* COMMA => nothing */ 0, /* ID => nothing */ 0, /* INDEXED => nothing */ - 26, /* ABORT => ID */ - 26, /* ACTION => ID */ - 26, /* AFTER => ID */ - 26, /* ANALYZE => ID */ - 26, /* ASC => ID */ - 26, /* ATTACH => ID */ - 26, /* BEFORE => ID */ - 26, /* BY => ID */ - 26, /* CASCADE => ID */ - 26, /* CAST => ID */ - 26, /* COLUMNKW => ID */ - 26, /* CONFLICT => ID */ - 26, /* DATABASE => ID */ - 26, /* DESC => ID */ - 26, /* DETACH => ID */ - 26, /* EACH => ID */ - 26, /* FAIL => ID */ - 26, /* FOR => ID */ - 26, /* IGNORE => ID */ - 26, /* INITIALLY => ID */ - 26, /* INSTEAD => ID */ - 26, /* LIKE_KW => ID */ - 26, /* MATCH => ID */ - 26, /* NO => ID */ - 26, /* KEY => ID */ - 26, /* OF => ID */ - 26, /* OFFSET => ID */ - 26, /* PRAGMA => ID */ - 26, /* RAISE => ID */ - 26, /* REPLACE => ID */ - 26, /* RESTRICT => ID */ - 26, /* ROW => ID */ - 26, /* TRIGGER => ID */ - 26, /* VACUUM => ID */ - 26, /* VIEW => ID */ - 26, /* VIRTUAL => ID */ - 26, /* REINDEX => ID */ - 26, /* RENAME => ID */ - 26, /* CTIME_KW => ID */ + 27, /* ABORT => ID */ + 27, /* ACTION => ID */ + 27, /* AFTER => ID */ + 27, /* ANALYZE => ID */ + 27, /* ASC => ID */ + 27, /* ATTACH => ID */ + 27, /* BEFORE => ID */ + 27, /* BY => ID */ + 27, /* CASCADE => ID */ + 27, /* CAST => ID */ + 27, /* COLUMNKW => ID */ + 27, /* CONFLICT => ID */ + 27, /* DATABASE => ID */ + 27, /* DESC => ID */ + 27, /* DETACH => ID */ + 27, /* EACH => ID */ + 27, /* FAIL => ID */ + 27, /* FOR => ID */ + 27, /* IGNORE => ID */ + 27, /* INITIALLY => ID */ + 27, /* INSTEAD => ID */ + 27, /* LIKE_KW => ID */ + 27, /* MATCH => ID */ + 27, /* NO => ID */ + 27, /* KEY => ID */ + 27, /* OF => ID */ + 27, /* OFFSET => ID */ + 27, /* PRAGMA => ID */ + 27, /* RAISE => ID */ + 27, /* RECURSIVE => ID */ + 27, /* REPLACE => ID */ + 27, /* RESTRICT => ID */ + 27, /* ROW => ID */ + 27, /* TRIGGER => ID */ + 27, /* VACUUM => ID */ + 27, /* VIEW => ID */ + 27, /* VIRTUAL => ID */ + 27, /* WITH => ID */ + 27, /* REINDEX => ID */ + 27, /* RENAME => ID */ + 27, /* CTIME_KW => ID */ }; #endif /* YYFALLBACK */ @@ -113728,63 +115656,64 @@ static const char *const yyTokenName[] = { "ROLLBACK", "SAVEPOINT", "RELEASE", "TO", "TABLE", "CREATE", "IF", "NOT", "EXISTS", "TEMP", "LP", "RP", - "AS", "COMMA", "ID", "INDEXED", - "ABORT", "ACTION", "AFTER", "ANALYZE", - "ASC", "ATTACH", "BEFORE", "BY", - "CASCADE", "CAST", "COLUMNKW", "CONFLICT", - "DATABASE", "DESC", "DETACH", "EACH", - "FAIL", "FOR", "IGNORE", "INITIALLY", - "INSTEAD", "LIKE_KW", "MATCH", "NO", - "KEY", "OF", "OFFSET", "PRAGMA", - "RAISE", "REPLACE", "RESTRICT", "ROW", - "TRIGGER", "VACUUM", "VIEW", "VIRTUAL", - "REINDEX", "RENAME", "CTIME_KW", "ANY", - "OR", "AND", "IS", "BETWEEN", - "IN", "ISNULL", "NOTNULL", "NE", - "EQ", "GT", "LE", "LT", - "GE", "ESCAPE", "BITAND", "BITOR", - "LSHIFT", "RSHIFT", "PLUS", "MINUS", - "STAR", "SLASH", "REM", "CONCAT", - "COLLATE", "BITNOT", "STRING", "JOIN_KW", - "CONSTRAINT", "DEFAULT", "NULL", "PRIMARY", - "UNIQUE", "CHECK", "REFERENCES", "AUTOINCR", - "ON", "INSERT", "DELETE", "UPDATE", - "SET", "DEFERRABLE", "FOREIGN", "DROP", - "UNION", "ALL", "EXCEPT", "INTERSECT", - "SELECT", "DISTINCT", "DOT", "FROM", + "AS", "WITHOUT", "COMMA", "ID", + "INDEXED", "ABORT", "ACTION", "AFTER", + "ANALYZE", "ASC", "ATTACH", "BEFORE", + "BY", "CASCADE", "CAST", "COLUMNKW", + "CONFLICT", "DATABASE", "DESC", "DETACH", + "EACH", "FAIL", "FOR", "IGNORE", + "INITIALLY", "INSTEAD", "LIKE_KW", "MATCH", + "NO", "KEY", "OF", "OFFSET", + "PRAGMA", "RAISE", "RECURSIVE", "REPLACE", + "RESTRICT", "ROW", "TRIGGER", "VACUUM", + "VIEW", "VIRTUAL", "WITH", "REINDEX", + "RENAME", "CTIME_KW", "ANY", "OR", + "AND", "IS", "BETWEEN", "IN", + "ISNULL", "NOTNULL", "NE", "EQ", + "GT", "LE", "LT", "GE", + "ESCAPE", "BITAND", "BITOR", "LSHIFT", + "RSHIFT", "PLUS", "MINUS", "STAR", + "SLASH", "REM", "CONCAT", "COLLATE", + "BITNOT", "STRING", "JOIN_KW", "CONSTRAINT", + "DEFAULT", "NULL", "PRIMARY", "UNIQUE", + "CHECK", "REFERENCES", "AUTOINCR", "ON", + "INSERT", "DELETE", "UPDATE", "SET", + "DEFERRABLE", "FOREIGN", "DROP", "UNION", + "ALL", "EXCEPT", "INTERSECT", "SELECT", + "VALUES", "DISTINCT", "DOT", "FROM", "JOIN", "USING", "ORDER", "GROUP", "HAVING", "LIMIT", "WHERE", "INTO", - "VALUES", "INTEGER", "FLOAT", "BLOB", - "REGISTER", "VARIABLE", "CASE", "WHEN", - "THEN", "ELSE", "INDEX", "ALTER", - "ADD", "error", "input", "cmdlist", - "ecmd", "explain", "cmdx", "cmd", - "transtype", "trans_opt", "nm", "savepoint_opt", - "create_table", "create_table_args", "createkw", "temp", - "ifnotexists", "dbnm", "columnlist", "conslist_opt", - "select", "column", "columnid", "type", - "carglist", "id", "ids", "typetoken", - "typename", "signed", "plus_num", "minus_num", - "ccons", "term", "expr", "onconf", - "sortorder", "autoinc", "idxlist_opt", "refargs", - "defer_subclause", "refarg", "refact", "init_deferred_pred_opt", - "conslist", "tconscomma", "tcons", "idxlist", - "defer_subclause_opt", "orconf", "resolvetype", "raisetype", - "ifexists", "fullname", "oneselect", "multiselect_op", - "distinct", "selcollist", "from", "where_opt", - "groupby_opt", "having_opt", "orderby_opt", "limit_opt", - "sclp", "as", "seltablist", "stl_prefix", - "joinop", "indexed_opt", "on_opt", "using_opt", - "joinop2", "inscollist", "sortlist", "nexprlist", - "setlist", "insert_cmd", "inscollist_opt", "valuelist", - "exprlist", "likeop", "between_op", "in_op", - "case_operand", "case_exprlist", "case_else", "uniqueflag", - "collate", "nmnum", "number", "trigger_decl", - "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause", - "when_clause", "trigger_cmd", "trnm", "tridxby", - "database_kw_opt", "key_opt", "add_column_fullname", "kwcolumn_opt", - "create_vtab", "vtabarglist", "vtabarg", "vtabargtoken", - "lp", "anylist", + "INTEGER", "FLOAT", "BLOB", "VARIABLE", + "CASE", "WHEN", "THEN", "ELSE", + "INDEX", "ALTER", "ADD", "error", + "input", "cmdlist", "ecmd", "explain", + "cmdx", "cmd", "transtype", "trans_opt", + "nm", "savepoint_opt", "create_table", "create_table_args", + "createkw", "temp", "ifnotexists", "dbnm", + "columnlist", "conslist_opt", "table_options", "select", + "column", "columnid", "type", "carglist", + "typetoken", "typename", "signed", "plus_num", + "minus_num", "ccons", "term", "expr", + "onconf", "sortorder", "autoinc", "idxlist_opt", + "refargs", "defer_subclause", "refarg", "refact", + "init_deferred_pred_opt", "conslist", "tconscomma", "tcons", + "idxlist", "defer_subclause_opt", "orconf", "resolvetype", + "raisetype", "ifexists", "fullname", "selectnowith", + "oneselect", "with", "multiselect_op", "distinct", + "selcollist", "from", "where_opt", "groupby_opt", + "having_opt", "orderby_opt", "limit_opt", "values", + "nexprlist", "exprlist", "sclp", "as", + "seltablist", "stl_prefix", "joinop", "indexed_opt", + "on_opt", "using_opt", "joinop2", "idlist", + "sortlist", "setlist", "insert_cmd", "inscollist_opt", + "likeop", "between_op", "in_op", "case_operand", + "case_exprlist", "case_else", "uniqueflag", "collate", + "nmnum", "trigger_decl", "trigger_cmd_list", "trigger_time", + "trigger_event", "foreach_clause", "when_clause", "trigger_cmd", + "trnm", "tridxby", "database_kw_opt", "key_opt", + "add_column_fullname", "kwcolumn_opt", "create_vtab", "vtabarglist", + "vtabarg", "vtabargtoken", "lp", "anylist", + "wqlist", }; #endif /* NDEBUG */ @@ -113824,301 +115753,301 @@ static const char *const yyRuleName[] = { /* 29 */ "ifnotexists ::= IF NOT EXISTS", /* 30 */ "temp ::= TEMP", /* 31 */ "temp ::=", - /* 32 */ "create_table_args ::= LP columnlist conslist_opt RP", + /* 32 */ "create_table_args ::= LP columnlist conslist_opt RP table_options", /* 33 */ "create_table_args ::= AS select", - /* 34 */ "columnlist ::= columnlist COMMA column", - /* 35 */ "columnlist ::= column", - /* 36 */ "column ::= columnid type carglist", - /* 37 */ "columnid ::= nm", - /* 38 */ "id ::= ID", - /* 39 */ "id ::= INDEXED", - /* 40 */ "ids ::= ID|STRING", - /* 41 */ "nm ::= id", - /* 42 */ "nm ::= STRING", - /* 43 */ "nm ::= JOIN_KW", - /* 44 */ "type ::=", - /* 45 */ "type ::= typetoken", - /* 46 */ "typetoken ::= typename", - /* 47 */ "typetoken ::= typename LP signed RP", - /* 48 */ "typetoken ::= typename LP signed COMMA signed RP", - /* 49 */ "typename ::= ids", - /* 50 */ "typename ::= typename ids", - /* 51 */ "signed ::= plus_num", - /* 52 */ "signed ::= minus_num", - /* 53 */ "carglist ::= carglist ccons", - /* 54 */ "carglist ::=", - /* 55 */ "ccons ::= CONSTRAINT nm", - /* 56 */ "ccons ::= DEFAULT term", - /* 57 */ "ccons ::= DEFAULT LP expr RP", - /* 58 */ "ccons ::= DEFAULT PLUS term", - /* 59 */ "ccons ::= DEFAULT MINUS term", - /* 60 */ "ccons ::= DEFAULT id", - /* 61 */ "ccons ::= NULL onconf", - /* 62 */ "ccons ::= NOT NULL onconf", - /* 63 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", - /* 64 */ "ccons ::= UNIQUE onconf", - /* 65 */ "ccons ::= CHECK LP expr RP", - /* 66 */ "ccons ::= REFERENCES nm idxlist_opt refargs", - /* 67 */ "ccons ::= defer_subclause", - /* 68 */ "ccons ::= COLLATE ids", - /* 69 */ "autoinc ::=", - /* 70 */ "autoinc ::= AUTOINCR", - /* 71 */ "refargs ::=", - /* 72 */ "refargs ::= refargs refarg", - /* 73 */ "refarg ::= MATCH nm", - /* 74 */ "refarg ::= ON INSERT refact", - /* 75 */ "refarg ::= ON DELETE refact", - /* 76 */ "refarg ::= ON UPDATE refact", - /* 77 */ "refact ::= SET NULL", - /* 78 */ "refact ::= SET DEFAULT", - /* 79 */ "refact ::= CASCADE", - /* 80 */ "refact ::= RESTRICT", - /* 81 */ "refact ::= NO ACTION", - /* 82 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", - /* 83 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", - /* 84 */ "init_deferred_pred_opt ::=", - /* 85 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", - /* 86 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", - /* 87 */ "conslist_opt ::=", - /* 88 */ "conslist_opt ::= COMMA conslist", - /* 89 */ "conslist ::= conslist tconscomma tcons", - /* 90 */ "conslist ::= tcons", - /* 91 */ "tconscomma ::= COMMA", - /* 92 */ "tconscomma ::=", - /* 93 */ "tcons ::= CONSTRAINT nm", - /* 94 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf", - /* 95 */ "tcons ::= UNIQUE LP idxlist RP onconf", - /* 96 */ "tcons ::= CHECK LP expr RP onconf", - /* 97 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", - /* 98 */ "defer_subclause_opt ::=", - /* 99 */ "defer_subclause_opt ::= defer_subclause", - /* 100 */ "onconf ::=", - /* 101 */ "onconf ::= ON CONFLICT resolvetype", - /* 102 */ "orconf ::=", - /* 103 */ "orconf ::= OR resolvetype", - /* 104 */ "resolvetype ::= raisetype", - /* 105 */ "resolvetype ::= IGNORE", - /* 106 */ "resolvetype ::= REPLACE", - /* 107 */ "cmd ::= DROP TABLE ifexists fullname", - /* 108 */ "ifexists ::= IF EXISTS", - /* 109 */ "ifexists ::=", - /* 110 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select", - /* 111 */ "cmd ::= DROP VIEW ifexists fullname", - /* 112 */ "cmd ::= select", - /* 113 */ "select ::= oneselect", - /* 114 */ "select ::= select multiselect_op oneselect", + /* 34 */ "table_options ::=", + /* 35 */ "table_options ::= WITHOUT nm", + /* 36 */ "columnlist ::= columnlist COMMA column", + /* 37 */ "columnlist ::= column", + /* 38 */ "column ::= columnid type carglist", + /* 39 */ "columnid ::= nm", + /* 40 */ "nm ::= ID|INDEXED", + /* 41 */ "nm ::= STRING", + /* 42 */ "nm ::= JOIN_KW", + /* 43 */ "type ::=", + /* 44 */ "type ::= typetoken", + /* 45 */ "typetoken ::= typename", + /* 46 */ "typetoken ::= typename LP signed RP", + /* 47 */ "typetoken ::= typename LP signed COMMA signed RP", + /* 48 */ "typename ::= ID|STRING", + /* 49 */ "typename ::= typename ID|STRING", + /* 50 */ "signed ::= plus_num", + /* 51 */ "signed ::= minus_num", + /* 52 */ "carglist ::= carglist ccons", + /* 53 */ "carglist ::=", + /* 54 */ "ccons ::= CONSTRAINT nm", + /* 55 */ "ccons ::= DEFAULT term", + /* 56 */ "ccons ::= DEFAULT LP expr RP", + /* 57 */ "ccons ::= DEFAULT PLUS term", + /* 58 */ "ccons ::= DEFAULT MINUS term", + /* 59 */ "ccons ::= DEFAULT ID|INDEXED", + /* 60 */ "ccons ::= NULL onconf", + /* 61 */ "ccons ::= NOT NULL onconf", + /* 62 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc", + /* 63 */ "ccons ::= UNIQUE onconf", + /* 64 */ "ccons ::= CHECK LP expr RP", + /* 65 */ "ccons ::= REFERENCES nm idxlist_opt refargs", + /* 66 */ "ccons ::= defer_subclause", + /* 67 */ "ccons ::= COLLATE ID|STRING", + /* 68 */ "autoinc ::=", + /* 69 */ "autoinc ::= AUTOINCR", + /* 70 */ "refargs ::=", + /* 71 */ "refargs ::= refargs refarg", + /* 72 */ "refarg ::= MATCH nm", + /* 73 */ "refarg ::= ON INSERT refact", + /* 74 */ "refarg ::= ON DELETE refact", + /* 75 */ "refarg ::= ON UPDATE refact", + /* 76 */ "refact ::= SET NULL", + /* 77 */ "refact ::= SET DEFAULT", + /* 78 */ "refact ::= CASCADE", + /* 79 */ "refact ::= RESTRICT", + /* 80 */ "refact ::= NO ACTION", + /* 81 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", + /* 82 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", + /* 83 */ "init_deferred_pred_opt ::=", + /* 84 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", + /* 85 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", + /* 86 */ "conslist_opt ::=", + /* 87 */ "conslist_opt ::= COMMA conslist", + /* 88 */ "conslist ::= conslist tconscomma tcons", + /* 89 */ "conslist ::= tcons", + /* 90 */ "tconscomma ::= COMMA", + /* 91 */ "tconscomma ::=", + /* 92 */ "tcons ::= CONSTRAINT nm", + /* 93 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf", + /* 94 */ "tcons ::= UNIQUE LP idxlist RP onconf", + /* 95 */ "tcons ::= CHECK LP expr RP onconf", + /* 96 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", + /* 97 */ "defer_subclause_opt ::=", + /* 98 */ "defer_subclause_opt ::= defer_subclause", + /* 99 */ "onconf ::=", + /* 100 */ "onconf ::= ON CONFLICT resolvetype", + /* 101 */ "orconf ::=", + /* 102 */ "orconf ::= OR resolvetype", + /* 103 */ "resolvetype ::= raisetype", + /* 104 */ "resolvetype ::= IGNORE", + /* 105 */ "resolvetype ::= REPLACE", + /* 106 */ "cmd ::= DROP TABLE ifexists fullname", + /* 107 */ "ifexists ::= IF EXISTS", + /* 108 */ "ifexists ::=", + /* 109 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select", + /* 110 */ "cmd ::= DROP VIEW ifexists fullname", + /* 111 */ "cmd ::= select", + /* 112 */ "select ::= with selectnowith", + /* 113 */ "selectnowith ::= oneselect", + /* 114 */ "selectnowith ::= selectnowith multiselect_op oneselect", /* 115 */ "multiselect_op ::= UNION", /* 116 */ "multiselect_op ::= UNION ALL", /* 117 */ "multiselect_op ::= EXCEPT|INTERSECT", /* 118 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 119 */ "distinct ::= DISTINCT", - /* 120 */ "distinct ::= ALL", - /* 121 */ "distinct ::=", - /* 122 */ "sclp ::= selcollist COMMA", - /* 123 */ "sclp ::=", - /* 124 */ "selcollist ::= sclp expr as", - /* 125 */ "selcollist ::= sclp STAR", - /* 126 */ "selcollist ::= sclp nm DOT STAR", - /* 127 */ "as ::= AS nm", - /* 128 */ "as ::= ids", - /* 129 */ "as ::=", - /* 130 */ "from ::=", - /* 131 */ "from ::= FROM seltablist", - /* 132 */ "stl_prefix ::= seltablist joinop", - /* 133 */ "stl_prefix ::=", - /* 134 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", - /* 135 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", - /* 136 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", - /* 137 */ "dbnm ::=", - /* 138 */ "dbnm ::= DOT nm", - /* 139 */ "fullname ::= nm dbnm", - /* 140 */ "joinop ::= COMMA|JOIN", - /* 141 */ "joinop ::= JOIN_KW JOIN", - /* 142 */ "joinop ::= JOIN_KW nm JOIN", - /* 143 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 144 */ "on_opt ::= ON expr", - /* 145 */ "on_opt ::=", - /* 146 */ "indexed_opt ::=", - /* 147 */ "indexed_opt ::= INDEXED BY nm", - /* 148 */ "indexed_opt ::= NOT INDEXED", - /* 149 */ "using_opt ::= USING LP inscollist RP", - /* 150 */ "using_opt ::=", - /* 151 */ "orderby_opt ::=", - /* 152 */ "orderby_opt ::= ORDER BY sortlist", - /* 153 */ "sortlist ::= sortlist COMMA expr sortorder", - /* 154 */ "sortlist ::= expr sortorder", - /* 155 */ "sortorder ::= ASC", - /* 156 */ "sortorder ::= DESC", - /* 157 */ "sortorder ::=", - /* 158 */ "groupby_opt ::=", - /* 159 */ "groupby_opt ::= GROUP BY nexprlist", - /* 160 */ "having_opt ::=", - /* 161 */ "having_opt ::= HAVING expr", - /* 162 */ "limit_opt ::=", - /* 163 */ "limit_opt ::= LIMIT expr", - /* 164 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 165 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 166 */ "cmd ::= DELETE FROM fullname indexed_opt where_opt", - /* 167 */ "where_opt ::=", - /* 168 */ "where_opt ::= WHERE expr", - /* 169 */ "cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt", - /* 170 */ "setlist ::= setlist COMMA nm EQ expr", - /* 171 */ "setlist ::= nm EQ expr", - /* 172 */ "cmd ::= insert_cmd INTO fullname inscollist_opt valuelist", - /* 173 */ "cmd ::= insert_cmd INTO fullname inscollist_opt select", - /* 174 */ "cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES", - /* 175 */ "insert_cmd ::= INSERT orconf", - /* 176 */ "insert_cmd ::= REPLACE", - /* 177 */ "valuelist ::= VALUES LP nexprlist RP", - /* 178 */ "valuelist ::= valuelist COMMA LP exprlist RP", + /* 119 */ "oneselect ::= values", + /* 120 */ "values ::= VALUES LP nexprlist RP", + /* 121 */ "values ::= values COMMA LP exprlist RP", + /* 122 */ "distinct ::= DISTINCT", + /* 123 */ "distinct ::= ALL", + /* 124 */ "distinct ::=", + /* 125 */ "sclp ::= selcollist COMMA", + /* 126 */ "sclp ::=", + /* 127 */ "selcollist ::= sclp expr as", + /* 128 */ "selcollist ::= sclp STAR", + /* 129 */ "selcollist ::= sclp nm DOT STAR", + /* 130 */ "as ::= AS nm", + /* 131 */ "as ::= ID|STRING", + /* 132 */ "as ::=", + /* 133 */ "from ::=", + /* 134 */ "from ::= FROM seltablist", + /* 135 */ "stl_prefix ::= seltablist joinop", + /* 136 */ "stl_prefix ::=", + /* 137 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", + /* 138 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", + /* 139 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", + /* 140 */ "dbnm ::=", + /* 141 */ "dbnm ::= DOT nm", + /* 142 */ "fullname ::= nm dbnm", + /* 143 */ "joinop ::= COMMA|JOIN", + /* 144 */ "joinop ::= JOIN_KW JOIN", + /* 145 */ "joinop ::= JOIN_KW nm JOIN", + /* 146 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 147 */ "on_opt ::= ON expr", + /* 148 */ "on_opt ::=", + /* 149 */ "indexed_opt ::=", + /* 150 */ "indexed_opt ::= INDEXED BY nm", + /* 151 */ "indexed_opt ::= NOT INDEXED", + /* 152 */ "using_opt ::= USING LP idlist RP", + /* 153 */ "using_opt ::=", + /* 154 */ "orderby_opt ::=", + /* 155 */ "orderby_opt ::= ORDER BY sortlist", + /* 156 */ "sortlist ::= sortlist COMMA expr sortorder", + /* 157 */ "sortlist ::= expr sortorder", + /* 158 */ "sortorder ::= ASC", + /* 159 */ "sortorder ::= DESC", + /* 160 */ "sortorder ::=", + /* 161 */ "groupby_opt ::=", + /* 162 */ "groupby_opt ::= GROUP BY nexprlist", + /* 163 */ "having_opt ::=", + /* 164 */ "having_opt ::= HAVING expr", + /* 165 */ "limit_opt ::=", + /* 166 */ "limit_opt ::= LIMIT expr", + /* 167 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 168 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 169 */ "cmd ::= with DELETE FROM fullname indexed_opt where_opt", + /* 170 */ "where_opt ::=", + /* 171 */ "where_opt ::= WHERE expr", + /* 172 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt", + /* 173 */ "setlist ::= setlist COMMA nm EQ expr", + /* 174 */ "setlist ::= nm EQ expr", + /* 175 */ "cmd ::= with insert_cmd INTO fullname inscollist_opt select", + /* 176 */ "cmd ::= with insert_cmd INTO fullname inscollist_opt DEFAULT VALUES", + /* 177 */ "insert_cmd ::= INSERT orconf", + /* 178 */ "insert_cmd ::= REPLACE", /* 179 */ "inscollist_opt ::=", - /* 180 */ "inscollist_opt ::= LP inscollist RP", - /* 181 */ "inscollist ::= inscollist COMMA nm", - /* 182 */ "inscollist ::= nm", + /* 180 */ "inscollist_opt ::= LP idlist RP", + /* 181 */ "idlist ::= idlist COMMA nm", + /* 182 */ "idlist ::= nm", /* 183 */ "expr ::= term", /* 184 */ "expr ::= LP expr RP", /* 185 */ "term ::= NULL", - /* 186 */ "expr ::= id", + /* 186 */ "expr ::= ID|INDEXED", /* 187 */ "expr ::= JOIN_KW", /* 188 */ "expr ::= nm DOT nm", /* 189 */ "expr ::= nm DOT nm DOT nm", /* 190 */ "term ::= INTEGER|FLOAT|BLOB", /* 191 */ "term ::= STRING", - /* 192 */ "expr ::= REGISTER", - /* 193 */ "expr ::= VARIABLE", - /* 194 */ "expr ::= expr COLLATE ids", - /* 195 */ "expr ::= CAST LP expr AS typetoken RP", - /* 196 */ "expr ::= ID LP distinct exprlist RP", - /* 197 */ "expr ::= ID LP STAR RP", - /* 198 */ "term ::= CTIME_KW", - /* 199 */ "expr ::= expr AND expr", - /* 200 */ "expr ::= expr OR expr", - /* 201 */ "expr ::= expr LT|GT|GE|LE expr", - /* 202 */ "expr ::= expr EQ|NE expr", - /* 203 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 204 */ "expr ::= expr PLUS|MINUS expr", - /* 205 */ "expr ::= expr STAR|SLASH|REM expr", - /* 206 */ "expr ::= expr CONCAT expr", - /* 207 */ "likeop ::= LIKE_KW", - /* 208 */ "likeop ::= NOT LIKE_KW", - /* 209 */ "likeop ::= MATCH", - /* 210 */ "likeop ::= NOT MATCH", - /* 211 */ "expr ::= expr likeop expr", - /* 212 */ "expr ::= expr likeop expr ESCAPE expr", - /* 213 */ "expr ::= expr ISNULL|NOTNULL", - /* 214 */ "expr ::= expr NOT NULL", - /* 215 */ "expr ::= expr IS expr", - /* 216 */ "expr ::= expr IS NOT expr", - /* 217 */ "expr ::= NOT expr", - /* 218 */ "expr ::= BITNOT expr", - /* 219 */ "expr ::= MINUS expr", - /* 220 */ "expr ::= PLUS expr", - /* 221 */ "between_op ::= BETWEEN", - /* 222 */ "between_op ::= NOT BETWEEN", - /* 223 */ "expr ::= expr between_op expr AND expr", - /* 224 */ "in_op ::= IN", - /* 225 */ "in_op ::= NOT IN", - /* 226 */ "expr ::= expr in_op LP exprlist RP", - /* 227 */ "expr ::= LP select RP", - /* 228 */ "expr ::= expr in_op LP select RP", - /* 229 */ "expr ::= expr in_op nm dbnm", - /* 230 */ "expr ::= EXISTS LP select RP", - /* 231 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 232 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 233 */ "case_exprlist ::= WHEN expr THEN expr", - /* 234 */ "case_else ::= ELSE expr", - /* 235 */ "case_else ::=", - /* 236 */ "case_operand ::= expr", - /* 237 */ "case_operand ::=", - /* 238 */ "exprlist ::= nexprlist", - /* 239 */ "exprlist ::=", - /* 240 */ "nexprlist ::= nexprlist COMMA expr", - /* 241 */ "nexprlist ::= expr", - /* 242 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt", - /* 243 */ "uniqueflag ::= UNIQUE", - /* 244 */ "uniqueflag ::=", - /* 245 */ "idxlist_opt ::=", - /* 246 */ "idxlist_opt ::= LP idxlist RP", - /* 247 */ "idxlist ::= idxlist COMMA nm collate sortorder", - /* 248 */ "idxlist ::= nm collate sortorder", - /* 249 */ "collate ::=", - /* 250 */ "collate ::= COLLATE ids", - /* 251 */ "cmd ::= DROP INDEX ifexists fullname", - /* 252 */ "cmd ::= VACUUM", - /* 253 */ "cmd ::= VACUUM nm", - /* 254 */ "cmd ::= PRAGMA nm dbnm", - /* 255 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 256 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 257 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 258 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 259 */ "nmnum ::= plus_num", - /* 260 */ "nmnum ::= nm", - /* 261 */ "nmnum ::= ON", - /* 262 */ "nmnum ::= DELETE", - /* 263 */ "nmnum ::= DEFAULT", - /* 264 */ "plus_num ::= PLUS number", - /* 265 */ "plus_num ::= number", - /* 266 */ "minus_num ::= MINUS number", - /* 267 */ "number ::= INTEGER|FLOAT", - /* 268 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 269 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 270 */ "trigger_time ::= BEFORE", - /* 271 */ "trigger_time ::= AFTER", - /* 272 */ "trigger_time ::= INSTEAD OF", - /* 273 */ "trigger_time ::=", - /* 274 */ "trigger_event ::= DELETE|INSERT", - /* 275 */ "trigger_event ::= UPDATE", - /* 276 */ "trigger_event ::= UPDATE OF inscollist", - /* 277 */ "foreach_clause ::=", - /* 278 */ "foreach_clause ::= FOR EACH ROW", - /* 279 */ "when_clause ::=", - /* 280 */ "when_clause ::= WHEN expr", - /* 281 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 282 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 283 */ "trnm ::= nm", - /* 284 */ "trnm ::= nm DOT nm", - /* 285 */ "tridxby ::=", - /* 286 */ "tridxby ::= INDEXED BY nm", - /* 287 */ "tridxby ::= NOT INDEXED", - /* 288 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt", - /* 289 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt valuelist", - /* 290 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select", - /* 291 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt", - /* 292 */ "trigger_cmd ::= select", - /* 293 */ "expr ::= RAISE LP IGNORE RP", - /* 294 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 295 */ "raisetype ::= ROLLBACK", - /* 296 */ "raisetype ::= ABORT", - /* 297 */ "raisetype ::= FAIL", - /* 298 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 299 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 300 */ "cmd ::= DETACH database_kw_opt expr", - /* 301 */ "key_opt ::=", - /* 302 */ "key_opt ::= KEY expr", - /* 303 */ "database_kw_opt ::= DATABASE", - /* 304 */ "database_kw_opt ::=", - /* 305 */ "cmd ::= REINDEX", - /* 306 */ "cmd ::= REINDEX nm dbnm", - /* 307 */ "cmd ::= ANALYZE", - /* 308 */ "cmd ::= ANALYZE nm dbnm", - /* 309 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 310 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column", - /* 311 */ "add_column_fullname ::= fullname", - /* 312 */ "kwcolumn_opt ::=", - /* 313 */ "kwcolumn_opt ::= COLUMNKW", - /* 314 */ "cmd ::= create_vtab", - /* 315 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 316 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 317 */ "vtabarglist ::= vtabarg", - /* 318 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 319 */ "vtabarg ::=", - /* 320 */ "vtabarg ::= vtabarg vtabargtoken", - /* 321 */ "vtabargtoken ::= ANY", - /* 322 */ "vtabargtoken ::= lp anylist RP", - /* 323 */ "lp ::= LP", - /* 324 */ "anylist ::=", - /* 325 */ "anylist ::= anylist LP anylist RP", - /* 326 */ "anylist ::= anylist ANY", + /* 192 */ "expr ::= VARIABLE", + /* 193 */ "expr ::= expr COLLATE ID|STRING", + /* 194 */ "expr ::= CAST LP expr AS typetoken RP", + /* 195 */ "expr ::= ID|INDEXED LP distinct exprlist RP", + /* 196 */ "expr ::= ID|INDEXED LP STAR RP", + /* 197 */ "term ::= CTIME_KW", + /* 198 */ "expr ::= expr AND expr", + /* 199 */ "expr ::= expr OR expr", + /* 200 */ "expr ::= expr LT|GT|GE|LE expr", + /* 201 */ "expr ::= expr EQ|NE expr", + /* 202 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 203 */ "expr ::= expr PLUS|MINUS expr", + /* 204 */ "expr ::= expr STAR|SLASH|REM expr", + /* 205 */ "expr ::= expr CONCAT expr", + /* 206 */ "likeop ::= LIKE_KW|MATCH", + /* 207 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 208 */ "expr ::= expr likeop expr", + /* 209 */ "expr ::= expr likeop expr ESCAPE expr", + /* 210 */ "expr ::= expr ISNULL|NOTNULL", + /* 211 */ "expr ::= expr NOT NULL", + /* 212 */ "expr ::= expr IS expr", + /* 213 */ "expr ::= expr IS NOT expr", + /* 214 */ "expr ::= NOT expr", + /* 215 */ "expr ::= BITNOT expr", + /* 216 */ "expr ::= MINUS expr", + /* 217 */ "expr ::= PLUS expr", + /* 218 */ "between_op ::= BETWEEN", + /* 219 */ "between_op ::= NOT BETWEEN", + /* 220 */ "expr ::= expr between_op expr AND expr", + /* 221 */ "in_op ::= IN", + /* 222 */ "in_op ::= NOT IN", + /* 223 */ "expr ::= expr in_op LP exprlist RP", + /* 224 */ "expr ::= LP select RP", + /* 225 */ "expr ::= expr in_op LP select RP", + /* 226 */ "expr ::= expr in_op nm dbnm", + /* 227 */ "expr ::= EXISTS LP select RP", + /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 230 */ "case_exprlist ::= WHEN expr THEN expr", + /* 231 */ "case_else ::= ELSE expr", + /* 232 */ "case_else ::=", + /* 233 */ "case_operand ::= expr", + /* 234 */ "case_operand ::=", + /* 235 */ "exprlist ::= nexprlist", + /* 236 */ "exprlist ::=", + /* 237 */ "nexprlist ::= nexprlist COMMA expr", + /* 238 */ "nexprlist ::= expr", + /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt", + /* 240 */ "uniqueflag ::= UNIQUE", + /* 241 */ "uniqueflag ::=", + /* 242 */ "idxlist_opt ::=", + /* 243 */ "idxlist_opt ::= LP idxlist RP", + /* 244 */ "idxlist ::= idxlist COMMA nm collate sortorder", + /* 245 */ "idxlist ::= nm collate sortorder", + /* 246 */ "collate ::=", + /* 247 */ "collate ::= COLLATE ID|STRING", + /* 248 */ "cmd ::= DROP INDEX ifexists fullname", + /* 249 */ "cmd ::= VACUUM", + /* 250 */ "cmd ::= VACUUM nm", + /* 251 */ "cmd ::= PRAGMA nm dbnm", + /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 256 */ "nmnum ::= plus_num", + /* 257 */ "nmnum ::= nm", + /* 258 */ "nmnum ::= ON", + /* 259 */ "nmnum ::= DELETE", + /* 260 */ "nmnum ::= DEFAULT", + /* 261 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 262 */ "plus_num ::= INTEGER|FLOAT", + /* 263 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 264 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 265 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 266 */ "trigger_time ::= BEFORE", + /* 267 */ "trigger_time ::= AFTER", + /* 268 */ "trigger_time ::= INSTEAD OF", + /* 269 */ "trigger_time ::=", + /* 270 */ "trigger_event ::= DELETE|INSERT", + /* 271 */ "trigger_event ::= UPDATE", + /* 272 */ "trigger_event ::= UPDATE OF idlist", + /* 273 */ "foreach_clause ::=", + /* 274 */ "foreach_clause ::= FOR EACH ROW", + /* 275 */ "when_clause ::=", + /* 276 */ "when_clause ::= WHEN expr", + /* 277 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 278 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 279 */ "trnm ::= nm", + /* 280 */ "trnm ::= nm DOT nm", + /* 281 */ "tridxby ::=", + /* 282 */ "tridxby ::= INDEXED BY nm", + /* 283 */ "tridxby ::= NOT INDEXED", + /* 284 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt", + /* 285 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select", + /* 286 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt", + /* 287 */ "trigger_cmd ::= select", + /* 288 */ "expr ::= RAISE LP IGNORE RP", + /* 289 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 290 */ "raisetype ::= ROLLBACK", + /* 291 */ "raisetype ::= ABORT", + /* 292 */ "raisetype ::= FAIL", + /* 293 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 294 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 295 */ "cmd ::= DETACH database_kw_opt expr", + /* 296 */ "key_opt ::=", + /* 297 */ "key_opt ::= KEY expr", + /* 298 */ "database_kw_opt ::= DATABASE", + /* 299 */ "database_kw_opt ::=", + /* 300 */ "cmd ::= REINDEX", + /* 301 */ "cmd ::= REINDEX nm dbnm", + /* 302 */ "cmd ::= ANALYZE", + /* 303 */ "cmd ::= ANALYZE nm dbnm", + /* 304 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 305 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column", + /* 306 */ "add_column_fullname ::= fullname", + /* 307 */ "kwcolumn_opt ::=", + /* 308 */ "kwcolumn_opt ::= COLUMNKW", + /* 309 */ "cmd ::= create_vtab", + /* 310 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 311 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 312 */ "vtabarglist ::= vtabarg", + /* 313 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 314 */ "vtabarg ::=", + /* 315 */ "vtabarg ::= vtabarg vtabargtoken", + /* 316 */ "vtabargtoken ::= ANY", + /* 317 */ "vtabargtoken ::= lp anylist RP", + /* 318 */ "lp ::= LP", + /* 319 */ "anylist ::=", + /* 320 */ "anylist ::= anylist LP anylist RP", + /* 321 */ "anylist ::= anylist ANY", + /* 322 */ "with ::=", + /* 323 */ "with ::= WITH wqlist", + /* 324 */ "with ::= WITH RECURSIVE wqlist", + /* 325 */ "wqlist ::= nm idxlist_opt AS LP select RP", + /* 326 */ "wqlist ::= wqlist COMMA nm idxlist_opt AS LP select RP", }; #endif /* NDEBUG */ @@ -114197,76 +116126,76 @@ static void yy_destructor( ** which appear on the RHS of the rule, but which are not used ** inside the C code. */ - case 160: /* select */ - case 194: /* oneselect */ + case 163: /* select */ + case 195: /* selectnowith */ + case 196: /* oneselect */ + case 207: /* values */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy159)); +sqlite3SelectDelete(pParse->db, (yypminor->yy3)); } break; - case 173: /* term */ - case 174: /* expr */ + case 174: /* term */ + case 175: /* expr */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy342).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy346).pExpr); } break; - case 178: /* idxlist_opt */ - case 187: /* idxlist */ - case 197: /* selcollist */ - case 200: /* groupby_opt */ - case 202: /* orderby_opt */ - case 204: /* sclp */ - case 214: /* sortlist */ - case 215: /* nexprlist */ - case 216: /* setlist */ - case 220: /* exprlist */ - case 225: /* case_exprlist */ + case 179: /* idxlist_opt */ + case 188: /* idxlist */ + case 200: /* selcollist */ + case 203: /* groupby_opt */ + case 205: /* orderby_opt */ + case 208: /* nexprlist */ + case 209: /* exprlist */ + case 210: /* sclp */ + case 220: /* sortlist */ + case 221: /* setlist */ + case 228: /* case_exprlist */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy442)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy14)); } break; - case 193: /* fullname */ - case 198: /* from */ - case 206: /* seltablist */ - case 207: /* stl_prefix */ + case 194: /* fullname */ + case 201: /* from */ + case 212: /* seltablist */ + case 213: /* stl_prefix */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy347)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy65)); } break; - case 199: /* where_opt */ - case 201: /* having_opt */ - case 210: /* on_opt */ - case 224: /* case_operand */ - case 226: /* case_else */ - case 236: /* when_clause */ - case 241: /* key_opt */ + case 197: /* with */ + case 252: /* wqlist */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy122)); +sqlite3WithDelete(pParse->db, (yypminor->yy59)); } break; - case 211: /* using_opt */ - case 213: /* inscollist */ - case 218: /* inscollist_opt */ + case 202: /* where_opt */ + case 204: /* having_opt */ + case 216: /* on_opt */ + case 227: /* case_operand */ + case 229: /* case_else */ + case 238: /* when_clause */ + case 243: /* key_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy180)); +sqlite3ExprDelete(pParse->db, (yypminor->yy132)); } break; - case 219: /* valuelist */ + case 217: /* using_opt */ + case 219: /* idlist */ + case 223: /* inscollist_opt */ { - - sqlite3ExprListDelete(pParse->db, (yypminor->yy487).pList); - sqlite3SelectDelete(pParse->db, (yypminor->yy487).pSelect); - +sqlite3IdListDelete(pParse->db, (yypminor->yy408)); } break; - case 232: /* trigger_cmd_list */ - case 237: /* trigger_cmd */ + case 234: /* trigger_cmd_list */ + case 239: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy327)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy473)); } break; - case 234: /* trigger_event */ + case 236: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy410).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy378).b); } break; default: break; /* If no destructor action specified: do nothing */ @@ -114511,333 +116440,333 @@ static const struct { YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ unsigned char nrhs; /* Number of right-hand side symbols in the rule */ } yyRuleInfo[] = { - { 142, 1 }, - { 143, 2 }, - { 143, 1 }, { 144, 1 }, - { 144, 3 }, - { 145, 0 }, + { 145, 2 }, { 145, 1 }, - { 145, 3 }, { 146, 1 }, - { 147, 3 }, - { 149, 0 }, - { 149, 1 }, - { 149, 2 }, - { 148, 0 }, - { 148, 1 }, - { 148, 1 }, - { 148, 1 }, - { 147, 2 }, - { 147, 2 }, - { 147, 2 }, - { 151, 1 }, - { 151, 0 }, - { 147, 2 }, - { 147, 3 }, - { 147, 5 }, - { 147, 2 }, - { 152, 6 }, - { 154, 1 }, - { 156, 0 }, - { 156, 3 }, - { 155, 1 }, - { 155, 0 }, - { 153, 4 }, - { 153, 2 }, - { 158, 3 }, - { 158, 1 }, - { 161, 3 }, - { 162, 1 }, - { 165, 1 }, - { 165, 1 }, - { 166, 1 }, - { 150, 1 }, - { 150, 1 }, - { 150, 1 }, - { 163, 0 }, - { 163, 1 }, - { 167, 1 }, - { 167, 4 }, - { 167, 6 }, - { 168, 1 }, - { 168, 2 }, - { 169, 1 }, - { 169, 1 }, - { 164, 2 }, - { 164, 0 }, - { 172, 2 }, - { 172, 2 }, - { 172, 4 }, - { 172, 3 }, - { 172, 3 }, - { 172, 2 }, - { 172, 2 }, - { 172, 3 }, - { 172, 5 }, - { 172, 2 }, - { 172, 4 }, - { 172, 4 }, - { 172, 1 }, - { 172, 2 }, - { 177, 0 }, - { 177, 1 }, - { 179, 0 }, - { 179, 2 }, - { 181, 2 }, - { 181, 3 }, - { 181, 3 }, - { 181, 3 }, - { 182, 2 }, - { 182, 2 }, - { 182, 1 }, - { 182, 1 }, - { 182, 2 }, - { 180, 3 }, - { 180, 2 }, - { 183, 0 }, - { 183, 2 }, - { 183, 2 }, - { 159, 0 }, - { 159, 2 }, - { 184, 3 }, - { 184, 1 }, - { 185, 1 }, - { 185, 0 }, - { 186, 2 }, - { 186, 7 }, - { 186, 5 }, - { 186, 5 }, - { 186, 10 }, - { 188, 0 }, - { 188, 1 }, - { 175, 0 }, - { 175, 3 }, - { 189, 0 }, - { 189, 2 }, - { 190, 1 }, - { 190, 1 }, - { 190, 1 }, - { 147, 4 }, - { 192, 2 }, - { 192, 0 }, - { 147, 8 }, - { 147, 4 }, + { 146, 3 }, + { 147, 0 }, { 147, 1 }, - { 160, 1 }, - { 160, 3 }, - { 195, 1 }, - { 195, 2 }, - { 195, 1 }, - { 194, 9 }, - { 196, 1 }, - { 196, 1 }, - { 196, 0 }, - { 204, 2 }, - { 204, 0 }, - { 197, 3 }, - { 197, 2 }, - { 197, 4 }, - { 205, 2 }, - { 205, 1 }, - { 205, 0 }, - { 198, 0 }, - { 198, 2 }, - { 207, 2 }, - { 207, 0 }, - { 206, 7 }, - { 206, 7 }, - { 206, 7 }, + { 147, 3 }, + { 148, 1 }, + { 149, 3 }, + { 151, 0 }, + { 151, 1 }, + { 151, 2 }, + { 150, 0 }, + { 150, 1 }, + { 150, 1 }, + { 150, 1 }, + { 149, 2 }, + { 149, 2 }, + { 149, 2 }, + { 153, 1 }, + { 153, 0 }, + { 149, 2 }, + { 149, 3 }, + { 149, 5 }, + { 149, 2 }, + { 154, 6 }, + { 156, 1 }, + { 158, 0 }, + { 158, 3 }, + { 157, 1 }, { 157, 0 }, - { 157, 2 }, + { 155, 5 }, + { 155, 2 }, + { 162, 0 }, + { 162, 2 }, + { 160, 3 }, + { 160, 1 }, + { 164, 3 }, + { 165, 1 }, + { 152, 1 }, + { 152, 1 }, + { 152, 1 }, + { 166, 0 }, + { 166, 1 }, + { 168, 1 }, + { 168, 4 }, + { 168, 6 }, + { 169, 1 }, + { 169, 2 }, + { 170, 1 }, + { 170, 1 }, + { 167, 2 }, + { 167, 0 }, + { 173, 2 }, + { 173, 2 }, + { 173, 4 }, + { 173, 3 }, + { 173, 3 }, + { 173, 2 }, + { 173, 2 }, + { 173, 3 }, + { 173, 5 }, + { 173, 2 }, + { 173, 4 }, + { 173, 4 }, + { 173, 1 }, + { 173, 2 }, + { 178, 0 }, + { 178, 1 }, + { 180, 0 }, + { 180, 2 }, + { 182, 2 }, + { 182, 3 }, + { 182, 3 }, + { 182, 3 }, + { 183, 2 }, + { 183, 2 }, + { 183, 1 }, + { 183, 1 }, + { 183, 2 }, + { 181, 3 }, + { 181, 2 }, + { 184, 0 }, + { 184, 2 }, + { 184, 2 }, + { 161, 0 }, + { 161, 2 }, + { 185, 3 }, + { 185, 1 }, + { 186, 1 }, + { 186, 0 }, + { 187, 2 }, + { 187, 7 }, + { 187, 5 }, + { 187, 5 }, + { 187, 10 }, + { 189, 0 }, + { 189, 1 }, + { 176, 0 }, + { 176, 3 }, + { 190, 0 }, + { 190, 2 }, + { 191, 1 }, + { 191, 1 }, + { 191, 1 }, + { 149, 4 }, { 193, 2 }, - { 208, 1 }, - { 208, 2 }, - { 208, 3 }, - { 208, 4 }, + { 193, 0 }, + { 149, 8 }, + { 149, 4 }, + { 149, 1 }, + { 163, 2 }, + { 195, 1 }, + { 195, 3 }, + { 198, 1 }, + { 198, 2 }, + { 198, 1 }, + { 196, 9 }, + { 196, 1 }, + { 207, 4 }, + { 207, 5 }, + { 199, 1 }, + { 199, 1 }, + { 199, 0 }, { 210, 2 }, { 210, 0 }, - { 209, 0 }, - { 209, 3 }, - { 209, 2 }, - { 211, 4 }, - { 211, 0 }, - { 202, 0 }, - { 202, 3 }, - { 214, 4 }, - { 214, 2 }, - { 176, 1 }, - { 176, 1 }, - { 176, 0 }, - { 200, 0 }, { 200, 3 }, + { 200, 2 }, + { 200, 4 }, + { 211, 2 }, + { 211, 1 }, + { 211, 0 }, { 201, 0 }, { 201, 2 }, - { 203, 0 }, - { 203, 2 }, - { 203, 4 }, - { 203, 4 }, - { 147, 5 }, - { 199, 0 }, - { 199, 2 }, - { 147, 7 }, - { 216, 5 }, - { 216, 3 }, - { 147, 5 }, - { 147, 5 }, - { 147, 6 }, - { 217, 2 }, - { 217, 1 }, - { 219, 4 }, - { 219, 5 }, - { 218, 0 }, - { 218, 3 }, - { 213, 3 }, - { 213, 1 }, - { 174, 1 }, - { 174, 3 }, - { 173, 1 }, - { 174, 1 }, - { 174, 1 }, - { 174, 3 }, - { 174, 5 }, - { 173, 1 }, - { 173, 1 }, - { 174, 1 }, - { 174, 1 }, - { 174, 3 }, - { 174, 6 }, - { 174, 5 }, - { 174, 4 }, - { 173, 1 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 174, 3 }, - { 221, 1 }, - { 221, 2 }, - { 221, 1 }, - { 221, 2 }, - { 174, 3 }, - { 174, 5 }, - { 174, 2 }, - { 174, 3 }, - { 174, 3 }, - { 174, 4 }, - { 174, 2 }, - { 174, 2 }, - { 174, 2 }, - { 174, 2 }, - { 222, 1 }, - { 222, 2 }, - { 174, 5 }, - { 223, 1 }, - { 223, 2 }, - { 174, 5 }, - { 174, 3 }, - { 174, 5 }, - { 174, 4 }, - { 174, 4 }, - { 174, 5 }, - { 225, 5 }, - { 225, 4 }, - { 226, 2 }, - { 226, 0 }, - { 224, 1 }, - { 224, 0 }, - { 220, 1 }, - { 220, 0 }, + { 213, 2 }, + { 213, 0 }, + { 212, 7 }, + { 212, 7 }, + { 212, 7 }, + { 159, 0 }, + { 159, 2 }, + { 194, 2 }, + { 214, 1 }, + { 214, 2 }, + { 214, 3 }, + { 214, 4 }, + { 216, 2 }, + { 216, 0 }, + { 215, 0 }, { 215, 3 }, - { 215, 1 }, - { 147, 12 }, + { 215, 2 }, + { 217, 4 }, + { 217, 0 }, + { 205, 0 }, + { 205, 3 }, + { 220, 4 }, + { 220, 2 }, + { 177, 1 }, + { 177, 1 }, + { 177, 0 }, + { 203, 0 }, + { 203, 3 }, + { 204, 0 }, + { 204, 2 }, + { 206, 0 }, + { 206, 2 }, + { 206, 4 }, + { 206, 4 }, + { 149, 6 }, + { 202, 0 }, + { 202, 2 }, + { 149, 8 }, + { 221, 5 }, + { 221, 3 }, + { 149, 6 }, + { 149, 7 }, + { 222, 2 }, + { 222, 1 }, + { 223, 0 }, + { 223, 3 }, + { 219, 3 }, + { 219, 1 }, + { 175, 1 }, + { 175, 3 }, + { 174, 1 }, + { 175, 1 }, + { 175, 1 }, + { 175, 3 }, + { 175, 5 }, + { 174, 1 }, + { 174, 1 }, + { 175, 1 }, + { 175, 3 }, + { 175, 6 }, + { 175, 5 }, + { 175, 4 }, + { 174, 1 }, + { 175, 3 }, + { 175, 3 }, + { 175, 3 }, + { 175, 3 }, + { 175, 3 }, + { 175, 3 }, + { 175, 3 }, + { 175, 3 }, + { 224, 1 }, + { 224, 2 }, + { 175, 3 }, + { 175, 5 }, + { 175, 2 }, + { 175, 3 }, + { 175, 3 }, + { 175, 4 }, + { 175, 2 }, + { 175, 2 }, + { 175, 2 }, + { 175, 2 }, + { 225, 1 }, + { 225, 2 }, + { 175, 5 }, + { 226, 1 }, + { 226, 2 }, + { 175, 5 }, + { 175, 3 }, + { 175, 5 }, + { 175, 4 }, + { 175, 4 }, + { 175, 5 }, + { 228, 5 }, + { 228, 4 }, + { 229, 2 }, + { 229, 0 }, { 227, 1 }, { 227, 0 }, - { 178, 0 }, - { 178, 3 }, - { 187, 5 }, - { 187, 3 }, - { 228, 0 }, - { 228, 2 }, - { 147, 4 }, - { 147, 1 }, - { 147, 2 }, - { 147, 3 }, - { 147, 5 }, - { 147, 6 }, - { 147, 5 }, - { 147, 6 }, - { 229, 1 }, - { 229, 1 }, - { 229, 1 }, - { 229, 1 }, - { 229, 1 }, - { 170, 2 }, - { 170, 1 }, - { 171, 2 }, + { 209, 1 }, + { 209, 0 }, + { 208, 3 }, + { 208, 1 }, + { 149, 12 }, { 230, 1 }, - { 147, 5 }, - { 231, 11 }, - { 233, 1 }, - { 233, 1 }, - { 233, 2 }, - { 233, 0 }, - { 234, 1 }, - { 234, 1 }, - { 234, 3 }, + { 230, 0 }, + { 179, 0 }, + { 179, 3 }, + { 188, 5 }, + { 188, 3 }, + { 231, 0 }, + { 231, 2 }, + { 149, 4 }, + { 149, 1 }, + { 149, 2 }, + { 149, 3 }, + { 149, 5 }, + { 149, 6 }, + { 149, 5 }, + { 149, 6 }, + { 232, 1 }, + { 232, 1 }, + { 232, 1 }, + { 232, 1 }, + { 232, 1 }, + { 171, 2 }, + { 171, 1 }, + { 172, 2 }, + { 149, 5 }, + { 233, 11 }, + { 235, 1 }, + { 235, 1 }, + { 235, 2 }, { 235, 0 }, - { 235, 3 }, - { 236, 0 }, - { 236, 2 }, - { 232, 3 }, - { 232, 2 }, - { 238, 1 }, - { 238, 3 }, - { 239, 0 }, - { 239, 3 }, - { 239, 2 }, - { 237, 7 }, - { 237, 5 }, - { 237, 5 }, - { 237, 5 }, - { 237, 1 }, - { 174, 4 }, - { 174, 6 }, - { 191, 1 }, - { 191, 1 }, - { 191, 1 }, - { 147, 4 }, - { 147, 6 }, - { 147, 3 }, - { 241, 0 }, - { 241, 2 }, + { 236, 1 }, + { 236, 1 }, + { 236, 3 }, + { 237, 0 }, + { 237, 3 }, + { 238, 0 }, + { 238, 2 }, + { 234, 3 }, + { 234, 2 }, { 240, 1 }, - { 240, 0 }, - { 147, 1 }, - { 147, 3 }, - { 147, 1 }, - { 147, 3 }, - { 147, 6 }, - { 147, 6 }, - { 242, 1 }, + { 240, 3 }, + { 241, 0 }, + { 241, 3 }, + { 241, 2 }, + { 239, 7 }, + { 239, 5 }, + { 239, 5 }, + { 239, 1 }, + { 175, 4 }, + { 175, 6 }, + { 192, 1 }, + { 192, 1 }, + { 192, 1 }, + { 149, 4 }, + { 149, 6 }, + { 149, 3 }, { 243, 0 }, - { 243, 1 }, - { 147, 1 }, - { 147, 4 }, - { 244, 8 }, + { 243, 2 }, + { 242, 1 }, + { 242, 0 }, + { 149, 1 }, + { 149, 3 }, + { 149, 1 }, + { 149, 3 }, + { 149, 6 }, + { 149, 6 }, + { 244, 1 }, + { 245, 0 }, { 245, 1 }, - { 245, 3 }, - { 246, 0 }, - { 246, 2 }, + { 149, 1 }, + { 149, 4 }, + { 246, 8 }, { 247, 1 }, { 247, 3 }, - { 248, 1 }, - { 249, 0 }, - { 249, 4 }, - { 249, 2 }, + { 248, 0 }, + { 248, 2 }, + { 249, 1 }, + { 249, 3 }, + { 250, 1 }, + { 251, 0 }, + { 251, 4 }, + { 251, 2 }, + { 197, 0 }, + { 197, 2 }, + { 197, 3 }, + { 252, 6 }, + { 252, 8 }, }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -114905,17 +116834,17 @@ static void yy_reduce( { sqlite3FinishCoding(pParse); } break; case 9: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy392);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy328);} break; case 13: /* transtype ::= */ -{yygotominor.yy392 = TK_DEFERRED;} +{yygotominor.yy328 = TK_DEFERRED;} break; case 14: /* transtype ::= DEFERRED */ case 15: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==15); case 16: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==16); case 115: /* multiselect_op ::= UNION */ yytestcase(yyruleno==115); case 117: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==117); -{yygotominor.yy392 = yymsp[0].major;} +{yygotominor.yy328 = yymsp[0].major;} break; case 17: /* cmd ::= COMMIT trans_opt */ case 18: /* cmd ::= END trans_opt */ yytestcase(yyruleno==18); @@ -114941,7 +116870,7 @@ static void yy_reduce( break; case 26: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy392,0,0,yymsp[-2].minor.yy392); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy328,0,0,yymsp[-2].minor.yy328); } break; case 27: /* createkw ::= CREATE */ @@ -114952,533 +116881,548 @@ static void yy_reduce( break; case 28: /* ifnotexists ::= */ case 31: /* temp ::= */ yytestcase(yyruleno==31); - case 69: /* autoinc ::= */ yytestcase(yyruleno==69); - case 82: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==82); - case 84: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==84); - case 86: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==86); - case 98: /* defer_subclause_opt ::= */ yytestcase(yyruleno==98); - case 109: /* ifexists ::= */ yytestcase(yyruleno==109); - case 221: /* between_op ::= BETWEEN */ yytestcase(yyruleno==221); - case 224: /* in_op ::= IN */ yytestcase(yyruleno==224); -{yygotominor.yy392 = 0;} + case 68: /* autoinc ::= */ yytestcase(yyruleno==68); + case 81: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==81); + case 83: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==83); + case 85: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==85); + case 97: /* defer_subclause_opt ::= */ yytestcase(yyruleno==97); + case 108: /* ifexists ::= */ yytestcase(yyruleno==108); + case 218: /* between_op ::= BETWEEN */ yytestcase(yyruleno==218); + case 221: /* in_op ::= IN */ yytestcase(yyruleno==221); +{yygotominor.yy328 = 0;} break; case 29: /* ifnotexists ::= IF NOT EXISTS */ case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30); - case 70: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==70); - case 85: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==85); - case 108: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==108); - case 222: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==222); - case 225: /* in_op ::= NOT IN */ yytestcase(yyruleno==225); -{yygotominor.yy392 = 1;} + case 69: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==69); + case 84: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==84); + case 107: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==107); + case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219); + case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222); +{yygotominor.yy328 = 1;} break; - case 32: /* create_table_args ::= LP columnlist conslist_opt RP */ + case 32: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ { - sqlite3EndTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy186,0); } break; case 33: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,yymsp[0].minor.yy159); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy159); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy3); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy3); } break; - case 36: /* column ::= columnid type carglist */ + case 34: /* table_options ::= */ +{yygotominor.yy186 = 0;} + break; + case 35: /* table_options ::= WITHOUT nm */ +{ + if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ + yygotominor.yy186 = TF_WithoutRowid; + }else{ + yygotominor.yy186 = 0; + sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); + } +} + break; + case 38: /* column ::= columnid type carglist */ { yygotominor.yy0.z = yymsp[-2].minor.yy0.z; yygotominor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-2].minor.yy0.z) + pParse->sLastToken.n; } break; - case 37: /* columnid ::= nm */ + case 39: /* columnid ::= nm */ { sqlite3AddColumn(pParse,&yymsp[0].minor.yy0); yygotominor.yy0 = yymsp[0].minor.yy0; pParse->constraintName.n = 0; } break; - case 38: /* id ::= ID */ - case 39: /* id ::= INDEXED */ yytestcase(yyruleno==39); - case 40: /* ids ::= ID|STRING */ yytestcase(yyruleno==40); - case 41: /* nm ::= id */ yytestcase(yyruleno==41); - case 42: /* nm ::= STRING */ yytestcase(yyruleno==42); - case 43: /* nm ::= JOIN_KW */ yytestcase(yyruleno==43); - case 46: /* typetoken ::= typename */ yytestcase(yyruleno==46); - case 49: /* typename ::= ids */ yytestcase(yyruleno==49); - case 127: /* as ::= AS nm */ yytestcase(yyruleno==127); - case 128: /* as ::= ids */ yytestcase(yyruleno==128); - case 138: /* dbnm ::= DOT nm */ yytestcase(yyruleno==138); - case 147: /* indexed_opt ::= INDEXED BY nm */ yytestcase(yyruleno==147); - case 250: /* collate ::= COLLATE ids */ yytestcase(yyruleno==250); - case 259: /* nmnum ::= plus_num */ yytestcase(yyruleno==259); - case 260: /* nmnum ::= nm */ yytestcase(yyruleno==260); - case 261: /* nmnum ::= ON */ yytestcase(yyruleno==261); - case 262: /* nmnum ::= DELETE */ yytestcase(yyruleno==262); - case 263: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==263); - case 264: /* plus_num ::= PLUS number */ yytestcase(yyruleno==264); - case 265: /* plus_num ::= number */ yytestcase(yyruleno==265); - case 266: /* minus_num ::= MINUS number */ yytestcase(yyruleno==266); - case 267: /* number ::= INTEGER|FLOAT */ yytestcase(yyruleno==267); - case 283: /* trnm ::= nm */ yytestcase(yyruleno==283); + case 40: /* nm ::= ID|INDEXED */ + case 41: /* nm ::= STRING */ yytestcase(yyruleno==41); + case 42: /* nm ::= JOIN_KW */ yytestcase(yyruleno==42); + case 45: /* typetoken ::= typename */ yytestcase(yyruleno==45); + case 48: /* typename ::= ID|STRING */ yytestcase(yyruleno==48); + case 130: /* as ::= AS nm */ yytestcase(yyruleno==130); + case 131: /* as ::= ID|STRING */ yytestcase(yyruleno==131); + case 141: /* dbnm ::= DOT nm */ yytestcase(yyruleno==141); + case 150: /* indexed_opt ::= INDEXED BY nm */ yytestcase(yyruleno==150); + case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247); + case 256: /* nmnum ::= plus_num */ yytestcase(yyruleno==256); + case 257: /* nmnum ::= nm */ yytestcase(yyruleno==257); + case 258: /* nmnum ::= ON */ yytestcase(yyruleno==258); + case 259: /* nmnum ::= DELETE */ yytestcase(yyruleno==259); + case 260: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==260); + case 261: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==261); + case 262: /* plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==262); + case 263: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==263); + case 279: /* trnm ::= nm */ yytestcase(yyruleno==279); {yygotominor.yy0 = yymsp[0].minor.yy0;} break; - case 45: /* type ::= typetoken */ + case 44: /* type ::= typetoken */ {sqlite3AddColumnType(pParse,&yymsp[0].minor.yy0);} break; - case 47: /* typetoken ::= typename LP signed RP */ + case 46: /* typetoken ::= typename LP signed RP */ { yygotominor.yy0.z = yymsp[-3].minor.yy0.z; yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z); } break; - case 48: /* typetoken ::= typename LP signed COMMA signed RP */ + case 47: /* typetoken ::= typename LP signed COMMA signed RP */ { yygotominor.yy0.z = yymsp[-5].minor.yy0.z; yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z); } break; - case 50: /* typename ::= typename ids */ + case 49: /* typename ::= typename ID|STRING */ {yygotominor.yy0.z=yymsp[-1].minor.yy0.z; yygotominor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);} break; - case 55: /* ccons ::= CONSTRAINT nm */ - case 93: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==93); + case 54: /* ccons ::= CONSTRAINT nm */ + case 92: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==92); {pParse->constraintName = yymsp[0].minor.yy0;} break; - case 56: /* ccons ::= DEFAULT term */ - case 58: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==58); -{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy342);} + case 55: /* ccons ::= DEFAULT term */ + case 57: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==57); +{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy346);} break; - case 57: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy342);} + case 56: /* ccons ::= DEFAULT LP expr RP */ +{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy346);} break; - case 59: /* ccons ::= DEFAULT MINUS term */ + case 58: /* ccons ::= DEFAULT MINUS term */ { ExprSpan v; - v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy342.pExpr, 0, 0); + v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy346.pExpr, 0, 0); v.zStart = yymsp[-1].minor.yy0.z; - v.zEnd = yymsp[0].minor.yy342.zEnd; + v.zEnd = yymsp[0].minor.yy346.zEnd; sqlite3AddDefaultValue(pParse,&v); } break; - case 60: /* ccons ::= DEFAULT id */ + case 59: /* ccons ::= DEFAULT ID|INDEXED */ { ExprSpan v; spanExpr(&v, pParse, TK_STRING, &yymsp[0].minor.yy0); sqlite3AddDefaultValue(pParse,&v); } break; - case 62: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy392);} + case 61: /* ccons ::= NOT NULL onconf */ +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy328);} break; - case 63: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy392,yymsp[0].minor.yy392,yymsp[-2].minor.yy392);} + case 62: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy328,yymsp[0].minor.yy328,yymsp[-2].minor.yy328);} break; - case 64: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy392,0,0,0,0);} + case 63: /* ccons ::= UNIQUE onconf */ +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy328,0,0,0,0);} break; - case 65: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy342.pExpr);} + case 64: /* ccons ::= CHECK LP expr RP */ +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy346.pExpr);} break; - case 66: /* ccons ::= REFERENCES nm idxlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy442,yymsp[0].minor.yy392);} + case 65: /* ccons ::= REFERENCES nm idxlist_opt refargs */ +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy328);} break; - case 67: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy392);} + case 66: /* ccons ::= defer_subclause */ +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy328);} break; - case 68: /* ccons ::= COLLATE ids */ + case 67: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; - case 71: /* refargs ::= */ -{ yygotominor.yy392 = OE_None*0x0101; /* EV: R-19803-45884 */} + case 70: /* refargs ::= */ +{ yygotominor.yy328 = OE_None*0x0101; /* EV: R-19803-45884 */} break; - case 72: /* refargs ::= refargs refarg */ -{ yygotominor.yy392 = (yymsp[-1].minor.yy392 & ~yymsp[0].minor.yy207.mask) | yymsp[0].minor.yy207.value; } + case 71: /* refargs ::= refargs refarg */ +{ yygotominor.yy328 = (yymsp[-1].minor.yy328 & ~yymsp[0].minor.yy429.mask) | yymsp[0].minor.yy429.value; } break; - case 73: /* refarg ::= MATCH nm */ - case 74: /* refarg ::= ON INSERT refact */ yytestcase(yyruleno==74); -{ yygotominor.yy207.value = 0; yygotominor.yy207.mask = 0x000000; } + case 72: /* refarg ::= MATCH nm */ + case 73: /* refarg ::= ON INSERT refact */ yytestcase(yyruleno==73); +{ yygotominor.yy429.value = 0; yygotominor.yy429.mask = 0x000000; } break; - case 75: /* refarg ::= ON DELETE refact */ -{ yygotominor.yy207.value = yymsp[0].minor.yy392; yygotominor.yy207.mask = 0x0000ff; } + case 74: /* refarg ::= ON DELETE refact */ +{ yygotominor.yy429.value = yymsp[0].minor.yy328; yygotominor.yy429.mask = 0x0000ff; } break; - case 76: /* refarg ::= ON UPDATE refact */ -{ yygotominor.yy207.value = yymsp[0].minor.yy392<<8; yygotominor.yy207.mask = 0x00ff00; } + case 75: /* refarg ::= ON UPDATE refact */ +{ yygotominor.yy429.value = yymsp[0].minor.yy328<<8; yygotominor.yy429.mask = 0x00ff00; } break; - case 77: /* refact ::= SET NULL */ -{ yygotominor.yy392 = OE_SetNull; /* EV: R-33326-45252 */} + case 76: /* refact ::= SET NULL */ +{ yygotominor.yy328 = OE_SetNull; /* EV: R-33326-45252 */} break; - case 78: /* refact ::= SET DEFAULT */ -{ yygotominor.yy392 = OE_SetDflt; /* EV: R-33326-45252 */} + case 77: /* refact ::= SET DEFAULT */ +{ yygotominor.yy328 = OE_SetDflt; /* EV: R-33326-45252 */} break; - case 79: /* refact ::= CASCADE */ -{ yygotominor.yy392 = OE_Cascade; /* EV: R-33326-45252 */} + case 78: /* refact ::= CASCADE */ +{ yygotominor.yy328 = OE_Cascade; /* EV: R-33326-45252 */} break; - case 80: /* refact ::= RESTRICT */ -{ yygotominor.yy392 = OE_Restrict; /* EV: R-33326-45252 */} + case 79: /* refact ::= RESTRICT */ +{ yygotominor.yy328 = OE_Restrict; /* EV: R-33326-45252 */} break; - case 81: /* refact ::= NO ACTION */ -{ yygotominor.yy392 = OE_None; /* EV: R-33326-45252 */} + case 80: /* refact ::= NO ACTION */ +{ yygotominor.yy328 = OE_None; /* EV: R-33326-45252 */} break; - case 83: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - case 99: /* defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==99); - case 101: /* onconf ::= ON CONFLICT resolvetype */ yytestcase(yyruleno==101); - case 104: /* resolvetype ::= raisetype */ yytestcase(yyruleno==104); -{yygotominor.yy392 = yymsp[0].minor.yy392;} + case 82: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + case 98: /* defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==98); + case 100: /* onconf ::= ON CONFLICT resolvetype */ yytestcase(yyruleno==100); + case 103: /* resolvetype ::= raisetype */ yytestcase(yyruleno==103); +{yygotominor.yy328 = yymsp[0].minor.yy328;} break; - case 87: /* conslist_opt ::= */ + case 86: /* conslist_opt ::= */ {yygotominor.yy0.n = 0; yygotominor.yy0.z = 0;} break; - case 88: /* conslist_opt ::= COMMA conslist */ + case 87: /* conslist_opt ::= COMMA conslist */ {yygotominor.yy0 = yymsp[-1].minor.yy0;} break; - case 91: /* tconscomma ::= COMMA */ + case 90: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; - case 94: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy442,yymsp[0].minor.yy392,yymsp[-2].minor.yy392,0);} + case 93: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */ +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy328,yymsp[-2].minor.yy328,0);} break; - case 95: /* tcons ::= UNIQUE LP idxlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy442,yymsp[0].minor.yy392,0,0,0,0);} + case 94: /* tcons ::= UNIQUE LP idxlist RP onconf */ +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy328,0,0,0,0);} break; - case 96: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy342.pExpr);} + case 95: /* tcons ::= CHECK LP expr RP onconf */ +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy346.pExpr);} break; - case 97: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */ + case 96: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy442, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy442, yymsp[-1].minor.yy392); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy392); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy328); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy328); } break; - case 100: /* onconf ::= */ -{yygotominor.yy392 = OE_Default;} + case 99: /* onconf ::= */ +{yygotominor.yy328 = OE_Default;} break; - case 102: /* orconf ::= */ -{yygotominor.yy258 = OE_Default;} + case 101: /* orconf ::= */ +{yygotominor.yy186 = OE_Default;} break; - case 103: /* orconf ::= OR resolvetype */ -{yygotominor.yy258 = (u8)yymsp[0].minor.yy392;} + case 102: /* orconf ::= OR resolvetype */ +{yygotominor.yy186 = (u8)yymsp[0].minor.yy328;} break; - case 105: /* resolvetype ::= IGNORE */ -{yygotominor.yy392 = OE_Ignore;} + case 104: /* resolvetype ::= IGNORE */ +{yygotominor.yy328 = OE_Ignore;} break; - case 106: /* resolvetype ::= REPLACE */ -{yygotominor.yy392 = OE_Replace;} + case 105: /* resolvetype ::= REPLACE */ +{yygotominor.yy328 = OE_Replace;} break; - case 107: /* cmd ::= DROP TABLE ifexists fullname */ + case 106: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy347, 0, yymsp[-1].minor.yy392); + sqlite3DropTable(pParse, yymsp[0].minor.yy65, 0, yymsp[-1].minor.yy328); } break; - case 110: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select */ + case 109: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select */ { - sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy159, yymsp[-6].minor.yy392, yymsp[-4].minor.yy392); + sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy3, yymsp[-6].minor.yy328, yymsp[-4].minor.yy328); } break; - case 111: /* cmd ::= DROP VIEW ifexists fullname */ + case 110: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy347, 1, yymsp[-1].minor.yy392); + sqlite3DropTable(pParse, yymsp[0].minor.yy65, 1, yymsp[-1].minor.yy328); } break; - case 112: /* cmd ::= select */ + case 111: /* cmd ::= select */ { - SelectDest dest = {SRT_Output, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy159, &dest); + SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0}; + sqlite3Select(pParse, yymsp[0].minor.yy3, &dest); sqlite3ExplainBegin(pParse->pVdbe); - sqlite3ExplainSelect(pParse->pVdbe, yymsp[0].minor.yy159); + sqlite3ExplainSelect(pParse->pVdbe, yymsp[0].minor.yy3); sqlite3ExplainFinish(pParse->pVdbe); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy159); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy3); } break; - case 113: /* select ::= oneselect */ -{yygotominor.yy159 = yymsp[0].minor.yy159;} - break; - case 114: /* select ::= select multiselect_op oneselect */ -{ - if( yymsp[0].minor.yy159 ){ - yymsp[0].minor.yy159->op = (u8)yymsp[-1].minor.yy392; - yymsp[0].minor.yy159->pPrior = yymsp[-2].minor.yy159; - if( yymsp[-1].minor.yy392!=TK_ALL ) pParse->hasCompound = 1; + case 112: /* select ::= with selectnowith */ +{ + if( yymsp[0].minor.yy3 ){ + yymsp[0].minor.yy3->pWith = yymsp[-1].minor.yy59; }else{ - sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy159); + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } - yygotominor.yy159 = yymsp[0].minor.yy159; + yygotominor.yy3 = yymsp[0].minor.yy3; +} + break; + case 113: /* selectnowith ::= oneselect */ + case 119: /* oneselect ::= values */ yytestcase(yyruleno==119); +{yygotominor.yy3 = yymsp[0].minor.yy3;} + break; + case 114: /* selectnowith ::= selectnowith multiselect_op oneselect */ +{ + if( yymsp[0].minor.yy3 ){ + yymsp[0].minor.yy3->op = (u8)yymsp[-1].minor.yy328; + yymsp[0].minor.yy3->pPrior = yymsp[-2].minor.yy3; + if( yymsp[-1].minor.yy328!=TK_ALL ) pParse->hasCompound = 1; + }else{ + sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy3); + } + yygotominor.yy3 = yymsp[0].minor.yy3; } break; case 116: /* multiselect_op ::= UNION ALL */ -{yygotominor.yy392 = TK_ALL;} +{yygotominor.yy328 = TK_ALL;} break; case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yygotominor.yy159 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy442,yymsp[-5].minor.yy347,yymsp[-4].minor.yy122,yymsp[-3].minor.yy442,yymsp[-2].minor.yy122,yymsp[-1].minor.yy442,yymsp[-7].minor.yy305,yymsp[0].minor.yy64.pLimit,yymsp[0].minor.yy64.pOffset); + yygotominor.yy3 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy65,yymsp[-4].minor.yy132,yymsp[-3].minor.yy14,yymsp[-2].minor.yy132,yymsp[-1].minor.yy14,yymsp[-7].minor.yy381,yymsp[0].minor.yy476.pLimit,yymsp[0].minor.yy476.pOffset); } break; - case 119: /* distinct ::= DISTINCT */ -{yygotominor.yy305 = SF_Distinct;} - break; - case 120: /* distinct ::= ALL */ - case 121: /* distinct ::= */ yytestcase(yyruleno==121); -{yygotominor.yy305 = 0;} - break; - case 122: /* sclp ::= selcollist COMMA */ - case 246: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==246); -{yygotominor.yy442 = yymsp[-1].minor.yy442;} - break; - case 123: /* sclp ::= */ - case 151: /* orderby_opt ::= */ yytestcase(yyruleno==151); - case 158: /* groupby_opt ::= */ yytestcase(yyruleno==158); - case 239: /* exprlist ::= */ yytestcase(yyruleno==239); - case 245: /* idxlist_opt ::= */ yytestcase(yyruleno==245); -{yygotominor.yy442 = 0;} - break; - case 124: /* selcollist ::= sclp expr as */ + case 120: /* values ::= VALUES LP nexprlist RP */ { - yygotominor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy442, yymsp[-1].minor.yy342.pExpr); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yygotominor.yy442,&yymsp[-1].minor.yy342); + yygotominor.yy3 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0,0); } break; - case 125: /* selcollist ::= sclp STAR */ + case 121: /* values ::= values COMMA LP exprlist RP */ +{ + Select *pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0,0); + if( pRight ){ + pRight->op = TK_ALL; + pRight->pPrior = yymsp[-4].minor.yy3; + yygotominor.yy3 = pRight; + }else{ + yygotominor.yy3 = yymsp[-4].minor.yy3; + } +} + break; + case 122: /* distinct ::= DISTINCT */ +{yygotominor.yy381 = SF_Distinct;} + break; + case 123: /* distinct ::= ALL */ + case 124: /* distinct ::= */ yytestcase(yyruleno==124); +{yygotominor.yy381 = 0;} + break; + case 125: /* sclp ::= selcollist COMMA */ + case 243: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==243); +{yygotominor.yy14 = yymsp[-1].minor.yy14;} + break; + case 126: /* sclp ::= */ + case 154: /* orderby_opt ::= */ yytestcase(yyruleno==154); + case 161: /* groupby_opt ::= */ yytestcase(yyruleno==161); + case 236: /* exprlist ::= */ yytestcase(yyruleno==236); + case 242: /* idxlist_opt ::= */ yytestcase(yyruleno==242); +{yygotominor.yy14 = 0;} + break; + case 127: /* selcollist ::= sclp expr as */ +{ + yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, yymsp[-1].minor.yy346.pExpr); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yygotominor.yy14,&yymsp[-1].minor.yy346); +} + break; + case 128: /* selcollist ::= sclp STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ALL, 0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy442, p); + yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy14, p); } break; - case 126: /* selcollist ::= sclp nm DOT STAR */ + case 129: /* selcollist ::= sclp nm DOT STAR */ { Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &yymsp[0].minor.yy0); Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy442, pDot); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, pDot); } break; - case 129: /* as ::= */ + case 132: /* as ::= */ {yygotominor.yy0.n = 0;} break; - case 130: /* from ::= */ -{yygotominor.yy347 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy347));} + case 133: /* from ::= */ +{yygotominor.yy65 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy65));} break; - case 131: /* from ::= FROM seltablist */ + case 134: /* from ::= FROM seltablist */ { - yygotominor.yy347 = yymsp[0].minor.yy347; - sqlite3SrcListShiftJoinType(yygotominor.yy347); + yygotominor.yy65 = yymsp[0].minor.yy65; + sqlite3SrcListShiftJoinType(yygotominor.yy65); } break; - case 132: /* stl_prefix ::= seltablist joinop */ + case 135: /* stl_prefix ::= seltablist joinop */ { - yygotominor.yy347 = yymsp[-1].minor.yy347; - if( ALWAYS(yygotominor.yy347 && yygotominor.yy347->nSrc>0) ) yygotominor.yy347->a[yygotominor.yy347->nSrc-1].jointype = (u8)yymsp[0].minor.yy392; + yygotominor.yy65 = yymsp[-1].minor.yy65; + if( ALWAYS(yygotominor.yy65 && yygotominor.yy65->nSrc>0) ) yygotominor.yy65->a[yygotominor.yy65->nSrc-1].jointype = (u8)yymsp[0].minor.yy328; } break; - case 133: /* stl_prefix ::= */ -{yygotominor.yy347 = 0;} + case 136: /* stl_prefix ::= */ +{yygotominor.yy65 = 0;} break; - case 134: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + case 137: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ { - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); - sqlite3SrcListIndexedBy(pParse, yygotominor.yy347, &yymsp[-2].minor.yy0); + yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy132,yymsp[0].minor.yy408); + sqlite3SrcListIndexedBy(pParse, yygotominor.yy65, &yymsp[-2].minor.yy0); } break; - case 135: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + case 138: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ { - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy159,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); + yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy3,yymsp[-1].minor.yy132,yymsp[0].minor.yy408); } break; - case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + case 139: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ { - if( yymsp[-6].minor.yy347==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy122==0 && yymsp[0].minor.yy180==0 ){ - yygotominor.yy347 = yymsp[-4].minor.yy347; - }else if( yymsp[-4].minor.yy347->nSrc==1 ){ - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); - if( yygotominor.yy347 ){ - struct SrcList_item *pNew = &yygotominor.yy347->a[yygotominor.yy347->nSrc-1]; - struct SrcList_item *pOld = yymsp[-4].minor.yy347->a; + if( yymsp[-6].minor.yy65==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy132==0 && yymsp[0].minor.yy408==0 ){ + yygotominor.yy65 = yymsp[-4].minor.yy65; + }else if( yymsp[-4].minor.yy65->nSrc==1 ){ + yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy132,yymsp[0].minor.yy408); + if( yygotominor.yy65 ){ + struct SrcList_item *pNew = &yygotominor.yy65->a[yygotominor.yy65->nSrc-1]; + struct SrcList_item *pOld = yymsp[-4].minor.yy65->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy347); + sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy65); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy347); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy347,0,0,0,0,SF_NestedFrom,0,0); - yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy122,yymsp[0].minor.yy180); + sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy65); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy65,0,0,0,0,SF_NestedFrom,0,0); + yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy132,yymsp[0].minor.yy408); } } break; - case 137: /* dbnm ::= */ - case 146: /* indexed_opt ::= */ yytestcase(yyruleno==146); + case 140: /* dbnm ::= */ + case 149: /* indexed_opt ::= */ yytestcase(yyruleno==149); {yygotominor.yy0.z=0; yygotominor.yy0.n=0;} break; - case 139: /* fullname ::= nm dbnm */ -{yygotominor.yy347 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} + case 142: /* fullname ::= nm dbnm */ +{yygotominor.yy65 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} break; - case 140: /* joinop ::= COMMA|JOIN */ -{ yygotominor.yy392 = JT_INNER; } + case 143: /* joinop ::= COMMA|JOIN */ +{ yygotominor.yy328 = JT_INNER; } break; - case 141: /* joinop ::= JOIN_KW JOIN */ -{ yygotominor.yy392 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); } + case 144: /* joinop ::= JOIN_KW JOIN */ +{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); } break; - case 142: /* joinop ::= JOIN_KW nm JOIN */ -{ yygotominor.yy392 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); } + case 145: /* joinop ::= JOIN_KW nm JOIN */ +{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); } break; - case 143: /* joinop ::= JOIN_KW nm nm JOIN */ -{ yygotominor.yy392 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); } + case 146: /* joinop ::= JOIN_KW nm nm JOIN */ +{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); } break; - case 144: /* on_opt ::= ON expr */ - case 161: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==161); - case 168: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==168); - case 234: /* case_else ::= ELSE expr */ yytestcase(yyruleno==234); - case 236: /* case_operand ::= expr */ yytestcase(yyruleno==236); -{yygotominor.yy122 = yymsp[0].minor.yy342.pExpr;} + case 147: /* on_opt ::= ON expr */ + case 164: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==164); + case 171: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==171); + case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231); + case 233: /* case_operand ::= expr */ yytestcase(yyruleno==233); +{yygotominor.yy132 = yymsp[0].minor.yy346.pExpr;} break; - case 145: /* on_opt ::= */ - case 160: /* having_opt ::= */ yytestcase(yyruleno==160); - case 167: /* where_opt ::= */ yytestcase(yyruleno==167); - case 235: /* case_else ::= */ yytestcase(yyruleno==235); - case 237: /* case_operand ::= */ yytestcase(yyruleno==237); -{yygotominor.yy122 = 0;} + case 148: /* on_opt ::= */ + case 163: /* having_opt ::= */ yytestcase(yyruleno==163); + case 170: /* where_opt ::= */ yytestcase(yyruleno==170); + case 232: /* case_else ::= */ yytestcase(yyruleno==232); + case 234: /* case_operand ::= */ yytestcase(yyruleno==234); +{yygotominor.yy132 = 0;} break; - case 148: /* indexed_opt ::= NOT INDEXED */ + case 151: /* indexed_opt ::= NOT INDEXED */ {yygotominor.yy0.z=0; yygotominor.yy0.n=1;} break; - case 149: /* using_opt ::= USING LP inscollist RP */ - case 180: /* inscollist_opt ::= LP inscollist RP */ yytestcase(yyruleno==180); -{yygotominor.yy180 = yymsp[-1].minor.yy180;} + case 152: /* using_opt ::= USING LP idlist RP */ + case 180: /* inscollist_opt ::= LP idlist RP */ yytestcase(yyruleno==180); +{yygotominor.yy408 = yymsp[-1].minor.yy408;} break; - case 150: /* using_opt ::= */ + case 153: /* using_opt ::= */ case 179: /* inscollist_opt ::= */ yytestcase(yyruleno==179); -{yygotominor.yy180 = 0;} +{yygotominor.yy408 = 0;} break; - case 152: /* orderby_opt ::= ORDER BY sortlist */ - case 159: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==159); - case 238: /* exprlist ::= nexprlist */ yytestcase(yyruleno==238); -{yygotominor.yy442 = yymsp[0].minor.yy442;} + case 155: /* orderby_opt ::= ORDER BY sortlist */ + case 162: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==162); + case 235: /* exprlist ::= nexprlist */ yytestcase(yyruleno==235); +{yygotominor.yy14 = yymsp[0].minor.yy14;} break; - case 153: /* sortlist ::= sortlist COMMA expr sortorder */ + case 156: /* sortlist ::= sortlist COMMA expr sortorder */ { - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy442,yymsp[-1].minor.yy342.pExpr); - if( yygotominor.yy442 ) yygotominor.yy442->a[yygotominor.yy442->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy392; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14,yymsp[-1].minor.yy346.pExpr); + if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 154: /* sortlist ::= expr sortorder */ + case 157: /* sortlist ::= expr sortorder */ { - yygotominor.yy442 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy342.pExpr); - if( yygotominor.yy442 && ALWAYS(yygotominor.yy442->a) ) yygotominor.yy442->a[0].sortOrder = (u8)yymsp[0].minor.yy392; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy346.pExpr); + if( yygotominor.yy14 && ALWAYS(yygotominor.yy14->a) ) yygotominor.yy14->a[0].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 155: /* sortorder ::= ASC */ - case 157: /* sortorder ::= */ yytestcase(yyruleno==157); -{yygotominor.yy392 = SQLITE_SO_ASC;} + case 158: /* sortorder ::= ASC */ + case 160: /* sortorder ::= */ yytestcase(yyruleno==160); +{yygotominor.yy328 = SQLITE_SO_ASC;} break; - case 156: /* sortorder ::= DESC */ -{yygotominor.yy392 = SQLITE_SO_DESC;} + case 159: /* sortorder ::= DESC */ +{yygotominor.yy328 = SQLITE_SO_DESC;} break; - case 162: /* limit_opt ::= */ -{yygotominor.yy64.pLimit = 0; yygotominor.yy64.pOffset = 0;} + case 165: /* limit_opt ::= */ +{yygotominor.yy476.pLimit = 0; yygotominor.yy476.pOffset = 0;} break; - case 163: /* limit_opt ::= LIMIT expr */ -{yygotominor.yy64.pLimit = yymsp[0].minor.yy342.pExpr; yygotominor.yy64.pOffset = 0;} + case 166: /* limit_opt ::= LIMIT expr */ +{yygotominor.yy476.pLimit = yymsp[0].minor.yy346.pExpr; yygotominor.yy476.pOffset = 0;} break; - case 164: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yygotominor.yy64.pLimit = yymsp[-2].minor.yy342.pExpr; yygotominor.yy64.pOffset = yymsp[0].minor.yy342.pExpr;} + case 167: /* limit_opt ::= LIMIT expr OFFSET expr */ +{yygotominor.yy476.pLimit = yymsp[-2].minor.yy346.pExpr; yygotominor.yy476.pOffset = yymsp[0].minor.yy346.pExpr;} break; - case 165: /* limit_opt ::= LIMIT expr COMMA expr */ -{yygotominor.yy64.pOffset = yymsp[-2].minor.yy342.pExpr; yygotominor.yy64.pLimit = yymsp[0].minor.yy342.pExpr;} + case 168: /* limit_opt ::= LIMIT expr COMMA expr */ +{yygotominor.yy476.pOffset = yymsp[-2].minor.yy346.pExpr; yygotominor.yy476.pLimit = yymsp[0].minor.yy346.pExpr;} break; - case 166: /* cmd ::= DELETE FROM fullname indexed_opt where_opt */ + case 169: /* cmd ::= with DELETE FROM fullname indexed_opt where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy347, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy347,yymsp[0].minor.yy122); + sqlite3WithPush(pParse, yymsp[-5].minor.yy59, 1); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy65, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy65,yymsp[0].minor.yy132); } break; - case 169: /* cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt */ + case 172: /* cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy347, &yymsp[-3].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy442,"set list"); - sqlite3Update(pParse,yymsp[-4].minor.yy347,yymsp[-1].minor.yy442,yymsp[0].minor.yy122,yymsp[-5].minor.yy258); + sqlite3WithPush(pParse, yymsp[-7].minor.yy59, 1); + sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy65, &yymsp[-3].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy14,"set list"); + sqlite3Update(pParse,yymsp[-4].minor.yy65,yymsp[-1].minor.yy14,yymsp[0].minor.yy132,yymsp[-5].minor.yy186); } break; - case 170: /* setlist ::= setlist COMMA nm EQ expr */ + case 173: /* setlist ::= setlist COMMA nm EQ expr */ { - yygotominor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy442, yymsp[0].minor.yy342.pExpr); - sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[-2].minor.yy0, 1); + yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy346.pExpr); + sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); } break; - case 171: /* setlist ::= nm EQ expr */ + case 174: /* setlist ::= nm EQ expr */ { - yygotominor.yy442 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy342.pExpr); - sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[-2].minor.yy0, 1); + yygotominor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy346.pExpr); + sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); } break; - case 172: /* cmd ::= insert_cmd INTO fullname inscollist_opt valuelist */ -{sqlite3Insert(pParse, yymsp[-2].minor.yy347, yymsp[0].minor.yy487.pList, yymsp[0].minor.yy487.pSelect, yymsp[-1].minor.yy180, yymsp[-4].minor.yy258);} - break; - case 173: /* cmd ::= insert_cmd INTO fullname inscollist_opt select */ -{sqlite3Insert(pParse, yymsp[-2].minor.yy347, 0, yymsp[0].minor.yy159, yymsp[-1].minor.yy180, yymsp[-4].minor.yy258);} - break; - case 174: /* cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */ -{sqlite3Insert(pParse, yymsp[-3].minor.yy347, 0, 0, yymsp[-2].minor.yy180, yymsp[-5].minor.yy258);} - break; - case 175: /* insert_cmd ::= INSERT orconf */ -{yygotominor.yy258 = yymsp[0].minor.yy258;} - break; - case 176: /* insert_cmd ::= REPLACE */ -{yygotominor.yy258 = OE_Replace;} - break; - case 177: /* valuelist ::= VALUES LP nexprlist RP */ + case 175: /* cmd ::= with insert_cmd INTO fullname inscollist_opt select */ { - yygotominor.yy487.pList = yymsp[-1].minor.yy442; - yygotominor.yy487.pSelect = 0; + sqlite3WithPush(pParse, yymsp[-5].minor.yy59, 1); + sqlite3Insert(pParse, yymsp[-2].minor.yy65, yymsp[0].minor.yy3, yymsp[-1].minor.yy408, yymsp[-4].minor.yy186); } break; - case 178: /* valuelist ::= valuelist COMMA LP exprlist RP */ + case 176: /* cmd ::= with insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */ { - Select *pRight = sqlite3SelectNew(pParse, yymsp[-1].minor.yy442, 0, 0, 0, 0, 0, 0, 0, 0); - if( yymsp[-4].minor.yy487.pList ){ - yymsp[-4].minor.yy487.pSelect = sqlite3SelectNew(pParse, yymsp[-4].minor.yy487.pList, 0, 0, 0, 0, 0, 0, 0, 0); - yymsp[-4].minor.yy487.pList = 0; - } - yygotominor.yy487.pList = 0; - if( yymsp[-4].minor.yy487.pSelect==0 || pRight==0 ){ - sqlite3SelectDelete(pParse->db, pRight); - sqlite3SelectDelete(pParse->db, yymsp[-4].minor.yy487.pSelect); - yygotominor.yy487.pSelect = 0; - }else{ - pRight->op = TK_ALL; - pRight->pPrior = yymsp[-4].minor.yy487.pSelect; - pRight->selFlags |= SF_Values; - pRight->pPrior->selFlags |= SF_Values; - yygotominor.yy487.pSelect = pRight; - } + sqlite3WithPush(pParse, yymsp[-6].minor.yy59, 1); + sqlite3Insert(pParse, yymsp[-3].minor.yy65, 0, yymsp[-2].minor.yy408, yymsp[-5].minor.yy186); } break; - case 181: /* inscollist ::= inscollist COMMA nm */ -{yygotominor.yy180 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy180,&yymsp[0].minor.yy0);} + case 177: /* insert_cmd ::= INSERT orconf */ +{yygotominor.yy186 = yymsp[0].minor.yy186;} break; - case 182: /* inscollist ::= nm */ -{yygotominor.yy180 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);} + case 178: /* insert_cmd ::= REPLACE */ +{yygotominor.yy186 = OE_Replace;} + break; + case 181: /* idlist ::= idlist COMMA nm */ +{yygotominor.yy408 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy408,&yymsp[0].minor.yy0);} + break; + case 182: /* idlist ::= nm */ +{yygotominor.yy408 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);} break; case 183: /* expr ::= term */ -{yygotominor.yy342 = yymsp[0].minor.yy342;} +{yygotominor.yy346 = yymsp[0].minor.yy346;} break; case 184: /* expr ::= LP expr RP */ -{yygotominor.yy342.pExpr = yymsp[-1].minor.yy342.pExpr; spanSet(&yygotominor.yy342,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);} +{yygotominor.yy346.pExpr = yymsp[-1].minor.yy346.pExpr; spanSet(&yygotominor.yy346,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);} break; case 185: /* term ::= NULL */ case 190: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==190); case 191: /* term ::= STRING */ yytestcase(yyruleno==191); -{spanExpr(&yygotominor.yy342, pParse, yymsp[0].major, &yymsp[0].minor.yy0);} +{spanExpr(&yygotominor.yy346, pParse, yymsp[0].major, &yymsp[0].minor.yy0);} break; - case 186: /* expr ::= id */ + case 186: /* expr ::= ID|INDEXED */ case 187: /* expr ::= JOIN_KW */ yytestcase(yyruleno==187); -{spanExpr(&yygotominor.yy342, pParse, TK_ID, &yymsp[0].minor.yy0);} +{spanExpr(&yygotominor.yy346, pParse, TK_ID, &yymsp[0].minor.yy0);} break; case 188: /* expr ::= nm DOT nm */ { Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0); - spanSet(&yygotominor.yy342,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0); + spanSet(&yygotominor.yy346,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); } break; case 189: /* expr ::= nm DOT nm DOT nm */ @@ -115487,163 +117431,154 @@ static void yy_reduce( Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0); Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0); - spanSet(&yygotominor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0); + spanSet(&yygotominor.yy346,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); } break; - case 192: /* expr ::= REGISTER */ + case 192: /* expr ::= VARIABLE */ { - /* When doing a nested parse, one can include terms in an expression - ** that look like this: #1 #2 ... These terms refer to registers - ** in the virtual machine. #N is the N-th register. */ - if( pParse->nested==0 ){ - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &yymsp[0].minor.yy0); - yygotominor.yy342.pExpr = 0; + if( yymsp[0].minor.yy0.n>=2 && yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1]) ){ + /* When doing a nested parse, one can include terms in an expression + ** that look like this: #1 #2 ... These terms refer to registers + ** in the virtual machine. #N is the N-th register. */ + if( pParse->nested==0 ){ + sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = 0; + }else{ + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &yymsp[0].minor.yy0); + if( yygotominor.yy346.pExpr ) sqlite3GetInt32(&yymsp[0].minor.yy0.z[1], &yygotominor.yy346.pExpr->iTable); + } }else{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &yymsp[0].minor.yy0); - if( yygotominor.yy342.pExpr ) sqlite3GetInt32(&yymsp[0].minor.yy0.z[1], &yygotominor.yy342.pExpr->iTable); + spanExpr(&yygotominor.yy346, pParse, TK_VARIABLE, &yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yygotominor.yy346.pExpr); } - spanSet(&yygotominor.yy342, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); + spanSet(&yygotominor.yy346, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); } break; - case 193: /* expr ::= VARIABLE */ + case 193: /* expr ::= expr COLLATE ID|STRING */ { - spanExpr(&yygotominor.yy342, pParse, TK_VARIABLE, &yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yygotominor.yy342.pExpr); - spanSet(&yygotominor.yy342, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0); + yygotominor.yy346.zStart = yymsp[-2].minor.yy346.zStart; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 194: /* expr ::= expr COLLATE ids */ + case 194: /* expr ::= CAST LP expr AS typetoken RP */ { - yygotominor.yy342.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy342.pExpr, &yymsp[0].minor.yy0); - yygotominor.yy342.zStart = yymsp[-2].minor.yy342.zStart; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy346.pExpr, 0, &yymsp[-1].minor.yy0); + spanSet(&yygotominor.yy346,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); } break; - case 195: /* expr ::= CAST LP expr AS typetoken RP */ + case 195: /* expr ::= ID|INDEXED LP distinct exprlist RP */ { - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy342.pExpr, 0, &yymsp[-1].minor.yy0); - spanSet(&yygotominor.yy342,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); -} - break; - case 196: /* expr ::= ID LP distinct exprlist RP */ -{ - if( yymsp[-1].minor.yy442 && yymsp[-1].minor.yy442->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ + if( yymsp[-1].minor.yy14 && yymsp[-1].minor.yy14->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0); } - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy442, &yymsp[-4].minor.yy0); - spanSet(&yygotominor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); - if( yymsp[-2].minor.yy305 && yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->flags |= EP_Distinct; + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0); + spanSet(&yygotominor.yy346,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); + if( yymsp[-2].minor.yy381 && yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->flags |= EP_Distinct; } } break; - case 197: /* expr ::= ID LP STAR RP */ + case 196: /* expr ::= ID|INDEXED LP STAR RP */ { - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0); - spanSet(&yygotominor.yy342,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0); + spanSet(&yygotominor.yy346,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); } break; - case 198: /* term ::= CTIME_KW */ + case 197: /* term ::= CTIME_KW */ { - /* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are - ** treated as functions that return constants */ - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->op = TK_CONST_FUNC; - } - spanSet(&yygotominor.yy342, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0); + spanSet(&yygotominor.yy346, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); } break; - case 199: /* expr ::= expr AND expr */ - case 200: /* expr ::= expr OR expr */ yytestcase(yyruleno==200); - case 201: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==201); - case 202: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==202); - case 203: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==203); - case 204: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==204); - case 205: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==205); - case 206: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==206); -{spanBinaryExpr(&yygotominor.yy342,pParse,yymsp[-1].major,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy342);} + case 198: /* expr ::= expr AND expr */ + case 199: /* expr ::= expr OR expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==200); + case 201: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==201); + case 202: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==202); + case 203: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==203); + case 204: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==204); + case 205: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==205); +{spanBinaryExpr(&yygotominor.yy346,pParse,yymsp[-1].major,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy346);} break; - case 207: /* likeop ::= LIKE_KW */ - case 209: /* likeop ::= MATCH */ yytestcase(yyruleno==209); -{yygotominor.yy318.eOperator = yymsp[0].minor.yy0; yygotominor.yy318.bNot = 0;} + case 206: /* likeop ::= LIKE_KW|MATCH */ +{yygotominor.yy96.eOperator = yymsp[0].minor.yy0; yygotominor.yy96.bNot = 0;} break; - case 208: /* likeop ::= NOT LIKE_KW */ - case 210: /* likeop ::= NOT MATCH */ yytestcase(yyruleno==210); -{yygotominor.yy318.eOperator = yymsp[0].minor.yy0; yygotominor.yy318.bNot = 1;} + case 207: /* likeop ::= NOT LIKE_KW|MATCH */ +{yygotominor.yy96.eOperator = yymsp[0].minor.yy0; yygotominor.yy96.bNot = 1;} break; - case 211: /* expr ::= expr likeop expr */ + case 208: /* expr ::= expr likeop expr */ { ExprList *pList; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy342.pExpr); - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy318.eOperator); - if( yymsp[-1].minor.yy318.bNot ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-2].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy342.zEnd; - if( yygotominor.yy342.pExpr ) yygotominor.yy342.pExpr->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy346.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy346.pExpr); + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy96.eOperator); + if( yymsp[-1].minor.yy96.bNot ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-2].minor.yy346.zStart; + yygotominor.yy346.zEnd = yymsp[0].minor.yy346.zEnd; + if( yygotominor.yy346.pExpr ) yygotominor.yy346.pExpr->flags |= EP_InfixFunc; } break; - case 212: /* expr ::= expr likeop expr ESCAPE expr */ + case 209: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy342.pExpr); - yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy318.eOperator); - if( yymsp[-3].minor.yy318.bNot ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy342.zEnd; - if( yygotominor.yy342.pExpr ) yygotominor.yy342.pExpr->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy346.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy346.pExpr); + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy96.eOperator); + if( yymsp[-3].minor.yy96.bNot ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart; + yygotominor.yy346.zEnd = yymsp[0].minor.yy346.zEnd; + if( yygotominor.yy346.pExpr ) yygotominor.yy346.pExpr->flags |= EP_InfixFunc; } break; - case 213: /* expr ::= expr ISNULL|NOTNULL */ -{spanUnaryPostfix(&yygotominor.yy342,pParse,yymsp[0].major,&yymsp[-1].minor.yy342,&yymsp[0].minor.yy0);} + case 210: /* expr ::= expr ISNULL|NOTNULL */ +{spanUnaryPostfix(&yygotominor.yy346,pParse,yymsp[0].major,&yymsp[-1].minor.yy346,&yymsp[0].minor.yy0);} break; - case 214: /* expr ::= expr NOT NULL */ -{spanUnaryPostfix(&yygotominor.yy342,pParse,TK_NOTNULL,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy0);} + case 211: /* expr ::= expr NOT NULL */ +{spanUnaryPostfix(&yygotominor.yy346,pParse,TK_NOTNULL,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy0);} break; - case 215: /* expr ::= expr IS expr */ + case 212: /* expr ::= expr IS expr */ { - spanBinaryExpr(&yygotominor.yy342,pParse,TK_IS,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy342); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy342.pExpr, yygotominor.yy342.pExpr, TK_ISNULL); + spanBinaryExpr(&yygotominor.yy346,pParse,TK_IS,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy346); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy346.pExpr, yygotominor.yy346.pExpr, TK_ISNULL); } break; - case 216: /* expr ::= expr IS NOT expr */ + case 213: /* expr ::= expr IS NOT expr */ { - spanBinaryExpr(&yygotominor.yy342,pParse,TK_ISNOT,&yymsp[-3].minor.yy342,&yymsp[0].minor.yy342); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy342.pExpr, yygotominor.yy342.pExpr, TK_NOTNULL); + spanBinaryExpr(&yygotominor.yy346,pParse,TK_ISNOT,&yymsp[-3].minor.yy346,&yymsp[0].minor.yy346); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy346.pExpr, yygotominor.yy346.pExpr, TK_NOTNULL); } break; - case 217: /* expr ::= NOT expr */ - case 218: /* expr ::= BITNOT expr */ yytestcase(yyruleno==218); -{spanUnaryPrefix(&yygotominor.yy342,pParse,yymsp[-1].major,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);} + case 214: /* expr ::= NOT expr */ + case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); +{spanUnaryPrefix(&yygotominor.yy346,pParse,yymsp[-1].major,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);} break; - case 219: /* expr ::= MINUS expr */ -{spanUnaryPrefix(&yygotominor.yy342,pParse,TK_UMINUS,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);} + case 216: /* expr ::= MINUS expr */ +{spanUnaryPrefix(&yygotominor.yy346,pParse,TK_UMINUS,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);} break; - case 220: /* expr ::= PLUS expr */ -{spanUnaryPrefix(&yygotominor.yy342,pParse,TK_UPLUS,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);} + case 217: /* expr ::= PLUS expr */ +{spanUnaryPrefix(&yygotominor.yy346,pParse,TK_UPLUS,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);} break; - case 223: /* expr ::= expr between_op expr AND expr */ + case 220: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy342.pExpr); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy346.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy342.zEnd; + if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart; + yygotominor.yy346.zEnd = yymsp[0].minor.yy346.zEnd; } break; - case 226: /* expr ::= expr in_op LP exprlist RP */ + case 223: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy442==0 ){ + if( yymsp[-1].minor.yy14==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -115652,225 +117587,225 @@ static void yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[yymsp[-3].minor.yy392]); - sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy342.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[yymsp[-3].minor.yy328]); + sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy346.pExpr); }else{ - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pList = yymsp[-1].minor.yy442; - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pList = yymsp[-1].minor.yy14; + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy442); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); } - if( yymsp[-3].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); + if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); } - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 227: /* expr ::= LP select RP */ + case 224: /* expr ::= LP select RP */ { - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pSelect = yymsp[-1].minor.yy159; - ExprSetProperty(yygotominor.yy342.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy159); + sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } - yygotominor.yy342.zStart = yymsp[-2].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-2].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 228: /* expr ::= expr in_op LP select RP */ + case 225: /* expr ::= expr in_op LP select RP */ { - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pSelect = yymsp[-1].minor.yy159; - ExprSetProperty(yygotominor.yy342.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy159); + sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } - if( yymsp[-3].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-4].minor.yy342.zStart; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 229: /* expr ::= expr in_op nm dbnm */ + case 226: /* expr ::= expr in_op nm dbnm */ { SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0); - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy342.pExpr, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); - ExprSetProperty(yygotominor.yy342.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SrcListDelete(pParse->db, pSrc); } - if( yymsp[-2].minor.yy392 ) yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy342.pExpr, 0, 0); - yygotominor.yy342.zStart = yymsp[-3].minor.yy342.zStart; - yygotominor.yy342.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]; + if( yymsp[-2].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-3].minor.yy346.zStart; + yygotominor.yy346.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]; } break; - case 230: /* expr ::= EXISTS LP select RP */ + case 227: /* expr ::= EXISTS LP select RP */ { - Expr *p = yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); + Expr *p = yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); if( p ){ - p->x.pSelect = yymsp[-1].minor.yy159; + p->x.pSelect = yymsp[-1].minor.yy3; ExprSetProperty(p, EP_xIsSelect); sqlite3ExprSetHeight(pParse, p); }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy159); + sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } - yygotominor.yy342.zStart = yymsp[-3].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-3].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 231: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 228: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy122, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->x.pList = yymsp[-1].minor.yy122 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy442,yymsp[-1].minor.yy122) : yymsp[-2].minor.yy442; - sqlite3ExprSetHeight(pParse, yygotominor.yy342.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy132, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pList = yymsp[-1].minor.yy132 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy132) : yymsp[-2].minor.yy14; + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy442); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy122); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy132); } - yygotominor.yy342.zStart = yymsp[-4].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-4].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 232: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy442, yymsp[-2].minor.yy342.pExpr); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yygotominor.yy442, yymsp[0].minor.yy342.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy346.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yygotominor.yy14, yymsp[0].minor.yy346.pExpr); } break; - case 233: /* case_exprlist ::= WHEN expr THEN expr */ + case 230: /* case_exprlist ::= WHEN expr THEN expr */ { - yygotominor.yy442 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yygotominor.yy442, yymsp[0].minor.yy342.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yygotominor.yy14, yymsp[0].minor.yy346.pExpr); } break; - case 240: /* nexprlist ::= nexprlist COMMA expr */ -{yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy442,yymsp[0].minor.yy342.pExpr);} + case 237: /* nexprlist ::= nexprlist COMMA expr */ +{yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy346.pExpr);} break; - case 241: /* nexprlist ::= expr */ -{yygotominor.yy442 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy342.pExpr);} + case 238: /* nexprlist ::= expr */ +{yygotominor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy346.pExpr);} break; - case 242: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt */ + case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy442, yymsp[-10].minor.yy392, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy122, SQLITE_SO_ASC, yymsp[-8].minor.yy392); + sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy328, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy132, SQLITE_SO_ASC, yymsp[-8].minor.yy328); } break; - case 243: /* uniqueflag ::= UNIQUE */ - case 296: /* raisetype ::= ABORT */ yytestcase(yyruleno==296); -{yygotominor.yy392 = OE_Abort;} + case 240: /* uniqueflag ::= UNIQUE */ + case 291: /* raisetype ::= ABORT */ yytestcase(yyruleno==291); +{yygotominor.yy328 = OE_Abort;} break; - case 244: /* uniqueflag ::= */ -{yygotominor.yy392 = OE_None;} + case 241: /* uniqueflag ::= */ +{yygotominor.yy328 = OE_None;} break; - case 247: /* idxlist ::= idxlist COMMA nm collate sortorder */ + case 244: /* idxlist ::= idxlist COMMA nm collate sortorder */ { Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy442, p); - sqlite3ExprListSetName(pParse,yygotominor.yy442,&yymsp[-2].minor.yy0,1); - sqlite3ExprListCheckLength(pParse, yygotominor.yy442, "index"); - if( yygotominor.yy442 ) yygotominor.yy442->a[yygotominor.yy442->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy392; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, p); + sqlite3ExprListSetName(pParse,yygotominor.yy14,&yymsp[-2].minor.yy0,1); + sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); + if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 248: /* idxlist ::= nm collate sortorder */ + case 245: /* idxlist ::= nm collate sortorder */ { Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); - yygotominor.yy442 = sqlite3ExprListAppend(pParse,0, p); - sqlite3ExprListSetName(pParse, yygotominor.yy442, &yymsp[-2].minor.yy0, 1); - sqlite3ExprListCheckLength(pParse, yygotominor.yy442, "index"); - if( yygotominor.yy442 ) yygotominor.yy442->a[yygotominor.yy442->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy392; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, p); + sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); + sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); + if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 249: /* collate ::= */ + case 246: /* collate ::= */ {yygotominor.yy0.z = 0; yygotominor.yy0.n = 0;} break; - case 251: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy347, yymsp[-1].minor.yy392);} + case 248: /* cmd ::= DROP INDEX ifexists fullname */ +{sqlite3DropIndex(pParse, yymsp[0].minor.yy65, yymsp[-1].minor.yy328);} break; - case 252: /* cmd ::= VACUUM */ - case 253: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==253); + case 249: /* cmd ::= VACUUM */ + case 250: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==250); {sqlite3Vacuum(pParse);} break; - case 254: /* cmd ::= PRAGMA nm dbnm */ + case 251: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 255: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 256: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 257: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 258: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 268: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 264: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy327, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy473, &all); } break; - case 269: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 265: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy392, yymsp[-4].minor.yy410.a, yymsp[-4].minor.yy410.b, yymsp[-2].minor.yy347, yymsp[0].minor.yy122, yymsp[-10].minor.yy392, yymsp[-8].minor.yy392); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy328, yymsp[-4].minor.yy378.a, yymsp[-4].minor.yy378.b, yymsp[-2].minor.yy65, yymsp[0].minor.yy132, yymsp[-10].minor.yy328, yymsp[-8].minor.yy328); yygotominor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); } break; - case 270: /* trigger_time ::= BEFORE */ - case 273: /* trigger_time ::= */ yytestcase(yyruleno==273); -{ yygotominor.yy392 = TK_BEFORE; } + case 266: /* trigger_time ::= BEFORE */ + case 269: /* trigger_time ::= */ yytestcase(yyruleno==269); +{ yygotominor.yy328 = TK_BEFORE; } break; - case 271: /* trigger_time ::= AFTER */ -{ yygotominor.yy392 = TK_AFTER; } + case 267: /* trigger_time ::= AFTER */ +{ yygotominor.yy328 = TK_AFTER; } break; - case 272: /* trigger_time ::= INSTEAD OF */ -{ yygotominor.yy392 = TK_INSTEAD;} + case 268: /* trigger_time ::= INSTEAD OF */ +{ yygotominor.yy328 = TK_INSTEAD;} break; - case 274: /* trigger_event ::= DELETE|INSERT */ - case 275: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==275); -{yygotominor.yy410.a = yymsp[0].major; yygotominor.yy410.b = 0;} + case 270: /* trigger_event ::= DELETE|INSERT */ + case 271: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==271); +{yygotominor.yy378.a = yymsp[0].major; yygotominor.yy378.b = 0;} break; - case 276: /* trigger_event ::= UPDATE OF inscollist */ -{yygotominor.yy410.a = TK_UPDATE; yygotominor.yy410.b = yymsp[0].minor.yy180;} + case 272: /* trigger_event ::= UPDATE OF idlist */ +{yygotominor.yy378.a = TK_UPDATE; yygotominor.yy378.b = yymsp[0].minor.yy408;} break; - case 279: /* when_clause ::= */ - case 301: /* key_opt ::= */ yytestcase(yyruleno==301); -{ yygotominor.yy122 = 0; } + case 275: /* when_clause ::= */ + case 296: /* key_opt ::= */ yytestcase(yyruleno==296); +{ yygotominor.yy132 = 0; } break; - case 280: /* when_clause ::= WHEN expr */ - case 302: /* key_opt ::= KEY expr */ yytestcase(yyruleno==302); -{ yygotominor.yy122 = yymsp[0].minor.yy342.pExpr; } + case 276: /* when_clause ::= WHEN expr */ + case 297: /* key_opt ::= KEY expr */ yytestcase(yyruleno==297); +{ yygotominor.yy132 = yymsp[0].minor.yy346.pExpr; } break; - case 281: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 277: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy327!=0 ); - yymsp[-2].minor.yy327->pLast->pNext = yymsp[-1].minor.yy327; - yymsp[-2].minor.yy327->pLast = yymsp[-1].minor.yy327; - yygotominor.yy327 = yymsp[-2].minor.yy327; + assert( yymsp[-2].minor.yy473!=0 ); + yymsp[-2].minor.yy473->pLast->pNext = yymsp[-1].minor.yy473; + yymsp[-2].minor.yy473->pLast = yymsp[-1].minor.yy473; + yygotominor.yy473 = yymsp[-2].minor.yy473; } break; - case 282: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 278: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy327!=0 ); - yymsp[-1].minor.yy327->pLast = yymsp[-1].minor.yy327; - yygotominor.yy327 = yymsp[-1].minor.yy327; + assert( yymsp[-1].minor.yy473!=0 ); + yymsp[-1].minor.yy473->pLast = yymsp[-1].minor.yy473; + yygotominor.yy473 = yymsp[-1].minor.yy473; } break; - case 284: /* trnm ::= nm DOT nm */ + case 280: /* trnm ::= nm DOT nm */ { yygotominor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -115878,123 +117813,137 @@ static void yy_reduce( "statements within triggers"); } break; - case 286: /* tridxby ::= INDEXED BY nm */ + case 282: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 287: /* tridxby ::= NOT INDEXED */ + case 283: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 288: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */ -{ yygotominor.yy327 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy442, yymsp[0].minor.yy122, yymsp[-5].minor.yy258); } + case 284: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */ +{ yygotominor.yy473 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy14, yymsp[0].minor.yy132, yymsp[-5].minor.yy186); } break; - case 289: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt valuelist */ -{yygotominor.yy327 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy180, yymsp[0].minor.yy487.pList, yymsp[0].minor.yy487.pSelect, yymsp[-4].minor.yy258);} + case 285: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select */ +{yygotominor.yy473 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy408, yymsp[0].minor.yy3, yymsp[-4].minor.yy186);} break; - case 290: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select */ -{yygotominor.yy327 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy180, 0, yymsp[0].minor.yy159, yymsp[-4].minor.yy258);} + case 286: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */ +{yygotominor.yy473 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy132);} break; - case 291: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */ -{yygotominor.yy327 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy122);} + case 287: /* trigger_cmd ::= select */ +{yygotominor.yy473 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy3); } break; - case 292: /* trigger_cmd ::= select */ -{yygotominor.yy327 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy159); } - break; - case 293: /* expr ::= RAISE LP IGNORE RP */ + case 288: /* expr ::= RAISE LP IGNORE RP */ { - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0); - if( yygotominor.yy342.pExpr ){ - yygotominor.yy342.pExpr->affinity = OE_Ignore; + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->affinity = OE_Ignore; } - yygotominor.yy342.zStart = yymsp[-3].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-3].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 294: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 289: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yygotominor.yy342.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0); - if( yygotominor.yy342.pExpr ) { - yygotominor.yy342.pExpr->affinity = (char)yymsp[-3].minor.yy392; + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0); + if( yygotominor.yy346.pExpr ) { + yygotominor.yy346.pExpr->affinity = (char)yymsp[-3].minor.yy328; } - yygotominor.yy342.zStart = yymsp[-5].minor.yy0.z; - yygotominor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-5].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 295: /* raisetype ::= ROLLBACK */ -{yygotominor.yy392 = OE_Rollback;} + case 290: /* raisetype ::= ROLLBACK */ +{yygotominor.yy328 = OE_Rollback;} break; - case 297: /* raisetype ::= FAIL */ -{yygotominor.yy392 = OE_Fail;} + case 292: /* raisetype ::= FAIL */ +{yygotominor.yy328 = OE_Fail;} break; - case 298: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 293: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy347,yymsp[-1].minor.yy392); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy65,yymsp[-1].minor.yy328); } break; - case 299: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 294: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy342.pExpr, yymsp[-1].minor.yy342.pExpr, yymsp[0].minor.yy122); + sqlite3Attach(pParse, yymsp[-3].minor.yy346.pExpr, yymsp[-1].minor.yy346.pExpr, yymsp[0].minor.yy132); } break; - case 300: /* cmd ::= DETACH database_kw_opt expr */ + case 295: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy342.pExpr); + sqlite3Detach(pParse, yymsp[0].minor.yy346.pExpr); } break; - case 305: /* cmd ::= REINDEX */ + case 300: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 306: /* cmd ::= REINDEX nm dbnm */ + case 301: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 307: /* cmd ::= ANALYZE */ + case 302: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 308: /* cmd ::= ANALYZE nm dbnm */ + case 303: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 309: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 304: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy347,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy65,&yymsp[0].minor.yy0); } break; - case 310: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */ + case 305: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */ { sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy0); } break; - case 311: /* add_column_fullname ::= fullname */ + case 306: /* add_column_fullname ::= fullname */ { pParse->db->lookaside.bEnabled = 0; - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy347); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy65); } break; - case 314: /* cmd ::= create_vtab */ + case 309: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 315: /* cmd ::= create_vtab LP vtabarglist RP */ + case 310: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 316: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 311: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy392); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy328); } break; - case 319: /* vtabarg ::= */ + case 314: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 321: /* vtabargtoken ::= ANY */ - case 322: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==322); - case 323: /* lp ::= LP */ yytestcase(yyruleno==323); + case 316: /* vtabargtoken ::= ANY */ + case 317: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==317); + case 318: /* lp ::= LP */ yytestcase(yyruleno==318); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; + case 322: /* with ::= */ +{yygotominor.yy59 = 0;} + break; + case 323: /* with ::= WITH wqlist */ + case 324: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==324); +{ yygotominor.yy59 = yymsp[0].minor.yy59; } + break; + case 325: /* wqlist ::= nm idxlist_opt AS LP select RP */ +{ + yygotominor.yy59 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy3); +} + break; + case 326: /* wqlist ::= wqlist COMMA nm idxlist_opt AS LP select RP */ +{ + yygotominor.yy59 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy59, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy3); +} + break; default: /* (0) input ::= cmdlist */ yytestcase(yyruleno==0); /* (1) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==1); @@ -116007,30 +117956,30 @@ static void yy_reduce( /* (20) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==20); /* (21) savepoint_opt ::= */ yytestcase(yyruleno==21); /* (25) cmd ::= create_table create_table_args */ yytestcase(yyruleno==25); - /* (34) columnlist ::= columnlist COMMA column */ yytestcase(yyruleno==34); - /* (35) columnlist ::= column */ yytestcase(yyruleno==35); - /* (44) type ::= */ yytestcase(yyruleno==44); - /* (51) signed ::= plus_num */ yytestcase(yyruleno==51); - /* (52) signed ::= minus_num */ yytestcase(yyruleno==52); - /* (53) carglist ::= carglist ccons */ yytestcase(yyruleno==53); - /* (54) carglist ::= */ yytestcase(yyruleno==54); - /* (61) ccons ::= NULL onconf */ yytestcase(yyruleno==61); - /* (89) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==89); - /* (90) conslist ::= tcons */ yytestcase(yyruleno==90); - /* (92) tconscomma ::= */ yytestcase(yyruleno==92); - /* (277) foreach_clause ::= */ yytestcase(yyruleno==277); - /* (278) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==278); - /* (285) tridxby ::= */ yytestcase(yyruleno==285); - /* (303) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==303); - /* (304) database_kw_opt ::= */ yytestcase(yyruleno==304); - /* (312) kwcolumn_opt ::= */ yytestcase(yyruleno==312); - /* (313) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==313); - /* (317) vtabarglist ::= vtabarg */ yytestcase(yyruleno==317); - /* (318) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==318); - /* (320) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==320); - /* (324) anylist ::= */ yytestcase(yyruleno==324); - /* (325) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==325); - /* (326) anylist ::= anylist ANY */ yytestcase(yyruleno==326); + /* (36) columnlist ::= columnlist COMMA column */ yytestcase(yyruleno==36); + /* (37) columnlist ::= column */ yytestcase(yyruleno==37); + /* (43) type ::= */ yytestcase(yyruleno==43); + /* (50) signed ::= plus_num */ yytestcase(yyruleno==50); + /* (51) signed ::= minus_num */ yytestcase(yyruleno==51); + /* (52) carglist ::= carglist ccons */ yytestcase(yyruleno==52); + /* (53) carglist ::= */ yytestcase(yyruleno==53); + /* (60) ccons ::= NULL onconf */ yytestcase(yyruleno==60); + /* (88) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==88); + /* (89) conslist ::= tcons */ yytestcase(yyruleno==89); + /* (91) tconscomma ::= */ yytestcase(yyruleno==91); + /* (273) foreach_clause ::= */ yytestcase(yyruleno==273); + /* (274) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==274); + /* (281) tridxby ::= */ yytestcase(yyruleno==281); + /* (298) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==298); + /* (299) database_kw_opt ::= */ yytestcase(yyruleno==299); + /* (307) kwcolumn_opt ::= */ yytestcase(yyruleno==307); + /* (308) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==308); + /* (312) vtabarglist ::= vtabarg */ yytestcase(yyruleno==312); + /* (313) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==313); + /* (315) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==315); + /* (319) anylist ::= */ yytestcase(yyruleno==319); + /* (320) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==320); + /* (321) anylist ::= anylist ANY */ yytestcase(yyruleno==321); break; }; assert( yyruleno>=0 && yyruleno1 ){ - /* Parameters of the form #NNN (where NNN is a number) are used - ** internally by sqlite3NestedParse. */ - *tokenType = TK_REGISTER; - return i; - } - /* Fall through into the next case if the '#' is not followed by - ** a digit. Try to match #AAAA where AAAA is a parameter name. */ - } #ifndef SQLITE_OMIT_TCL_VARIABLE case '$': #endif case '@': /* For compatibility with MS SQL Server */ + case '#': case ':': { int n = 0; - testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' ); + testcase( z[0]=='$' ); testcase( z[0]=='@' ); + testcase( z[0]==':' ); testcase( z[0]=='#' ); *tokenType = TK_VARIABLE; for(i=1; (c=z[i])!=0; i++){ if( IdChar(c) ){ @@ -117069,10 +119013,10 @@ abort_parse: sqlite3DeleteTable(db, pParse->pNewTable); } + if( pParse->bFreeWith ) sqlite3WithDelete(db, pParse->pWith); sqlite3DeleteTrigger(db, pParse->pNewTrigger); for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]); sqlite3DbFree(db, pParse->azVar); - sqlite3DbFree(db, pParse->aAlias); while( pParse->pAinc ){ AutoincInfo *p = pParse->pAinc; pParse->pAinc = p->pNext; @@ -117600,13 +119544,6 @@ SQLITE_API int sqlite3_initialize(void){ */ if( sqlite3GlobalConfig.isInit ) return SQLITE_OK; -#ifdef SQLITE_ENABLE_SQLLOG - { - extern void sqlite3_init_sqllog(void); - sqlite3_init_sqllog(); - } -#endif - /* Make sure the mutex subsystem is initialized. If unable to ** initialize the mutex subsystem, return early with the error. ** If the system is so sick that we are unable to allocate a mutex, @@ -117980,6 +119917,13 @@ SQLITE_API int sqlite3_config(int op, ...){ break; } +#if SQLITE_OS_WIN && defined(SQLITE_WIN32_MALLOC) + case SQLITE_CONFIG_WIN32_HEAPSIZE: { + sqlite3GlobalConfig.nHeap = va_arg(ap, int); + break; + } +#endif + default: { rc = SQLITE_ERROR; break; @@ -118046,7 +119990,8 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ db->lookaside.bEnabled = 1; db->lookaside.bMalloced = pBuf==0 ?1:0; }else{ - db->lookaside.pEnd = 0; + db->lookaside.pStart = db; + db->lookaside.pEnd = db; db->lookaside.bEnabled = 0; db->lookaside.bMalloced = 0; } @@ -118444,9 +120389,7 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ #endif sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */ - if( db->pErr ){ - sqlite3ValueFree(db->pErr); - } + sqlite3ValueFree(db->pErr); sqlite3CloseExtensions(db); db->magic = SQLITE_MAGIC_ERROR; @@ -118521,8 +120464,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ ** Return a static string containing the name corresponding to the error code ** specified in the argument. */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ - defined(SQLITE_DEBUG_OS_TRACE) +#if defined(SQLITE_TEST) SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ const char *zName = 0; int i, origRc = rc; @@ -118544,6 +120486,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break; case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break; case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break; + case SQLITE_READONLY_DBMOVED: zName = "SQLITE_READONLY_DBMOVED"; break; case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break; @@ -118600,6 +120543,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ case SQLITE_CONSTRAINT_VTAB: zName = "SQLITE_CONSTRAINT_VTAB"; break; case SQLITE_CONSTRAINT_FUNCTION: zName = "SQLITE_CONSTRAINT_FUNCTION"; break; + case SQLITE_CONSTRAINT_ROWID: zName = "SQLITE_CONSTRAINT_ROWID"; break; case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; @@ -118827,6 +120771,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc( ){ FuncDef *p; int nName; + int extraFlags; assert( sqlite3_mutex_held(db->mutex) ); if( zFunctionName==0 || @@ -118837,6 +120782,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc( (255<(nName = sqlite3Strlen30( zFunctionName))) ){ return SQLITE_MISUSE_BKPT; } + + assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC ); + extraFlags = enc & SQLITE_DETERMINISTIC; + enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY); #ifndef SQLITE_OMIT_UTF16 /* If SQLITE_UTF16 is specified as the encoding type, transform this @@ -118850,10 +120799,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc( enc = SQLITE_UTF16NATIVE; }else if( enc==SQLITE_ANY ){ int rc; - rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8, + rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags, pUserData, xFunc, xStep, xFinal, pDestructor); if( rc==SQLITE_OK ){ - rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE, + rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags, pUserData, xFunc, xStep, xFinal, pDestructor); } if( rc!=SQLITE_OK ){ @@ -118896,7 +120845,8 @@ SQLITE_PRIVATE int sqlite3CreateFunc( pDestructor->nRef++; } p->pDestructor = pDestructor; - p->funcFlags &= SQLITE_FUNC_ENCMASK; + p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags; + testcase( p->funcFlags & SQLITE_DETERMINISTIC ); p->xFunc = xFunc; p->xStep = xStep; p->xFinalize = xFinal; @@ -119326,6 +121276,7 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ if( db->mallocFailed ){ z = sqlite3ErrStr(SQLITE_NOMEM); }else{ + testcase( db->pErr==0 ); z = (char*)sqlite3_value_text(db->pErr); assert( !db->mallocFailed ); if( z==0 ){ @@ -119367,8 +121318,7 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){ }else{ z = sqlite3_value_text16(db->pErr); if( z==0 ){ - sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode), - SQLITE_UTF8, SQLITE_STATIC); + sqlite3Error(db, db->errCode, sqlite3ErrStr(db->errCode)); z = sqlite3_value_text16(db->pErr); } /* A malloc() may have failed within the call to sqlite3_value_text16() @@ -119415,6 +121365,32 @@ SQLITE_API const char *sqlite3_errstr(int rc){ return sqlite3ErrStr(rc); } +/* +** Invalidate all cached KeyInfo objects for database connection "db" +*/ +static void invalidateCachedKeyInfo(sqlite3 *db){ + Db *pDb; /* A single database */ + int iDb; /* The database index number */ + HashElem *k; /* For looping over tables in pDb */ + Table *pTab; /* A table in the database */ + Index *pIdx; /* Each index */ + + for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ + if( pDb->pBt==0 ) continue; + sqlite3BtreeEnter(pDb->pBt); + for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ + pTab = (Table*)sqliteHashData(k); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){ + sqlite3KeyInfoUnref(pIdx->pKeyInfo); + pIdx->pKeyInfo = 0; + } + } + } + sqlite3BtreeLeave(pDb->pBt); + } +} + /* ** Create a new collating function for database "db". The name is zName ** and the encoding is enc. @@ -119459,6 +121435,7 @@ static int createCollation( return SQLITE_BUSY; } sqlite3ExpirePreparedStatements(db); + invalidateCachedKeyInfo(db); /* If collation sequence pColl was created directly by a call to ** sqlite3_create_collation, and not generated by synthCollSeq(), @@ -120055,8 +122032,6 @@ static int openDatabase( } #endif - sqlite3Error(db, rc, 0); - /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking ** mode. Doing nothing at all also makes NORMAL the default. @@ -120067,6 +122042,8 @@ static int openDatabase( SQLITE_DEFAULT_LOCKING_MODE); #endif + if( rc ) sqlite3Error(db, rc, 0); + /* Enable the lookaside-malloc subsystem */ setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside, sqlite3GlobalConfig.nLookaside); @@ -120526,7 +122503,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){ ** to the xRandomness method of the default VFS. */ case SQLITE_TESTCTRL_PRNG_RESET: { - sqlite3PrngResetState(); + sqlite3_randomness(0,0); break; } @@ -120726,6 +122703,19 @@ SQLITE_API int sqlite3_test_control(int op, ...){ } #endif + /* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int); + ** + ** Set or clear a flag that indicates that the database file is always well- + ** formed and never corrupt. This flag is clear by default, indicating that + ** database files might have arbitrary corruption. Setting the flag during + ** testing causes certain assert() statements in the code to be activated + ** that demonstrat invariants on well-formed database files. + */ + case SQLITE_TESTCTRL_NEVER_CORRUPT: { + sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int); + break; + } + } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ @@ -122244,6 +124234,10 @@ struct Fts3MultiSegReader { SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); +#define fts3GetVarint32(p, piVal) ( \ + (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \ +) + /* fts3.c */ SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); @@ -122351,21 +124345,37 @@ SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){ return (int) (q - (unsigned char *)p); } +#define GETVARINT_STEP(v, ptr, shift, mask1, mask2, var, ret) \ + v = (v & mask1) | ( (*ptr++) << shift ); \ + if( (v & mask2)==0 ){ var = v; return ret; } +#define GETVARINT_INIT(v, ptr, shift, mask1, mask2, var, ret) \ + v = (*ptr++); \ + if( (v & mask2)==0 ){ var = v; return ret; } + /* ** Read a 64-bit variable-length integer from memory starting at p[0]. ** Return the number of bytes read, or 0 on error. ** The value is stored in *v. */ SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){ - const unsigned char *q = (const unsigned char *) p; - sqlite_uint64 x = 0, y = 1; - while( (*q&0x80)==0x80 && q-(unsigned char *)pestimatedRows variable to nRow. Unless this +** extension is currently being used by a version of SQLite too old to +** support estimatedRows. In that case this function is a no-op. +*/ +static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){ +#if SQLITE_VERSION_NUMBER>=3008002 + if( sqlite3_libversion_number()>=3008002 ){ + pIdxInfo->estimatedRows = nRow; + } +#endif +} + /* ** Implementation of the xBestIndex method for FTS3 tables. There ** are three possible strategies, in order of preference: @@ -123493,7 +125527,20 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ for(i=0; inConstraint; i++){ int bDocid; /* True if this constraint is on docid */ struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; - if( pCons->usable==0 ) continue; + if( pCons->usable==0 ){ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ + /* There exists an unusable MATCH constraint. This means that if + ** the planner does elect to use the results of this call as part + ** of the overall query plan the user will see an "unable to use + ** function MATCH in the requested context" error. To discourage + ** this, return a very high cost here. */ + pInfo->idxNum = FTS3_FULLSCAN_SEARCH; + pInfo->estimatedCost = 1e50; + fts3SetEstimatedRows(pInfo, ((sqlite3_int64)1) << 50); + return SQLITE_OK; + } + continue; + } bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1); @@ -123736,10 +125783,10 @@ static int fts3ScanInteriorNode( /* Load the next term on the node into zBuffer. Use realloc() to expand ** the size of zBuffer if required. */ if( !isFirstTerm ){ - zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix); + zCsr += fts3GetVarint32(zCsr, &nPrefix); } isFirstTerm = 0; - zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix); + zCsr += fts3GetVarint32(zCsr, &nSuffix); if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){ rc = FTS_CORRUPT_VTAB; @@ -123827,7 +125874,7 @@ static int fts3SelectLeaf( assert( piLeaf || piLeaf2 ); - sqlite3Fts3GetVarint32(zNode, &iHeight); + fts3GetVarint32(zNode, &iHeight); rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); @@ -124029,11 +126076,11 @@ static void fts3PoslistMerge( int iCol1; /* The current column index in pp1 */ int iCol2; /* The current column index in pp2 */ - if( *p1==POS_COLUMN ) sqlite3Fts3GetVarint32(&p1[1], &iCol1); + if( *p1==POS_COLUMN ) fts3GetVarint32(&p1[1], &iCol1); else if( *p1==POS_END ) iCol1 = POSITION_LIST_END; else iCol1 = 0; - if( *p2==POS_COLUMN ) sqlite3Fts3GetVarint32(&p2[1], &iCol2); + if( *p2==POS_COLUMN ) fts3GetVarint32(&p2[1], &iCol2); else if( *p2==POS_END ) iCol2 = POSITION_LIST_END; else iCol2 = 0; @@ -124126,11 +126173,11 @@ static int fts3PoslistPhraseMerge( assert( p!=0 && *p1!=0 && *p2!=0 ); if( *p1==POS_COLUMN ){ p1++; - p1 += sqlite3Fts3GetVarint32(p1, &iCol1); + p1 += fts3GetVarint32(p1, &iCol1); } if( *p2==POS_COLUMN ){ p2++; - p2 += sqlite3Fts3GetVarint32(p2, &iCol2); + p2 += fts3GetVarint32(p2, &iCol2); } while( 1 ){ @@ -124180,9 +126227,9 @@ static int fts3PoslistPhraseMerge( if( 0==*p1 || 0==*p2 ) break; p1++; - p1 += sqlite3Fts3GetVarint32(p1, &iCol1); + p1 += fts3GetVarint32(p1, &iCol1); p2++; - p2 += sqlite3Fts3GetVarint32(p2, &iCol2); + p2 += fts3GetVarint32(p2, &iCol2); } /* Advance pointer p1 or p2 (whichever corresponds to the smaller of @@ -124194,12 +126241,12 @@ static int fts3PoslistPhraseMerge( fts3ColumnlistCopy(0, &p1); if( 0==*p1 ) break; p1++; - p1 += sqlite3Fts3GetVarint32(p1, &iCol1); + p1 += fts3GetVarint32(p1, &iCol1); }else{ fts3ColumnlistCopy(0, &p2); if( 0==*p2 ) break; p2++; - p2 += sqlite3Fts3GetVarint32(p2, &iCol2); + p2 += fts3GetVarint32(p2, &iCol2); } } @@ -127366,7 +129413,7 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr){ pExpr->aMI[iCol*3 + 2] += (iCnt>0); if( *p==0x00 ) break; p++; - p += sqlite3Fts3GetVarint32(p, &iCol); + p += fts3GetVarint32(p, &iCol); } } @@ -127667,7 +129714,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( if( *pIter==0x01 ){ pIter++; - pIter += sqlite3Fts3GetVarint32(pIter, &iThis); + pIter += fts3GetVarint32(pIter, &iThis); }else{ iThis = 0; } @@ -127675,7 +129722,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( fts3ColumnlistCopy(0, &pIter); if( *pIter==0x00 ) return 0; pIter++; - pIter += sqlite3Fts3GetVarint32(pIter, &iThis); + pIter += fts3GetVarint32(pIter, &iThis); } *ppOut = ((iCol==iThis)?pIter:0); @@ -128441,6 +130488,11 @@ SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer( return rc; } +/* +** Function getNextNode(), which is called by fts3ExprParse(), may itself +** call fts3ExprParse(). So this forward declaration is required. +*/ +static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); /* ** Extract the next token from buffer z (length n) using the tokenizer @@ -128475,7 +130527,31 @@ static int getNextToken( int nByte; /* total space to allocate */ rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); - if( rc==SQLITE_OK ){ + + if( (rc==SQLITE_OK || rc==SQLITE_DONE) && sqlite3_fts3_enable_parentheses ){ + int i; + if( rc==SQLITE_DONE ) iStart = n; + for(i=0; inNest++; + rc = fts3ExprParse(pParse, &z[i+1], n-i-1, &pRet, &nConsumed); + if( rc==SQLITE_OK && !pRet ){ + rc = SQLITE_DONE; + } + nConsumed = (int)(i + 1 + nConsumed); + break; + } + + if( z[i]==')' ){ + rc = SQLITE_DONE; + pParse->nNest--; + nConsumed = i+1; + break; + } + } + } + + if( nConsumed==0 && rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; pRet = (Fts3Expr *)fts3MallocZero(nByte); if( !pRet ){ @@ -128655,12 +130731,6 @@ no_mem: return SQLITE_NOMEM; } -/* -** Function getNextNode(), which is called by fts3ExprParse(), may itself -** call fts3ExprParse(). So this forward declaration is required. -*/ -static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); - /* ** The output variable *ppExpr is populated with an allocated Fts3Expr ** structure, or set to 0 if the end of the input buffer is reached. @@ -128757,27 +130827,6 @@ static int getNextNode( } } - /* Check for an open bracket. */ - if( sqlite3_fts3_enable_parentheses ){ - if( *zInput=='(' ){ - int nConsumed; - pParse->nNest++; - rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed); - if( rc==SQLITE_OK && !*ppExpr ){ - rc = SQLITE_DONE; - } - *pnConsumed = (int)((zInput - z) + 1 + nConsumed); - return rc; - } - - /* Check for a close bracket. */ - if( *zInput==')' ){ - pParse->nNest--; - *pnConsumed = (int)((zInput - z) + 1); - return SQLITE_DONE; - } - } - /* See if we are dealing with a quoted phrase. If this is the case, then ** search for the closing quote and pass the whole string to getNextString() ** for processing. This is easy to do, as fts3 has no syntax for escaping @@ -129661,13 +131710,13 @@ SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash *pH){ */ static int fts3StrHash(const void *pKey, int nKey){ const char *z = (const char *)pKey; - int h = 0; + unsigned h = 0; if( nKey<=0 ) nKey = (int) strlen(z); while( nKey > 0 ){ h = (h<<3) ^ h ^ *z++; nKey--; } - return h & 0x7fffffff; + return (int)(h & 0x7fffffff); } static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){ if( n1!=n2 ) return 1; @@ -130352,12 +132401,14 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ /* Step 2 */ switch( z[1] ){ case 'a': - stem(&z, "lanoita", "ate", m_gt_0) || - stem(&z, "lanoit", "tion", m_gt_0); + if( !stem(&z, "lanoita", "ate", m_gt_0) ){ + stem(&z, "lanoit", "tion", m_gt_0); + } break; case 'c': - stem(&z, "icne", "ence", m_gt_0) || - stem(&z, "icna", "ance", m_gt_0); + if( !stem(&z, "icne", "ence", m_gt_0) ){ + stem(&z, "icna", "ance", m_gt_0); + } break; case 'e': stem(&z, "rezi", "ize", m_gt_0); @@ -130366,43 +132417,54 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ stem(&z, "igol", "log", m_gt_0); break; case 'l': - stem(&z, "ilb", "ble", m_gt_0) || - stem(&z, "illa", "al", m_gt_0) || - stem(&z, "iltne", "ent", m_gt_0) || - stem(&z, "ile", "e", m_gt_0) || - stem(&z, "ilsuo", "ous", m_gt_0); + if( !stem(&z, "ilb", "ble", m_gt_0) + && !stem(&z, "illa", "al", m_gt_0) + && !stem(&z, "iltne", "ent", m_gt_0) + && !stem(&z, "ile", "e", m_gt_0) + ){ + stem(&z, "ilsuo", "ous", m_gt_0); + } break; case 'o': - stem(&z, "noitazi", "ize", m_gt_0) || - stem(&z, "noita", "ate", m_gt_0) || - stem(&z, "rota", "ate", m_gt_0); + if( !stem(&z, "noitazi", "ize", m_gt_0) + && !stem(&z, "noita", "ate", m_gt_0) + ){ + stem(&z, "rota", "ate", m_gt_0); + } break; case 's': - stem(&z, "msila", "al", m_gt_0) || - stem(&z, "ssenevi", "ive", m_gt_0) || - stem(&z, "ssenluf", "ful", m_gt_0) || - stem(&z, "ssensuo", "ous", m_gt_0); + if( !stem(&z, "msila", "al", m_gt_0) + && !stem(&z, "ssenevi", "ive", m_gt_0) + && !stem(&z, "ssenluf", "ful", m_gt_0) + ){ + stem(&z, "ssensuo", "ous", m_gt_0); + } break; case 't': - stem(&z, "itila", "al", m_gt_0) || - stem(&z, "itivi", "ive", m_gt_0) || - stem(&z, "itilib", "ble", m_gt_0); + if( !stem(&z, "itila", "al", m_gt_0) + && !stem(&z, "itivi", "ive", m_gt_0) + ){ + stem(&z, "itilib", "ble", m_gt_0); + } break; } /* Step 3 */ switch( z[0] ){ case 'e': - stem(&z, "etaci", "ic", m_gt_0) || - stem(&z, "evita", "", m_gt_0) || - stem(&z, "ezila", "al", m_gt_0); + if( !stem(&z, "etaci", "ic", m_gt_0) + && !stem(&z, "evita", "", m_gt_0) + ){ + stem(&z, "ezila", "al", m_gt_0); + } break; case 'i': stem(&z, "itici", "ic", m_gt_0); break; case 'l': - stem(&z, "laci", "ic", m_gt_0) || - stem(&z, "luf", "", m_gt_0); + if( !stem(&z, "laci", "ic", m_gt_0) ){ + stem(&z, "luf", "", m_gt_0); + } break; case 's': stem(&z, "ssen", "", m_gt_0); @@ -130443,9 +132505,11 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ z += 3; } }else if( z[2]=='e' ){ - stem(&z, "tneme", "", m_gt_1) || - stem(&z, "tnem", "", m_gt_1) || - stem(&z, "tne", "", m_gt_1); + if( !stem(&z, "tneme", "", m_gt_1) + && !stem(&z, "tnem", "", m_gt_1) + ){ + stem(&z, "tne", "", m_gt_1); + } } } break; @@ -130464,8 +132528,9 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ } break; case 't': - stem(&z, "eta", "", m_gt_1) || - stem(&z, "iti", "", m_gt_1); + if( !stem(&z, "eta", "", m_gt_1) ){ + stem(&z, "iti", "", m_gt_1); + } break; case 'u': if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ @@ -133122,8 +135187,8 @@ static int fts3SegReaderNext( /* Because of the FTS3_NODE_PADDING bytes of padding, the following is ** safe (no risk of overread) even if the node data is corrupted. */ - pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); - pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); + pNext += fts3GetVarint32(pNext, &nPrefix); + pNext += fts3GetVarint32(pNext, &nSuffix); if( nPrefix<0 || nSuffix<=0 || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] ){ @@ -133146,7 +135211,7 @@ static int fts3SegReaderNext( memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix); pReader->nTerm = nPrefix+nSuffix; pNext += nSuffix; - pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist); + pNext += fts3GetVarint32(pNext, &pReader->nDoclist); pReader->aDoclist = pNext; pReader->pOffsetList = 0; @@ -134307,7 +136372,7 @@ static void fts3ColumnFilter( break; } p = &pList[1]; - p += sqlite3Fts3GetVarint32(p, &iCurrent); + p += fts3GetVarint32(p, &iCurrent); } if( bZero && &pList[nList]!=pEnd ){ @@ -135272,9 +137337,9 @@ static int nodeReaderNext(NodeReader *p){ p->aNode = 0; }else{ if( bFirst==0 ){ - p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nPrefix); + p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nPrefix); } - p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); + p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); if( rc==SQLITE_OK ){ @@ -135282,7 +137347,7 @@ static int nodeReaderNext(NodeReader *p){ p->term.n = nPrefix+nSuffix; p->iOff += nSuffix; if( p->iChild==0 ){ - p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); + p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); p->aDoclist = &p->aNode[p->iOff]; p->iOff += p->nDoclist; } @@ -136334,7 +138399,7 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){ pHint->n = i; i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel); - i += sqlite3Fts3GetVarint32(&pHint->a[i], pnInput); + i += fts3GetVarint32(&pHint->a[i], pnInput); if( i!=nHint ) return SQLITE_CORRUPT_VTAB; return SQLITE_OK; @@ -137327,7 +139392,7 @@ struct StrBuffer { */ static void fts3GetDeltaPosition(char **pp, int *piPos){ int iVal; - *pp += sqlite3Fts3GetVarint32(*pp, &iVal); + *pp += fts3GetVarint32(*pp, &iVal); *piPos += (iVal-2); } @@ -139620,6 +141685,16 @@ typedef union RtreeCoord RtreeCoord; */ #define HASHSIZE 128 +/* The xBestIndex method of this virtual table requires an estimate of +** the number of rows in the virtual table to calculate the costs of +** various strategies. If possible, this estimate is loaded from the +** sqlite_stat1 table (with RTREE_MIN_ROWEST as a hard-coded minimum). +** Otherwise, if no sqlite_stat1 entry is available, use +** RTREE_DEFAULT_ROWEST. +*/ +#define RTREE_DEFAULT_ROWEST 1048576 +#define RTREE_MIN_ROWEST 100 + /* ** An rtree virtual-table object. */ @@ -139634,6 +141709,7 @@ struct Rtree { char *zName; /* Name of r-tree table */ RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */ int nBusy; /* Current number of users of this structure */ + i64 nRowEst; /* Estimated number of rows in this table */ /* List of nodes removed during a CondenseTree operation. List is ** linked together via the pointer normally used for hash chains - @@ -140826,6 +142902,19 @@ static int rtreeFilter( return rc; } +/* +** Set the pIdxInfo->estimatedRows variable to nRow. Unless this +** extension is currently being used by a version of SQLite too old to +** support estimatedRows. In that case this function is a no-op. +*/ +static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){ +#if SQLITE_VERSION_NUMBER>=3008002 + if( sqlite3_libversion_number()>=3008002 ){ + pIdxInfo->estimatedRows = nRow; + } +#endif +} + /* ** Rtree virtual table module xBestIndex method. There are three ** table scan strategies to choose from (in order from most to @@ -140861,13 +142950,14 @@ static int rtreeFilter( ** is 'a', the second from the left 'b' etc. */ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + Rtree *pRtree = (Rtree*)tab; int rc = SQLITE_OK; int ii; + i64 nRow; /* Estimated rows returned by this scan */ int iIdx = 0; char zIdxStr[RTREE_MAX_DIMENSIONS*8+1]; memset(zIdxStr, 0, sizeof(zIdxStr)); - UNUSED_PARAMETER(tab); assert( pIdxInfo->idxStr==0 ); for(ii=0; iinConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){ @@ -140887,9 +142977,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ /* This strategy involves a two rowid lookups on an B-Tree structures ** and then a linear search of an R-Tree node. This should be ** considered almost as quick as a direct rowid lookup (for which - ** sqlite uses an internal cost of 0.0). + ** sqlite uses an internal cost of 0.0). It is expected to return + ** a single row. */ - pIdxInfo->estimatedCost = 10.0; + pIdxInfo->estimatedCost = 30.0; + setEstimatedRows(pIdxInfo, 1); return SQLITE_OK; } @@ -140918,8 +143010,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){ return SQLITE_NOMEM; } - assert( iIdx>=0 ); - pIdxInfo->estimatedCost = (2000000.0 / (double)(iIdx + 1)); + + nRow = pRtree->nRowEst / (iIdx + 1); + pIdxInfo->estimatedCost = (double)6.0 * (double)nRow; + setEstimatedRows(pIdxInfo, nRow); + return rc; } @@ -142394,6 +144489,37 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ return rc; } +/* +** This function populates the pRtree->nRowEst variable with an estimate +** of the number of rows in the virtual table. If possible, this is based +** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST. +*/ +static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ + const char *zSql = "SELECT stat FROM sqlite_stat1 WHERE tbl= ? || '_rowid'"; + sqlite3_stmt *p; + int rc; + i64 nRow = 0; + + rc = sqlite3_prepare_v2(db, zSql, -1, &p, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_text(p, 1, pRtree->zName, -1, SQLITE_STATIC); + if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0); + rc = sqlite3_finalize(p); + }else if( rc!=SQLITE_NOMEM ){ + rc = SQLITE_OK; + } + + if( rc==SQLITE_OK ){ + if( nRow==0 ){ + pRtree->nRowEst = RTREE_DEFAULT_ROWEST; + }else{ + pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST); + } + } + + return rc; +} + static sqlite3_module rtreeModule = { 0, /* iVersion */ rtreeCreate, /* xCreate - create a table */ @@ -142479,6 +144605,7 @@ static int rtreeSqlInit( appStmt[7] = &pRtree->pWriteParent; appStmt[8] = &pRtree->pDeleteParent; + rc = rtreeQueryStat1(db, pRtree); for(i=0; i **
  • The application must insure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. -**
  • The application must not close [database connection] specified by +**
  • The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. **
  • The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. @@ -447,7 +447,7 @@ SQLITE_API int sqlite3_exec( ** [sqlite3_extended_result_codes()] API. ** ** Some of the available extended result codes are listed here. -** One may expect the number of extended result codes will be expand +** One may expect the number of extended result codes will increase ** over time. Software that uses extended result codes should expect ** to see new result codes in future releases of SQLite. ** @@ -491,6 +491,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) +#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) #define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) #define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) #define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) @@ -501,6 +502,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) #define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) +#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) @@ -557,7 +559,8 @@ SQLITE_API int sqlite3_exec( ** after reboot following a crash or power loss, the only bytes in a ** file that were written at the application level might have changed ** and that adjacent bytes, even bytes within the same sector are -** guaranteed to be unchanged. +** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN +** flag indicate that a file cannot be deleted when open. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 @@ -788,15 +791,29 @@ struct sqlite3_io_methods { ** additional information. ** **
  • [[SQLITE_FCNTL_SYNC_OMITTED]] -** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by -** SQLite and sent to all VFSes in place of a call to the xSync method -** when the database connection has [PRAGMA synchronous] set to OFF.)^ -** Some specialized VFSes need this signal in order to operate correctly -** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most -** VFSes do not need this signal and should silently ignore this opcode. -** Applications should not call [sqlite3_file_control()] with this -** opcode as doing so may disrupt the operation of the specialized VFSes -** that do require it. +** No longer in use. +** +**
  • [[SQLITE_FCNTL_SYNC]] +** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and +** sent to the VFS immediately before the xSync method is invoked on a +** database file descriptor. Or, if the xSync method is not invoked +** because the user has configured SQLite with +** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place +** of the xSync method. In most cases, the pointer argument passed with +** this file-control is NULL. However, if the database file is being synced +** as part of a multi-database commit, the argument points to a nul-terminated +** string containing the transactions master-journal file name. VFSes that +** do not need this signal should silently ignore this opcode. Applications +** should not call [sqlite3_file_control()] with this opcode as doing so may +** disrupt the operation of the specialized VFSes that do require it. +** +**
  • [[SQLITE_FCNTL_COMMIT_PHASETWO]] +** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite +** and sent to the VFS after a transaction has been committed immediately +** but before the database is unlocked. VFSes that do not need this signal +** should silently ignore this opcode. Applications should not call +** [sqlite3_file_control()] with this opcode as doing so may disrupt the +** operation of the specialized VFSes that do require it. ** **
  • [[SQLITE_FCNTL_WIN32_AV_RETRY]] ** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic @@ -912,6 +929,20 @@ struct sqlite3_io_methods { ** can be queried by passing in a pointer to a negative number. This ** file-control is used internally to implement [PRAGMA mmap_size]. ** +**
  • [[SQLITE_FCNTL_TRACE]] +** The [SQLITE_FCNTL_TRACE] file control provides advisory information +** to the VFS about what the higher layers of the SQLite stack are doing. +** This file control is used by some VFS activity tracing [shims]. +** The argument is a zero-terminated string. Higher layers in the +** SQLite stack may generate instances of this file control if +** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled. +** +**
  • [[SQLITE_FCNTL_HAS_MOVED]] +** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a +** pointer to an integer and it writes a boolean into that integer depending +** on whether or not the file has been renamed, moved, or deleted since it +** was first opened. +** ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -931,6 +962,10 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_BUSYHANDLER 15 #define SQLITE_FCNTL_TEMPFILENAME 16 #define SQLITE_FCNTL_MMAP_SIZE 18 +#define SQLITE_FCNTL_TRACE 19 +#define SQLITE_FCNTL_HAS_MOVED 20 +#define SQLITE_FCNTL_SYNC 21 +#define SQLITE_FCNTL_COMMIT_PHASETWO 22 /* ** CAPI3REF: Mutex Handle @@ -1375,7 +1410,7 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); ** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, ** that causes the corresponding memory allocation to fail. ** -** The xInit method initializes the memory allocator. (For example, +** The xInit method initializes the memory allocator. For example, ** it might allocate any require mutexes or initialize internal data ** structures. The xShutdown method is invoked (indirectly) by ** [sqlite3_shutdown()] and should deallocate any resources acquired @@ -1677,6 +1712,13 @@ struct sqlite3_mem_methods { ** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^ ** ^If either argument to this option is negative, then that argument is ** changed to its compile-time default. +** +** [[SQLITE_CONFIG_WIN32_HEAPSIZE]] +**
    SQLITE_CONFIG_WIN32_HEAPSIZE +**
    ^This option is only available if SQLite is compiled for Windows +** with the [SQLITE_WIN32_MALLOC] pre-processor macro defined. +** SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value +** that specifies the maximum size of the created heap. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1701,6 +1743,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ +#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ /* ** CAPI3REF: Database Connection Configuration Options @@ -1777,19 +1820,21 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid ** -** ^Each entry in an SQLite table has a unique 64-bit signed +** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) +** has a unique 64-bit signed ** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those ** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** -** ^This routine returns the [rowid] of the most recent -** successful [INSERT] into the database from the [database connection] -** in the first argument. ^As of SQLite version 3.7.7, this routines -** records the last insert rowid of both ordinary tables and [virtual tables]. -** ^If no successful [INSERT]s -** have ever occurred on that database connection, zero is returned. +** ^The sqlite3_last_insert_rowid(D) interface returns the [rowid] of the +** most recent successful [INSERT] into a rowid table or [virtual table] +** on database connection D. +** ^Inserts into [WITHOUT ROWID] tables are not recorded. +** ^If no successful [INSERT]s into rowid tables +** have ever occurred on the database connection D, +** then sqlite3_last_insert_rowid(D) returns zero. ** ** ^(If an [INSERT] occurs within a trigger or within a [virtual table] ** method, then this routine will return the [rowid] of the inserted @@ -2355,11 +2400,13 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** applications to access the same PRNG for other purposes. ** ** ^A call to this routine stores N bytes of randomness into buffer P. +** ^If N is less than one, then P can be a NULL pointer. ** -** ^The first time this routine is invoked (either internally or by -** the application) the PRNG is seeded using randomness obtained -** from the xRandomness method of the default [sqlite3_vfs] object. -** ^On all subsequent invocations, the pseudo-randomness is generated +** ^If this routine has not been previously called or if the previous +** call had N less than one, then the PRNG is seeded using randomness +** obtained from the xRandomness method of the default [sqlite3_vfs] object. +** ^If the previous call to this routine had an N of 1 or more then +** the pseudo-randomness is generated ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ @@ -2519,6 +2566,7 @@ SQLITE_API int sqlite3_set_authorizer( #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ +#define SQLITE_RECURSIVE 33 /* NULL NULL */ /* ** CAPI3REF: Tracing And Profiling Functions @@ -3099,7 +3147,6 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. -** the **
  • ** */ @@ -3761,19 +3808,19 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** **
    NULL INTEGER Result is 0 **
    NULL FLOAT Result is 0.0 -**
    NULL TEXT Result is NULL pointer -**
    NULL BLOB Result is NULL pointer +**
    NULL TEXT Result is a NULL pointer +**
    NULL BLOB Result is a NULL pointer **
    INTEGER FLOAT Convert from integer to float **
    INTEGER TEXT ASCII rendering of the integer **
    INTEGER BLOB Same as INTEGER->TEXT -**
    FLOAT INTEGER Convert from float to integer +**
    FLOAT INTEGER [CAST] to INTEGER **
    FLOAT TEXT ASCII rendering of the float -**
    FLOAT BLOB Same as FLOAT->TEXT -**
    TEXT INTEGER Use atoi() -**
    TEXT FLOAT Use atof() +**
    FLOAT BLOB [CAST] to BLOB +**
    TEXT INTEGER [CAST] to INTEGER +**
    TEXT FLOAT [CAST] to REAL **
    TEXT BLOB No change -**
    BLOB INTEGER Convert to TEXT then use atoi() -**
    BLOB FLOAT Convert to TEXT then use atof() +**
    BLOB INTEGER [CAST] to INTEGER +**
    BLOB FLOAT [CAST] to REAL **
    BLOB TEXT Add a zero terminator if needed **
    ** )^ @@ -3829,7 +3876,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned -** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into +** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** ** ^(If a memory allocation error occurs during the evaluation of any @@ -3938,15 +3985,24 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** ** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for -** its parameters. Every SQL function implementation must be able to work -** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be -** more efficient with one encoding than another. ^An application may -** invoke sqlite3_create_function() or sqlite3_create_function16() multiple -** times with the same function but with different values of eTextRep. +** its parameters. The application should set this parameter to +** [SQLITE_UTF16LE] if the function implementation invokes +** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the +** implementation invokes [sqlite3_value_text16be()] on an input, or +** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8] +** otherwise. ^The same SQL function may be registered multiple times using +** different preferred text encodings, with different implementations for +** each encoding. ** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. -** If there is only a single implementation which does not care what text -** encoding is used, then the fourth argument should be [SQLITE_ANY]. +** +** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC] +** to signal that the function will always return the same result given +** the same inputs within a single SQL statement. Most SQL functions are +** deterministic. The built-in [random()] SQL function is an example of a +** function that is not deterministic. The SQLite query planner is able to +** perform additional optimizations on deterministic functions, so use +** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ @@ -4032,9 +4088,19 @@ SQLITE_API int sqlite3_create_function_v2( #define SQLITE_UTF16LE 2 #define SQLITE_UTF16BE 3 #define SQLITE_UTF16 4 /* Use native byte order */ -#define SQLITE_ANY 5 /* sqlite3_create_function only */ +#define SQLITE_ANY 5 /* Deprecated */ #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ +/* +** CAPI3REF: Function Flags +** +** These constants may be ORed together with the +** [SQLITE_UTF8 | preferred text encoding] as the fourth argument +** to [sqlite3_create_function()], [sqlite3_create_function16()], or +** [sqlite3_create_function_v2()]. +*/ +#define SQLITE_DETERMINISTIC 0x800 + /* ** CAPI3REF: Deprecated Functions ** DEPRECATED @@ -4806,12 +4872,13 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument -** to be invoked whenever a row is updated, inserted or deleted. +** to be invoked whenever a row is updated, inserted or deleted in +** a rowid table. ** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** ** ^The second argument is a pointer to the function to invoke when a -** row is updated, inserted or deleted. +** row is updated, inserted or deleted in a rowid table. ** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], @@ -4824,6 +4891,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ** ^(The update hook is not invoked when internal system tables are ** modified (i.e. sqlite_master and sqlite_sequence).)^ +** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. ** ** ^In the current implementation, the update hook ** is not invoked when duplication rows are deleted because of an @@ -4905,8 +4973,8 @@ SQLITE_API int sqlite3_release_memory(int); ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the -** [sqlite3_release_memory()] interface, this interface is effect even -** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** [sqlite3_release_memory()] interface, this interface is in effect even +** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is ** omitted. ** ** See also: [sqlite3_release_memory()] @@ -5281,10 +5349,22 @@ struct sqlite3_module { ** the correct order to satisfy the ORDER BY clause so that no separate ** sorting step is required. ** -** ^The estimatedCost value is an estimate of the cost of doing the -** particular lookup. A full scan of a table with N entries should have -** a cost of N. A binary search of a table of N entries should have a -** cost of approximately log(N). +** ^The estimatedCost value is an estimate of the cost of a particular +** strategy. A cost of N indicates that the cost of the strategy is similar +** to a linear scan of an SQLite table with N rows. A cost of log(N) +** indicates that the expense of the operation is similar to that of a +** binary search on a unique indexed field of an SQLite table with N rows. +** +** ^The estimatedRows value is an estimate of the number of rows that +** will be returned by the strategy. +** +** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info +** structure for SQLite version 3.8.2. If a virtual table extension is +** used with an SQLite version earlier than 3.8.2, the results of attempting +** to read or write the estimatedRows field are undefined (but are likely +** to included crashing the application). The estimatedRows field should +** therefore only be used if [sqlite3_libversion_number()] returns a +** value greater than or equal to 3008002. */ struct sqlite3_index_info { /* Inputs */ @@ -5309,7 +5389,9 @@ struct sqlite3_index_info { char *idxStr; /* String, possibly obtained from sqlite3_malloc */ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ int orderByConsumed; /* True if output is already ordered */ - double estimatedCost; /* Estimated cost of using this index */ + double estimatedCost; /* Estimated cost of using this index */ + /* Fields below are only available in SQLite 3.8.2 and later */ + sqlite3_int64 estimatedRows; /* Estimated number of rows returned */ }; /* @@ -5513,6 +5595,9 @@ typedef struct sqlite3_blob sqlite3_blob; ** interface. Use the [UPDATE] SQL command to change the size of a ** blob. ** +** ^The [sqlite3_blob_open()] interface will fail for a [WITHOUT ROWID] +** table. Incremental BLOB I/O is not possible on [WITHOUT ROWID] tables. +** ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function can be used, if desired, ** to create an empty, zero-filled blob in which to read or write using @@ -6036,7 +6121,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 -#define SQLITE_TESTCTRL_LAST 19 +#define SQLITE_TESTCTRL_NEVER_CORRUPT 20 +#define SQLITE_TESTCTRL_LAST 20 /* ** CAPI3REF: SQLite Runtime Status diff --git a/TMessagesProj/libs/armeabi-v7a/libtmessages.so b/TMessagesProj/libs/armeabi-v7a/libtmessages.so index 3d4c9c5c4..2cd5e4e54 100755 Binary files a/TMessagesProj/libs/armeabi-v7a/libtmessages.so and b/TMessagesProj/libs/armeabi-v7a/libtmessages.so differ diff --git a/TMessagesProj/libs/armeabi/libtmessages.so b/TMessagesProj/libs/armeabi/libtmessages.so index 66eb96004..adc126c73 100755 Binary files a/TMessagesProj/libs/armeabi/libtmessages.so and b/TMessagesProj/libs/armeabi/libtmessages.so differ diff --git a/TMessagesProj/libs/x86/libtmessages.so b/TMessagesProj/libs/x86/libtmessages.so index adeed3ba3..6375537c5 100755 Binary files a/TMessagesProj/libs/x86/libtmessages.so and b/TMessagesProj/libs/x86/libtmessages.so differ diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index 60ce4b986..fd7be431e 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="175" + android:versionName="1.3.26"> - + @@ -55,7 +55,7 @@ + + + + + + + + + + + + + + + @@ -165,9 +180,6 @@ - - - diff --git a/TMessagesProj/src/main/java/jawnae/pyronet/ByteStream.java b/TMessagesProj/src/main/java/jawnae/pyronet/ByteStream.java index 7803a35a0..319c1fe42 100755 --- a/TMessagesProj/src/main/java/jawnae/pyronet/ByteStream.java +++ b/TMessagesProj/src/main/java/jawnae/pyronet/ByteStream.java @@ -22,8 +22,6 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import jawnae.pyronet.PyroException; - public class ByteStream { private final List queue; @@ -51,9 +49,11 @@ public class ByteStream { public boolean hasData() { int size = this.queue.size(); - for (int i = 0; i < size; i++) - if (this.queue.get(i).hasRemaining()) + for (ByteBuffer aQueue : this.queue) { + if (aQueue.hasRemaining()) { return true; + } + } return false; } @@ -61,8 +61,9 @@ public class ByteStream { int size = this.queue.size(); int sum = 0; - for (int i = 0; i < size; i++) - sum += this.queue.get(i).remaining(); + for (ByteBuffer aQueue : this.queue) { + sum += aQueue.remaining(); + } return sum; } diff --git a/TMessagesProj/src/main/java/jawnae/pyronet/PyroClient.java b/TMessagesProj/src/main/java/jawnae/pyronet/PyroClient.java index b8d67078a..ea8bdd836 100755 --- a/TMessagesProj/src/main/java/jawnae/pyronet/PyroClient.java +++ b/TMessagesProj/src/main/java/jawnae/pyronet/PyroClient.java @@ -107,6 +107,7 @@ public class PyroClient { * set */ + @SuppressWarnings("unchecked") public T attachment() { return (T) this.attachment; } @@ -155,12 +156,6 @@ public class PyroClient { ((SocketChannel) key.channel()).socket().setKeepAlive(enabled); } - // - - // - - // - private boolean doEagerWrite = false; /** @@ -320,7 +315,7 @@ public class PyroClient { public void run() { try { if (key.channel().isOpen()) { - ((SocketChannel) key.channel()).close(); + (key.channel()).close(); } } catch (Exception exc) { selector().scheduleTask(this); @@ -340,7 +335,7 @@ public class PyroClient { public boolean isDisconnected() { this.selector.checkThread(); - return !((SocketChannel) this.key.channel()).isOpen(); + return !this.key.channel().isOpen(); } // @@ -368,9 +363,7 @@ public class PyroClient { private long lastEventTime; boolean didTimeout(long now) { - if (this.timeout == 0) - return false; // never timeout - return (now - this.lastEventTime) > this.timeout; + return this.timeout != 0 && (now - this.lastEventTime) > this.timeout; } private void onReadyToConnect(long now) throws IOException { @@ -442,7 +435,7 @@ public class PyroClient { try { // if the key is invalid, the channel may remain open!! - ((SocketChannel) this.key.channel()).close(); + this.key.channel().close(); } catch (Exception exc) { // type: java.io.IOException // message: @@ -488,7 +481,7 @@ public class PyroClient { + "]"; } - private final String getAddressText() { + private String getAddressText() { if (!this.key.channel().isOpen()) return "closed"; @@ -510,7 +503,7 @@ public class PyroClient { interested); } - static final SelectionKey bindAndConfigure(PyroSelector selector, + static SelectionKey bindAndConfigure(PyroSelector selector, SocketChannel channel, InetSocketAddress bind) throws IOException { selector.checkThread(); @@ -519,7 +512,7 @@ public class PyroClient { return configure(selector, channel, true); } - static final SelectionKey configure(PyroSelector selector, + static SelectionKey configure(PyroSelector selector, SocketChannel channel, boolean connect) throws IOException { selector.checkThread(); diff --git a/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientAdapter.java b/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientAdapter.java index 263afb16f..b089b9738 100755 --- a/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientAdapter.java +++ b/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientAdapter.java @@ -18,6 +18,9 @@ package jawnae.pyronet; +import org.telegram.messenger.ConnectionsManager; +import org.telegram.messenger.FileLog; + import java.io.IOException; import java.nio.ByteBuffer; @@ -27,13 +30,16 @@ public class PyroClientAdapter implements PyroClientListener { } public void unconnectableClient(PyroClient client, Exception cause) { - System.out.println("unconnectable"); + if (ConnectionsManager.DEBUG_VERSION) { + FileLog.e("tmessages", "unconnectable"); + } } public void droppedClient(PyroClient client, IOException cause) { if (cause != null) { - System.out.println(this.getClass().getSimpleName() - + ".droppedClient() caught exception: " + cause); + if (ConnectionsManager.DEBUG_VERSION) { + FileLog.e("tmessages", this.getClass().getSimpleName() + ".droppedClient() caught exception: " + cause); + } } } diff --git a/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientListener.java b/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientListener.java index 44ac3e45f..117cb1b83 100755 --- a/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientListener.java +++ b/TMessagesProj/src/main/java/jawnae/pyronet/PyroClientListener.java @@ -21,8 +21,6 @@ package jawnae.pyronet; import java.io.IOException; import java.nio.ByteBuffer; -import jawnae.pyronet.PyroClient; - public interface PyroClientListener { public void connectedClient(PyroClient client); diff --git a/TMessagesProj/src/main/java/jawnae/pyronet/PyroSelector.java b/TMessagesProj/src/main/java/jawnae/pyronet/PyroSelector.java index c460a8481..899111d86 100755 --- a/TMessagesProj/src/main/java/jawnae/pyronet/PyroSelector.java +++ b/TMessagesProj/src/main/java/jawnae/pyronet/PyroSelector.java @@ -75,14 +75,8 @@ public class PyroSelector { return copy; } - // - public final boolean isNetworkThread() { - if (DO_NOT_CHECK_NETWORK_THREAD) { - return true; - } - - return networkThread == Thread.currentThread(); + return DO_NOT_CHECK_NETWORK_THREAD || networkThread == Thread.currentThread(); } public final Thread networkThread() { @@ -95,8 +89,7 @@ public class PyroSelector { } if (!this.isNetworkThread()) { - throw new PyroException( - "call from outside the network-thread, you must schedule tasks"); + throw new PyroException("call from outside the network-thread, you must schedule tasks"); } } @@ -104,13 +97,8 @@ public class PyroSelector { return this.connect(host, null); } - public PyroClient connect(InetSocketAddress host, InetSocketAddress bind) - throws IOException { - try { - return new PyroClient(this, bind, host); - } catch (IOException exc) { - throw exc; - } + public PyroClient connect(InetSocketAddress host, InetSocketAddress bind) throws IOException { + return new PyroClient(this, bind, host); } public void select() { @@ -144,17 +132,16 @@ public class PyroSelector { } } - private final void performNioSelect(long timeout) { + private void performNioSelect(long timeout) { int selected; try { selected = nioSelector.select(timeout); } catch (IOException exc) { exc.printStackTrace(); - return; } } - private final void handleSelectedKeys(long now) { + private void handleSelectedKeys(long now) { Iterator keys = nioSelector.selectedKeys().iterator(); while (keys.hasNext()) { @@ -168,7 +155,7 @@ public class PyroSelector { } } - private final void handleSocketTimeouts(long now) { + private void handleSocketTimeouts(long now) { for (SelectionKey key: nioSelector.keys()) { if (key.channel() instanceof SocketChannel) { PyroClient client = (PyroClient) key.attachment(); diff --git a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/PhoneFormat.java b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/PhoneFormat.java index b3385a64b..4372a267e 100644 --- a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/PhoneFormat.java +++ b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/PhoneFormat.java @@ -58,9 +58,12 @@ public class PhoneFormat { return res.toString(); } - public static String stripExceptNumbers(String str) { + public static String stripExceptNumbers(String str, boolean includePlus) { StringBuilder res = new StringBuilder(str); String phoneChars = "0123456789"; + if (includePlus) { + phoneChars += "+"; + } for (int i = res.length() - 1; i >= 0; i--) { if (!phoneChars.contains(res.substring(i, i + 1))) { res.deleteCharAt(i); @@ -69,6 +72,10 @@ public class PhoneFormat { return res.toString(); } + public static String stripExceptNumbers(String str) { + return stripExceptNumbers(str, false); + } + public PhoneFormat() { init(null); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java b/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java new file mode 100644 index 000000000..69449af9e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AbsSerializedData.java @@ -0,0 +1,35 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.messenger; + +public abstract class AbsSerializedData { + public abstract void writeInt32(int x); + public abstract void writeInt64(long x); + public abstract void writeBool(boolean value); + public abstract void writeRaw(byte[] b); + public abstract void writeRaw(byte[] b, int offset, int count); + public abstract void writeByte(int i); + public abstract void writeByte(byte b); + public abstract void writeString(String s); + public abstract void writeByteArray(byte[] b, int offset, int count); + public abstract void writeByteArray(byte[] b); + public abstract void writeDouble(double d); + public abstract int readInt32(); + public abstract int readInt32(boolean[] error); + public abstract boolean readBool(); + public abstract long readInt64(); + public abstract long readInt64(boolean[] error); + public abstract void readRaw(byte[] b); + public abstract byte[] readData(int count); + public abstract String readString(); + public abstract byte[] readByteArray(); + public abstract ByteBufferDesc readByteBuffer(); + public abstract double readDouble(); + public abstract int length(); +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BackgroundService.java b/TMessagesProj/src/main/java/org/telegram/messenger/BackgroundService.java deleted file mode 100644 index d5e929230..000000000 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BackgroundService.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.3.x. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013. - */ - -package org.telegram.messenger; - -import android.app.Service; -import android.content.Intent; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.util.Log; - -public class BackgroundService extends Service { - - private Handler handler = new Handler(Looper.getMainLooper()); - private Runnable checkRunnable = new Runnable() { - @Override - public void run() { - check(); - } - }; - - public BackgroundService() { - - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onCreate() { - super.onCreate(); - check(); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - super.onStartCommand(intent, flags, startId); - Log.e("tmessages", "onStartCommand"); - return START_STICKY; - } - - private void check() { - handler.removeCallbacks(checkRunnable); - handler.postDelayed(checkRunnable, 1500); - ConnectionsManager connectionsManager = ConnectionsManager.Instance; - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.e("tmessages", "onDestroy"); - } -} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java new file mode 100644 index 000000000..7331962a1 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java @@ -0,0 +1,136 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.messenger; + +import java.util.concurrent.ConcurrentLinkedQueue; + +public class BuffersStorage { + public static BuffersStorage Instance = new BuffersStorage(); + + private final ConcurrentLinkedQueue freeBuffers128; + private final ConcurrentLinkedQueue freeBuffers1024; + private final ConcurrentLinkedQueue freeBuffers4096; + private final ConcurrentLinkedQueue freeBuffers16384; + private final ConcurrentLinkedQueue freeBuffers32768; + + public BuffersStorage() { + freeBuffers128 = new ConcurrentLinkedQueue(); + freeBuffers1024 = new ConcurrentLinkedQueue(); + freeBuffers4096 = new ConcurrentLinkedQueue(); + freeBuffers16384 = new ConcurrentLinkedQueue(); + freeBuffers32768 = new ConcurrentLinkedQueue(); + + for (int a = 0; a < 5; a++) { + freeBuffers128.add(new ByteBufferDesc(128)); + } + for (int a = 0; a < 5; a++) { + freeBuffers1024.add(new ByteBufferDesc(1024 + 200)); + } + for (int a = 0; a < 2; a++) { + freeBuffers4096.add(new ByteBufferDesc(4096 + 200)); + } + for (int a = 0; a < 2; a++) { + freeBuffers16384.add(new ByteBufferDesc(16384 + 200)); + } + for (int a = 0; a < 2; a++) { + freeBuffers32768.add(new ByteBufferDesc(40000)); + } + } + + public ByteBufferDesc getFreeBuffer(int size) { + ByteBufferDesc buffer = null; + if (size <= 128) { + synchronized (freeBuffers128) { + buffer = freeBuffers128.poll(); + } + if (buffer == null) { + buffer = new ByteBufferDesc(128); + FileLog.e("tmessages", "create new 128 buffer"); + } + } else if (size <= 1024 + 200) { + synchronized (freeBuffers1024) { + buffer = freeBuffers1024.poll(); + } + if (buffer == null) { + buffer = new ByteBufferDesc(1024 + 200); + FileLog.e("tmessages", "create new 1024 buffer"); + } + } else if (size <= 4096 + 200) { + synchronized (freeBuffers4096) { + buffer = freeBuffers4096.poll(); + } + if (buffer == null) { + buffer = new ByteBufferDesc(4096 + 200); + FileLog.e("tmessages", "create new 4096 buffer"); + } + } else if (size <= 16384 + 200) { + synchronized (freeBuffers16384) { + buffer = freeBuffers16384.poll(); + } + if (buffer == null) { + buffer = new ByteBufferDesc(16384 + 200); + FileLog.e("tmessages", "create new 16384 buffer"); + } + } else if (size <= 40000) { + synchronized (freeBuffers32768) { + buffer = freeBuffers32768.poll(); + } + if (buffer == null) { + buffer = new ByteBufferDesc(40000); + FileLog.e("tmessages", "create new 40000 buffer"); + } + } else { + buffer = new ByteBufferDesc(size); + } + buffer.buffer.limit(size).rewind(); + return buffer; + } + + public void reuseFreeBuffer(ByteBufferDesc buffer) { + if (buffer == null) { + return; + } + if (buffer.buffer.capacity() == 128) { + synchronized (freeBuffers128) { + if (freeBuffers128.contains(buffer)) { + throw new RuntimeException("already containing buffer! 0"); + } + freeBuffers128.add(buffer); + } + } else if (buffer.buffer.capacity() == 1024 + 200) { + synchronized (freeBuffers1024) { + if (freeBuffers1024.contains(buffer)) { + throw new RuntimeException("already containing buffer! 1"); + } + freeBuffers1024.add(buffer); + } + } else if (buffer.buffer.capacity() == 4096 + 200) { + synchronized (freeBuffers4096) { + if (freeBuffers4096.contains(buffer)) { + throw new RuntimeException("already containing buffer! 2"); + } + freeBuffers4096.add(buffer); + } + } else if (buffer.buffer.capacity() == 16384 + 200) { + synchronized (freeBuffers16384) { + if (freeBuffers16384.contains(buffer)) { + throw new RuntimeException("already containing buffer! 3"); + } + freeBuffers16384.add(buffer); + } + } else if (buffer.buffer.capacity() == 40000) { + synchronized (freeBuffers32768) { + if (freeBuffers32768.contains(buffer)) { + throw new RuntimeException("already containing buffer! 4"); + } + freeBuffers32768.add(buffer); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java b/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java new file mode 100644 index 000000000..ab672cc57 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ByteBufferDesc.java @@ -0,0 +1,384 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.messenger; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class ByteBufferDesc extends AbsSerializedData { + public ByteBuffer buffer; + private boolean justCalc = false; + private int len = 0; + + public ByteBufferDesc(int size) { + buffer = ByteBuffer.allocateDirect(size); + buffer.order(ByteOrder.LITTLE_ENDIAN); + } + + public ByteBufferDesc(boolean calculate) { + justCalc = calculate; + } + + public int position() { + return buffer.position(); + } + + public void position(int position) { + buffer.position(position); + } + + public int capacity() { + return buffer.capacity(); + } + + public int limit() { + return buffer.limit(); + } + + public void limit(int limit) { + buffer.limit(limit); + } + + public void put(ByteBuffer buff) { + buffer.put(buff); + } + + public void rewind() { + buffer.rewind(); + } + + public void compact() { + buffer.compact(); + } + + public boolean hasRemaining() { + return buffer.hasRemaining(); + } + + public void writeInt32(int x) { + try { + if (!justCalc) { + buffer.putInt(x); + } else { + len += 4; + } + } catch(Exception e) { + FileLog.e("tmessages", "write int32 error"); + } + } + + public void writeInt64(long x) { + try { + if (!justCalc) { + buffer.putLong(x); + } else { + len += 4; + } + } catch(Exception e) { + FileLog.e("tmessages", "write int64 error"); + } + } + + public void writeBool(boolean value) { + if (!justCalc) { + if (value) { + writeInt32(0x997275b5); + } else { + writeInt32(0xbc799737); + } + } else { + len += 4; + } + } + + public void writeRaw(byte[] b) { + try { + if (!justCalc) { + buffer.put(b); + } else { + len += b.length; + } + } catch (Exception x) { + FileLog.e("tmessages", "write raw error"); + } + } + + public void writeRaw(byte[] b, int offset, int count) { + try { + if (!justCalc) { + buffer.put(b, offset, count); + } else { + len += count; + } + } catch (Exception x) { + FileLog.e("tmessages", "write raw error"); + } + } + + public void writeByte(int i) { + writeByte((byte)i); + } + + public void writeByte(byte b) { + try { + if (!justCalc) { + buffer.put(b); + } else { + len += 1; + } + } catch (Exception e) { + FileLog.e("tmessages", "write byte error"); + } + } + + public void writeString(String s) { + try { + writeByteArray(s.getBytes("UTF-8")); + } catch(Exception x) { + FileLog.e("tmessages", "write string error"); + } + } + + public void writeByteArray(byte[] b, int offset, int count) { + try { + if(count <= 253) { + if (!justCalc) { + buffer.put((byte)count); + } else { + len += 1; + } + } else { + if (!justCalc) { + buffer.put((byte)254); + buffer.put((byte)count); + buffer.put((byte)(count >> 8)); + buffer.put((byte)(count >> 16)); + } else { + len += 4; + } + } + if (!justCalc) { + buffer.put(b, offset, count); + } else { + len += count; + } + int i = count <= 253 ? 1 : 4; + while ((count + i) % 4 != 0) { + if (!justCalc) { + buffer.put((byte)0); + } else { + len += 1; + } + i++; + } + } catch (Exception x) { + FileLog.e("tmessages", "write byte array error"); + } + } + + public void writeByteArray(byte[] b) { + try { + if (b.length <= 253) { + if (!justCalc) { + buffer.put((byte) b.length); + } else { + len += 1; + } + } else { + if (!justCalc) { + buffer.put((byte) 254); + buffer.put((byte) b.length); + buffer.put((byte) (b.length >> 8)); + buffer.put((byte) (b.length >> 16)); + } else { + len += 4; + } + } + if (!justCalc) { + buffer.put(b); + } else { + len += b.length; + } + int i = b.length <= 253 ? 1 : 4; + while((b.length + i) % 4 != 0) { + if (!justCalc) { + buffer.put((byte) 0); + } else { + len += 1; + } + i++; + } + } catch (Exception x) { + FileLog.e("tmessages", "write byte array error"); + } + } + + public void writeDouble(double d) { + try { + writeInt64(Double.doubleToRawLongBits(d)); + } catch(Exception x) { + FileLog.e("tmessages", "write double error"); + } + } + + public int readInt32() { + return readInt32(null); + } + + public int readInt32(boolean[] error) { + try { + int i = buffer.getInt(); + if (error != null) { + error[0] = false; + } + return i; + } catch (Exception x) { + if (error != null) { + error[0] = true; + } + FileLog.e("tmessages", "read int32 error"); + } + return 0; + } + + public boolean readBool() { + int consructor = readInt32(); + if (consructor == 0x997275b5) { + return true; + } else if (consructor == 0xbc799737) { + return false; + } + FileLog.e("tmessages", "Not bool value!"); + return false; + } + + public long readInt64() { + return readInt64(null); + } + + public long readInt64(boolean[] error) { + try { + long i = buffer.getLong(); + if (error != null) { + error[0] = false; + } + return i; + } catch (Exception x) { + if (error != null) { + error[0] = true; + } + FileLog.e("tmessages", "read int64 error"); + } + return 0; + } + + public void readRaw(byte[] b) { + try { + buffer.get(b); + } catch (Exception x) { + FileLog.e("tmessages", "read raw error"); + } + } + + public byte[] readData(int count) { + byte[] arr = new byte[count]; + readRaw(arr); + return arr; + } + + public String readString() { + try { + int sl = 1; + int l = getIntFromByte(buffer.get()); + if(l >= 254) { + l = getIntFromByte(buffer.get()) | (getIntFromByte(buffer.get()) << 8) | (getIntFromByte(buffer.get()) << 16); + sl = 4; + } + byte[] b = new byte[l]; + buffer.get(b); + int i = sl; + while((l + i) % 4 != 0) { + buffer.get(); + i++; + } + return new String(b, "UTF-8"); + } catch (Exception x) { + FileLog.e("tmessages", "read string error"); + } + return null; + } + + public int getIntFromByte(byte b) { + return b >= 0 ? b : ((int)b) + 256; + } + + public byte[] readByteArray() { + try { + int sl = 1; + int l = getIntFromByte(buffer.get()); + if (l >= 254) { + l = getIntFromByte(buffer.get()) | (getIntFromByte(buffer.get()) << 8) | (getIntFromByte(buffer.get()) << 16); + sl = 4; + } + byte[] b = new byte[l]; + buffer.get(b); + int i = sl; + while((l + i) % 4 != 0) { + buffer.get(); + i++; + } + return b; + } catch (Exception x) { + FileLog.e("tmessages", "read byte array error"); + } + return null; + } + + public ByteBufferDesc readByteBuffer() { + try { + int sl = 1; + int l = getIntFromByte(buffer.get()); + if (l >= 254) { + l = getIntFromByte(buffer.get()) | (getIntFromByte(buffer.get()) << 8) | (getIntFromByte(buffer.get()) << 16); + sl = 4; + } + ByteBufferDesc b = BuffersStorage.Instance.getFreeBuffer(l); + int old = buffer.limit(); + buffer.limit(buffer.position() + l); + b.buffer.put(buffer); + buffer.limit(old); + b.buffer.position(0); + int i = sl; + while((l + i) % 4 != 0) { + buffer.get(); + i++; + } + return b; + } catch (Exception x) { + FileLog.e("tmessages", "read byte array error"); + } + return null; + } + + public double readDouble() { + try { + return Double.longBitsToDouble(readInt64()); + } catch(Exception x) { + FileLog.e("tmessages", "read double error"); + } + return 0; + } + + public int length() { + if (!justCalc) { + return buffer.position(); + } + return len; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java index 940e82854..170343d72 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java @@ -16,9 +16,6 @@ import android.net.NetworkInfo; import android.os.Build; import android.util.Base64; -import org.telegram.TL.TLClassStore; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; import org.telegram.ui.ApplicationLoader; import java.io.File; @@ -37,7 +34,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static int APP_ID = 2458; public static String APP_HASH = "5bce48dc7d331e62c955669eb7233217"; public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; - public static boolean disableContactsImport = false; + public static String GCM_SENDER_ID = "760348033672"; private HashMap datacenters = new HashMap(); private HashMap> processedMessageIdsSet = new HashMap>(); @@ -67,7 +64,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public int timeDifference = 0; public int currentPingTime; private int lastDestroySessionRequestTime; - private final boolean isDebugSession = false; + public static final boolean isDebugSession = false; private boolean updatingDcSettings = false; private int updatingDcStartTime = 0; private int lastDcUpdateTime = 0; @@ -693,7 +690,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } object = invoke; } - TLRPC.invokeWithLayer11 invoke = new TLRPC.invokeWithLayer11(); + TLRPC.invokeWithLayer12 invoke = new TLRPC.invokeWithLayer12(); invoke.query = object; FileLog.d("wrap in layer", "" + object); return invoke; @@ -949,6 +946,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. Datacenter requestDatacenter = datacenterWithId(datacenterId); if (!request.initRequest && requestDatacenter.lastInitVersion != currentAppVersion) { request.rpcRequest = wrapInLayer(request.rawRequest, requestDatacenter.datacenterId, request); + SerializedData os = new SerializedData(true); + request.rpcRequest.serializeToStream(os); + request.serializedLength = os.length(); } if (requestDatacenter == null) { @@ -1182,9 +1182,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. Integer tokenIt = activeTransportTokens.get(requestDatacenter.datacenterId); request.transportChannelToken = tokenIt != null ? tokenIt : 0; } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { - if (uploadRunningRequestCount >= 20) - continue; - if (!haveNetwork) { FileLog.d("tmessages", "Don't have any network connection, skipping upload request"); continue; @@ -1573,12 +1570,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. TLRPC.TL_protoMessage message = networkMessage.protoMessage; if (DEBUG_VERSION) { - if (message.body instanceof TLRPC.invokeWithLayer11) { - FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)message.body).query); + if (message.body instanceof TLRPC.invokeWithLayer12) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer12)message.body).query); } else if (message.body instanceof TLRPC.initConnection) { TLRPC.initConnection r = (TLRPC.initConnection)message.body; - if (r.query instanceof TLRPC.invokeWithLayer11) { - FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)r.query).query); + if (r.query instanceof TLRPC.invokeWithLayer12) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer12)r.query).query); } else { FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query); } @@ -1613,12 +1610,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. TLRPC.TL_protoMessage message = networkMessage.protoMessage; containerMessages.add(message); if (DEBUG_VERSION) { - if (message.body instanceof TLRPC.invokeWithLayer11) { - FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)message.body).query); + if (message.body instanceof TLRPC.invokeWithLayer12) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer12)message.body).query); } else if (message.body instanceof TLRPC.initConnection) { TLRPC.initConnection r = (TLRPC.initConnection)message.body; - if (r.query instanceof TLRPC.invokeWithLayer11) { - FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)r.query).query); + if (r.query instanceof TLRPC.invokeWithLayer12) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer12)r.query).query); } else { FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query); } @@ -1672,11 +1669,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. dataForEncryption.writeByte(b[0]); } - byte[] encryptedData = Utilities.aesIgeEncryption(dataForEncryption.toByteArray(), keyData.aesKey, keyData.aesIv, true, false); + byte[] encryptedData = Utilities.aesIgeEncryption(dataForEncryption.toByteArray(), keyData.aesKey, keyData.aesIv, true, false, 0); try { - SerializedData data = new SerializedData(datacenter.authKeyId.length + messageKey.length + encryptedData.length); - data.writeRaw(datacenter.authKeyId); + SerializedData data = new SerializedData(8 + messageKey.length + encryptedData.length); + data.writeInt64(datacenter.authKeyId); data.writeRaw(messageKey); data.writeRaw(encryptedData); @@ -1687,7 +1684,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. messageData = null; System.gc(); SerializedData data = new SerializedData(); - data.writeRaw(datacenter.authKeyId); + data.writeInt64(datacenter.authKeyId); data.writeRaw(messageKey); data.writeRaw(encryptedData); @@ -1859,6 +1856,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. request.completionBlock.run(futureSalts, null); } + futureSalts.freeResources(); + messagesConfirmed(requestMid); rpcCompleted(requestMid); @@ -2072,6 +2071,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } } + resultContainer.freeResources(); + if (!found) { FileLog.d("tmessages", "Response received, but request wasn't found."); rpcCompleted(resultMid); @@ -2198,8 +2199,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. return null; } - TLRPC.TL_ping ping = new TLRPC.TL_ping(); + TLRPC.TL_ping_delay_disconnect ping = new TLRPC.TL_ping_delay_disconnect(); ping.ping_id = nextPingId++; + ping.disconnect_delay = 35; if (recordTime && sessionId == datacenter.authSessionId) { pingIdToDate.put(ping.ping_id, (int)(System.currentTimeMillis() / 1000)); @@ -2234,10 +2236,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. byte[] keyId = is.readData(8); SerializedData keyIdData = new SerializedData(keyId); - if (keyIdData.readInt64() == 0) { + long key = keyIdData.readInt64(); + if (key == 0) { return -1; } else { - if (datacenter.authKeyId == null || !Arrays.equals(keyId, datacenter.authKeyId)) { + if (datacenter.authKeyId == 0 || key != datacenter.authKeyId) { FileLog.e("tmessages", "Error: invalid auth key id " + connection); return -1; } @@ -2246,7 +2249,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); byte[] messageData = is.readData(data.length - 24); - messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); + messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false, 0); if (messageData == null) { return -1; @@ -2441,7 +2444,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } @Override - public void tcpConnectionReceivedData(TcpConnection connection, byte[] data) { + public void tcpConnectionReceivedData(TcpConnection connection, ByteBufferDesc data, int length) { if (connection.getDatacenterId() == currentDatacenterId && (connection.transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0) { if (connectionState == 1 || connectionState == 2) { connectionState = 3; @@ -2456,22 +2459,18 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); - SerializedData is = new SerializedData(data); - - byte[] keyId = is.readData(8); - SerializedData keyIdData = new SerializedData(keyId); - if (keyIdData.readInt64() == 0) { - long messageId = is.readInt64(); + long keyId = data.readInt64(); + if (keyId == 0) { + long messageId = data.readInt64(); if (isMessageIdProcessed(0, messageId)) { finishUpdatingState(connection); return; } - int messageLength = is.readInt32(); + int messageLength = data.readInt32(); + int constructor = data.readInt32(); - int constructor = is.readInt32(); - - TLObject object = TLClassStore.Instance().TLdeserialize(is, constructor, getRequestWithMessageId(messageId)); + TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, getRequestWithMessageId(messageId)); processMessage(object, messageId, 0, 0, connection, 0, 0, 0); @@ -2479,29 +2478,29 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. addProcessedMessageId(0, messageId); } } else { - if (datacenter.authKeyId == null || !Arrays.equals(keyId, datacenter.authKeyId)) { + if (datacenter.authKeyId == 0 || keyId != datacenter.authKeyId) { FileLog.e("tmessages", "Error: invalid auth key id " + connection); connection.suspendConnection(true); connection.connect(); return; } - byte[] messageKey = is.readData(16); + byte[] messageKey = data.readData(16); MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); + data.compact(); + data.limit(data.position()); + data.position(0); - byte[] messageData = is.readData(data.length - 24); + Utilities.aesIgeEncryption2(data.buffer, keyData.aesKey, keyData.aesIv, false, false, length - 24); +// if (messageData == null) { +// FileLog.e("tmessages", "Error: can't decrypt message data " + connection); +// connection.suspendConnection(true); +// connection.connect(); +// return; +// } - messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); - if (messageData == null) { - FileLog.e("tmessages", "Error: can't decrypt message data " + connection); - connection.suspendConnection(true); - connection.connect(); - return; - } - - SerializedData messageIs = new SerializedData(messageData); - long messageServerSalt = messageIs.readInt64(); - long messageSessionId = messageIs.readInt64(); + long messageServerSalt = data.readInt64(); + long messageSessionId = data.readInt64(); if (messageSessionId != datacenter.authSessionId && messageSessionId != datacenter.authDownloadSessionId && messageSessionId != datacenter.authUploadSessionId) { FileLog.e("tmessages", String.format("***** Error: invalid message session ID (%d instead of %d)", messageSessionId, datacenter.authSessionId)); @@ -2511,9 +2510,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. boolean doNotProcess = false; - long messageId = messageIs.readInt64(); - int messageSeqNo = messageIs.readInt32(); - int messageLength = messageIs.readInt32(); + long messageId = data.readInt64(); + int messageSeqNo = data.readInt32(); + int messageLength = data.readInt32(); if (isMessageIdProcessed(messageSessionId, messageId)) { doNotProcess = true; @@ -2528,7 +2527,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. set.add(messageId); } - byte[] realMessageKeyFull = Utilities.computeSHA1(messageData, 0, Math.min(messageLength + 32, messageData.length)); + byte[] realMessageKeyFull = Utilities.computeSHA1(data.buffer, 0, Math.min(messageLength + 32, data.limit())); if (realMessageKeyFull == null) { return; } @@ -2543,8 +2542,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } if (!doNotProcess) { - int constructor = messageIs.readInt32(); - TLObject message = TLClassStore.Instance().TLdeserialize(messageIs, constructor, getRequestWithMessageId(messageId)); + int constructor = data.readInt32(); + TLObject message = TLClassStore.Instance().TLdeserialize(data, constructor, getRequestWithMessageId(messageId)); if (message == null) { FileLog.e("tmessages", "***** Error parsing message: " + constructor); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index 85b649bf8..3dec709ea 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -21,11 +21,8 @@ import android.provider.ContactsContract; import android.util.SparseArray; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; import org.telegram.ui.ApplicationLoader; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -39,7 +36,6 @@ public class ContactsController { private boolean ignoreChanges = false; private boolean contactsSyncInProgress = false; private final Integer observerLock = 1; - private HashMap delayedDontactsToDelete = null; public boolean contactsLoaded = false; private boolean contactsBookLoaded = false; private ArrayList delayedContactsUpdate = new ArrayList(); @@ -54,7 +50,7 @@ public class ContactsController { public String last_name; } - private String[] projectioPhones = { + private String[] projectionPhones = { ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.TYPE, @@ -78,6 +74,8 @@ public class ContactsController { public HashMap> usersSectionsDict = new HashMap>(); public ArrayList sortedUsersSectionsArray = new ArrayList(); + public HashMap contactsByPhone = new HashMap(); + private class MyContentObserver extends ContentObserver { public MyContentObserver() { @@ -128,8 +126,8 @@ public class ContactsController { usersSectionsDict.clear(); sortedUsersSectionsArray.clear(); delayedContactsUpdate.clear(); + contactsByPhone.clear(); - delayedDontactsToDelete = null; loadingContacts = false; contactsSyncInProgress = false; contactsLoaded = false; @@ -169,14 +167,16 @@ public class ContactsController { } public void readContacts() { + if (loadingContacts) { + return; + } Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - if (contactsBook.size() != 0 || contactsSyncInProgress) { + if (!contacts.isEmpty() || contactsLoaded) { return; } - contactsSyncInProgress = true; - MessagesStorage.Instance.getCachedPhoneBook(); + loadContacts(true, false); } }); } @@ -185,8 +185,10 @@ public class ContactsController { HashMap contactsMap = new HashMap(); try { ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver(); + + HashMap shortContacts = new HashMap(); String ids = ""; - Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectioPhones, null, null, null); + Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectionPhones, null, null, null); if (pCur != null) { if (pCur.getCount() > 0) { while (pCur.moveToNext()) { @@ -194,10 +196,21 @@ public class ContactsController { if (number == null || number.length() == 0) { continue; } - number = PhoneFormat.stripExceptNumbers(number); + number = PhoneFormat.stripExceptNumbers(number, true); if (number.length() == 0) { continue; } + + String shortNumber = number; + + if (number.startsWith("+")) { + shortNumber = number.substring(1); + } + + if (shortContacts.containsKey(shortNumber)) { + continue; + } + Integer id = pCur.getInt(0); if (ids.length() != 0) { ids += ","; @@ -214,25 +227,9 @@ public class ContactsController { contactsMap.put(id, contact); } - boolean addNumber = true; - if (number.length() > 8) { - String shortNumber = number.substring(number.length() - 8); - if (contact.shortPhones.contains(shortNumber)) { - addNumber = false; - } else { - contact.shortPhones.add(shortNumber); - } - } else { - if (contact.shortPhones.contains(number)) { - addNumber = false; - } else { - contact.shortPhones.add(number); - } - } - if (addNumber) { - contact.phones.add(number); - contact.phoneDeleted.add(0); - } + contact.shortPhones.add(shortNumber); + contact.phones.add(number); + contact.phoneDeleted.add(0); if (type == ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) { contact.phoneTypes.add(pCur.getString(3)); @@ -247,6 +244,7 @@ public class ContactsController { } else { contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneOther)); } + shortContacts.put(shortNumber, contact); } } pCur.close(); @@ -284,6 +282,61 @@ public class ContactsController { } pCur.close(); } + + try { + pCur = cr.query(ContactsContract.RawContacts.CONTENT_URI, new String[] { "display_name", ContactsContract.RawContacts.SYNC1, ContactsContract.RawContacts.CONTACT_ID }, ContactsContract.RawContacts.ACCOUNT_TYPE + " = " + "'com.whatsapp'", null, null); + if (pCur != null) { + while ((pCur.moveToNext())) { + String phone = pCur.getString(1); + if (phone == null || phone.length() == 0) { + continue; + } + boolean withPlus = phone.startsWith("+"); + phone = Utilities.parseIntToString(phone); + if (phone == null || phone.length() == 0) { + continue; + } + String shortPhone = phone; + if (!withPlus) { + phone = "+" + phone; + } + + if (shortContacts.containsKey(shortPhone)) { + continue; + } + + String name = pCur.getString(0); + if (name == null || name.length() == 0) { + name = PhoneFormat.Instance.format(phone); + } + + String[] args = name.split(" ", 2); + + Contact contact = new Contact(); + if (args.length > 0) { + contact.first_name = args[0]; + } else { + contact.first_name = ""; + } + if (args.length > 1) { + contact.last_name = args[1]; + } else { + contact.last_name = ""; + } + contact.id = pCur.getInt(2); + contactsMap.put(contact.id, contact); + + contact.phoneDeleted.add(0); + contact.shortPhones.add(shortPhone); + contact.phones.add(phone); + contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneMobile)); + shortContacts.put(shortPhone, contact); + } + pCur.close(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } } catch (Exception e) { FileLog.e("tmessages", e); contactsMap.clear(); @@ -308,46 +361,73 @@ public class ContactsController { return ret; } - public void performSyncPhoneBook(final HashMap contactHashMap, final boolean requ, final boolean first) { + public void performSyncPhoneBook(final HashMap contactHashMap, final boolean requ, final boolean first, final boolean schedule) { + if (!first && !contactsBookLoaded) { + return; + } Utilities.globalQueue.postRunnable(new Runnable() { @Override public void run() { + + boolean disableDeletion = false; + if (schedule) { + try { + AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); + Account[] accounts = am.getAccountsByType("org.telegram.messenger.account"); + boolean recreateAccount = false; + if (UserConfig.currentUser != null) { + if (accounts.length != 1) { + FileLog.e("tmessages", "detected account deletion!"); + currentAccount = new Account(UserConfig.currentUser.phone, "org.telegram.messenger.account"); + am.addAccountExplicitly(currentAccount, "", null); + performWriteContactsToPhoneBookInternal(); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + disableDeletion = true; + } + } + boolean request = requ; if (request && first) { - if (UserConfig.contactsHash != null && UserConfig.contactsHash.length() != 0) { - UserConfig.contactsHash = ""; + if (UserConfig.importHash != null && UserConfig.importHash.length() != 0 || UserConfig.contactsVersion != 1) { + UserConfig.importHash = ""; + UserConfig.contactsVersion = 1; UserConfig.saveConfig(false); request = false; } } + HashMap contactShortHashMap = new HashMap(); + for (HashMap.Entry entry : contactHashMap.entrySet()) { + Contact c = entry.getValue(); + for (String sphone : c.shortPhones) { + contactShortHashMap.put(sphone, c); + } + } + FileLog.e("tmessages", "start read contacts from phone"); final HashMap contactsMap = readContactsFromPhoneBook(); final HashMap contactsBookShort = new HashMap(); int oldCount = contactHashMap.size(); - if (ConnectionsManager.disableContactsImport) { - if (requ && first) { - Utilities.stageQueue.postRunnable(new Runnable() { - @Override - public void run() { - contactsBookSPhones = contactsBookShort; - contactsBook = contactsMap; - contactsSyncInProgress = false; - contactsBookLoaded = true; - loadContacts(true); - } - }); - } - return; - } - ArrayList toImport = new ArrayList(); if (!contactHashMap.isEmpty()) { for (HashMap.Entry pair : contactsMap.entrySet()) { Integer id = pair.getKey(); Contact value = pair.getValue(); Contact existing = contactHashMap.get(id); + if (existing == null) { + for (String s : value.shortPhones) { + Contact c = contactShortHashMap.get(s); + if (c != null) { + existing = c; + id = existing.id; + break; + } + } + } if (existing == null || existing != null && (!existing.first_name.equals(value.first_name) || !existing.last_name.equals(value.last_name))) { for (int a = 0; a < value.phones.size(); a++) { @@ -364,8 +444,12 @@ public class ContactsController { } } if (request) { + if (contactsByPhone.containsKey(sphone)) { + continue; + } + TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact(); - imp.client_id = id; + imp.client_id = value.id; imp.first_name = value.first_name; imp.last_name = value.last_name; imp.phone = value.phones.get(a); @@ -382,8 +466,12 @@ public class ContactsController { int index = existing.shortPhones.indexOf(sphone); if (index == -1) { if (request) { + if (contactsByPhone.containsKey(sphone)) { + continue; + } + TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact(); - imp.client_id = id; + imp.client_id = value.id; imp.first_name = value.first_name; imp.last_name = value.last_name; imp.phone = value.phones.get(a); @@ -394,6 +482,7 @@ public class ContactsController { existing.phones.remove(index); existing.shortPhones.remove(index); existing.phoneDeleted.remove(index); + existing.phoneTypes.remove(index); } } if (existing.phones.isEmpty()) { @@ -409,19 +498,72 @@ public class ContactsController { if (toImport.isEmpty()) { MessagesStorage.Instance.putCachedPhoneBook(contactsMap); } - Utilities.stageQueue.postRunnable(new Runnable() { - @Override - public void run() { - delayedDontactsToDelete = contactHashMap; - FileLog.e("tmessages", "need delete contacts"); - } - }); + if (!disableDeletion && !contactHashMap.isEmpty()) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + if (ConnectionsManager.DEBUG_VERSION) { + FileLog.e("tmessages", "need delete contacts"); + for (HashMap.Entry c : contactHashMap.entrySet()) { + Contact contact = c.getValue(); + FileLog.e("tmessages", "delete contact " + contact.first_name + " " + contact.last_name); + for (String phone : contact.phones) { + FileLog.e("tmessages", phone); + } + } + } + + final ArrayList toDelete = new ArrayList(); + if (contactHashMap != null && !contactHashMap.isEmpty()) { + try { + final HashMap contactsPhonesShort = new HashMap(); + + for (TLRPC.TL_contact value : contacts) { + TLRPC.User user = MessagesController.Instance.users.get(value.user_id); + if (user == null || user.phone == null || user.phone.length() == 0) { + continue; + } + contactsPhonesShort.put(user.phone, user); + } + int removed = 0; + for (HashMap.Entry entry : contactHashMap.entrySet()) { + Contact contact = entry.getValue(); + boolean was = false; + for (int a = 0; a < contact.shortPhones.size(); a++) { + String phone = contact.shortPhones.get(a); + TLRPC.User user = contactsPhonesShort.get(phone); + if (user != null) { + was = true; + toDelete.add(user); + contact.shortPhones.remove(a); + a--; + } + } + if (!was || contact.shortPhones.size() == 0) { + removed++; + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + if (!toDelete.isEmpty()) { + deleteContact(toDelete); + } + } + }); + } } } else if (request) { for (HashMap.Entry pair : contactsMap.entrySet()) { Contact value = pair.getValue(); int id = pair.getKey(); for (int a = 0; a < value.phones.size(); a++) { + String phone = value.shortPhones.get(a); + if (contactsByPhone.containsKey(phone)) { + continue; + } TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact(); imp.client_id = id; imp.first_name = value.first_name; @@ -446,7 +588,12 @@ public class ContactsController { if (request) { if (!toImport.isEmpty()) { - FileLog.e("tmessages", "start import contacts"); + if (ConnectionsManager.DEBUG_VERSION) { + FileLog.e("tmessages", "start import contacts"); + for (TLRPC.TL_inputPhoneContact contact : toImport) { + FileLog.e("tmessages", "add contact " + contact.first_name + " " + contact.last_name + " " + contact.phone); + } + } TLRPC.TL_contacts_importContacts req = new TLRPC.TL_contacts_importContacts(); req.contacts = toImport; req.replace = false; @@ -472,23 +619,12 @@ public class ContactsController { } } }, null, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors | RPCRequest.RPCRequestClassCanCompress); - } else if (first) { - Utilities.stageQueue.postRunnable(new Runnable() { - @Override - public void run() { - loadContacts(true); - } - }); } else { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { updateUnregisteredContacts(contacts); NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded); - ArrayList toDelete = getContactsToDelete(contacts, MessagesController.Instance.users, 0); - if (!toDelete.isEmpty()) { - deleteContact(toDelete); - } } }); } @@ -496,20 +632,12 @@ public class ContactsController { if (!contactsMap.isEmpty()) { MessagesStorage.Instance.putCachedPhoneBook(contactsMap); } - if (first) { - Utilities.stageQueue.postRunnable(new Runnable() { - @Override - public void run() { - loadContacts(true); - } - }); - } } } }); } - public void loadContacts(boolean fromCache) { + public void loadContacts(boolean fromCache, boolean cacheEmpty) { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { @@ -522,14 +650,13 @@ public class ContactsController { } else { FileLog.e("tmessages", "load contacts from server"); TLRPC.TL_contacts_getContacts req = new TLRPC.TL_contacts_getContacts(); - req.hash = UserConfig.contactsHash; + req.hash = cacheEmpty ? "" : UserConfig.contactsHash; ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { TLRPC.contacts_Contacts res = (TLRPC.contacts_Contacts)response; if (res instanceof TLRPC.TL_contacts_contactsNotModified) { - delayedDontactsToDelete = null; contactsLoaded = true; if (!delayedContactsUpdate.isEmpty() && contactsLoaded && contactsBookLoaded) { applyContactsUpdates(delayedContactsUpdate, null, null, null); @@ -542,6 +669,7 @@ public class ContactsController { NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded); } }); + FileLog.e("tmessages", "load contacts don't change"); return; } processLoadedContacts(res.contacts, res.users, 0); @@ -551,208 +679,189 @@ public class ContactsController { } } - private ArrayList getContactsToDelete(ArrayList contactsArr, AbstractMap usersDict, int from) { - final ArrayList toDelete = new ArrayList(); - if (delayedDontactsToDelete != null && !delayedDontactsToDelete.isEmpty()) { - try { - final HashMap contactsPhonesShort = new HashMap(); - - for (TLRPC.TL_contact value : contactsArr) { - TLRPC.User user = usersDict.get(value.user_id); - if (user == null || user.phone == null || user.phone.length() == 0) { - continue; - } - if (user.phone.length() > 8) { - String shortPhone = user.phone.substring(user.phone.length() - 8); - contactsPhonesShort.put(shortPhone, user); + public void processLoadedContacts(final ArrayList contactsArr, final ArrayList usersArr, final int from) { + //from: 0 - from server, 1 - from db, 2 - from imported contacts + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + for (TLRPC.User user : usersArr) { + if (from == 1) { + MessagesController.Instance.users.putIfAbsent(user.id, user); } else { - contactsPhonesShort.put(user.phone, user); + MessagesController.Instance.users.put(user.id, user); + if (user.id == UserConfig.clientUserId) { + UserConfig.currentUser = user; + } } } - int removed = 0; - for (HashMap.Entry entry : delayedDontactsToDelete.entrySet()) { - Contact contact = entry.getValue(); - boolean was = false; - for (int a = 0; a < contact.shortPhones.size(); a++) { - String phone = contact.shortPhones.get(a); - TLRPC.User user = contactsPhonesShort.get(phone); - if (user != null) { - was = true; - toDelete.add(user); - contact.shortPhones.remove(a); + + if (!contacts.isEmpty()) { + for (int a = 0; a < contactsArr.size(); a++) { + TLRPC.TL_contact contact = contactsArr.get(a); + if (contactsDict.get(contact.user_id) != null) { + contactsArr.remove(a); a--; } } - if (!was || contact.shortPhones.size() == 0) { - removed++; - } - } - if (from != 2 || removed == delayedDontactsToDelete.size()) { - delayedDontactsToDelete = null; - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - return toDelete; - } - - public void processLoadedContacts(final ArrayList contactsArr, final ArrayList usersArr, final int from) { - //from: - //0 - from server - //1 - from db - //2 - from imported contacts - Utilities.stageQueue.postRunnable(new Runnable() { - @Override - public void run() { - FileLog.e("tmessages", "done loading contacts"); - if (from == 1 && contactsArr.isEmpty()) { - loadContacts(false); - return; - } - final HashMap usersDict = new HashMap(); - for (TLRPC.User user : usersArr) { - usersDict.put(user.id, user); - } - if (from == 1) { - for (TLRPC.TL_contact contact : contactsArr) { - if (usersDict.get(contact.user_id) == null) { - loadContacts(false); - FileLog.e("tmessages", "contacts are broken, load from server"); - return; - } - } + contactsArr.addAll(contacts); } - if (from == 0 || from == 2) { - MessagesStorage.Instance.putUsersAndChats(usersArr, null, true, true); - MessagesStorage.Instance.putContacts(contactsArr, true); - Collections.sort(contactsArr, new Comparator() { - @Override - public int compare(TLRPC.TL_contact tl_contact, TLRPC.TL_contact tl_contact2) { - if (tl_contact.user_id > tl_contact2.user_id) { - return 1; - } else if (tl_contact.user_id < tl_contact2.user_id) { - return -1; - } - return 0; - } - }); - String ids = ""; - for (TLRPC.TL_contact aContactsArr : contactsArr) { - if (ids.length() != 0) { - ids += ","; - } - ids += aContactsArr.user_id; - } - UserConfig.contactsHash = Utilities.MD5(ids); - UserConfig.saveConfig(false); - if (from == 2) { - loadContacts(false); - } - } - - Collections.sort(contactsArr, new Comparator() { - @Override - public int compare(TLRPC.TL_contact tl_contact, TLRPC.TL_contact tl_contact2) { - TLRPC.User user1 = usersDict.get(tl_contact.user_id); - TLRPC.User user2 = usersDict.get(tl_contact2.user_id); - String name1 = user1.first_name; - if (name1 == null || name1.length() == 0) { - name1 = user1.last_name; - } - String name2 = user2.first_name; - if (name2 == null || name2.length() == 0) { - name2 = user2.last_name; - } - return name1.compareTo(name2); - } - }); - - final SparseArray contactsDictionary = new SparseArray(); - final HashMap> sectionsDict = new HashMap>(); - final ArrayList sortedSectionsArray = new ArrayList(); - - for (TLRPC.TL_contact value : contactsArr) { - TLRPC.User user = usersDict.get(value.user_id); - if (user == null) { - continue; - } - contactsDictionary.put(value.user_id, value); - - String key = user.first_name; - if (key == null || key.length() == 0) { - key = user.last_name; - } - if (key.length() == 0) { - key = "#"; - } else { - key = key.toUpperCase(); - } - if (key.length() > 1) { - key = key.substring(0, 1); - } - ArrayList arr = sectionsDict.get(key); - if (arr == null) { - arr = new ArrayList(); - sectionsDict.put(key, arr); - sortedSectionsArray.add(key); - } - arr.add(value); - } - - Collections.sort(sortedSectionsArray, new Comparator() { - @Override - public int compare(String s, String s2) { - char cv1 = s.charAt(0); - char cv2 = s2.charAt(0); - if (cv1 == '#') { - return 1; - } else if (cv2 == '#') { - return -1; - } - return s.compareTo(s2); - } - }); - - final ArrayList toDelete = getContactsToDelete(contactsArr, usersDict, from); - - if (from != 2) { - contactsLoaded = true; - } - - if (!delayedContactsUpdate.isEmpty() && contactsLoaded && contactsBookLoaded) { - applyContactsUpdates(delayedContactsUpdate, null, null, null); - delayedContactsUpdate.clear(); - } - - Utilities.RunOnUIThread(new Runnable() { + Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - for (TLRPC.User user : usersArr) { - if (from == 1) { - MessagesController.Instance.users.putIfAbsent(user.id, user); - } else { - MessagesController.Instance.users.put(user.id, user); - if (user.id == UserConfig.clientUserId) { - UserConfig.currentUser = user; + FileLog.e("tmessages", "done loading contacts"); + if (from == 1 && contactsArr.isEmpty()) { + loadContacts(false, true); + return; + } + + if (from == 1) { + for (TLRPC.TL_contact contact : contactsArr) { + if (MessagesController.Instance.users.get(contact.user_id) == null && contact.user_id != UserConfig.clientUserId) { + loadContacts(false, true); + FileLog.e("tmessages", "contacts are broken, load from server"); + return; } } + } else { + MessagesStorage.Instance.putUsersAndChats(usersArr, null, true, true); + MessagesStorage.Instance.putContacts(contactsArr, from != 2); + Collections.sort(contactsArr, new Comparator() { + @Override + public int compare(TLRPC.TL_contact tl_contact, TLRPC.TL_contact tl_contact2) { + if (tl_contact.user_id > tl_contact2.user_id) { + return 1; + } else if (tl_contact.user_id < tl_contact2.user_id) { + return -1; + } + return 0; + } + }); + String ids = ""; + for (TLRPC.TL_contact aContactsArr : contactsArr) { + if (ids.length() != 0) { + ids += ","; + } + ids += aContactsArr.user_id; + } + UserConfig.contactsHash = Utilities.MD5(ids); + UserConfig.saveConfig(false); } - contacts = contactsArr; - contactsDict = contactsDictionary; - usersSectionsDict = sectionsDict; - sortedUsersSectionsArray = sortedSectionsArray; - if (from != 2) { - loadingContacts = false; + + Collections.sort(contactsArr, new Comparator() { + @Override + public int compare(TLRPC.TL_contact tl_contact, TLRPC.TL_contact tl_contact2) { + TLRPC.User user1 = MessagesController.Instance.users.get(tl_contact.user_id); + TLRPC.User user2 = MessagesController.Instance.users.get(tl_contact2.user_id); + String name1 = user1.first_name; + if (name1 == null || name1.length() == 0) { + name1 = user1.last_name; + } + String name2 = user2.first_name; + if (name2 == null || name2.length() == 0) { + name2 = user2.last_name; + } + return name1.compareTo(name2); + } + }); + + final SparseArray contactsDictionary = new SparseArray(); + final HashMap> sectionsDict = new HashMap>(); + final ArrayList sortedSectionsArray = new ArrayList(); + HashMap contactsByPhonesDict = null; + + if (!contactsBookLoaded) { + contactsByPhonesDict = new HashMap(); } - performWriteContactsToPhoneBook(); - updateUnregisteredContacts(contactsArr); - NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded); + final HashMap contactsByPhonesDictFinal = contactsByPhonesDict; - if (!toDelete.isEmpty()) { - deleteContact(toDelete); + for (TLRPC.TL_contact value : contactsArr) { + TLRPC.User user = MessagesController.Instance.users.get(value.user_id); + if (user == null) { + continue; + } + contactsDictionary.put(value.user_id, value); + if (contactsByPhonesDict != null) { + contactsByPhonesDict.put(user.phone, value); + } + + String key = user.first_name; + if (key == null || key.length() == 0) { + key = user.last_name; + } + if (key.length() == 0) { + key = "#"; + } else { + key = key.toUpperCase(); + } + if (key.length() > 1) { + key = key.substring(0, 1); + } + ArrayList arr = sectionsDict.get(key); + if (arr == null) { + arr = new ArrayList(); + sectionsDict.put(key, arr); + sortedSectionsArray.add(key); + } + arr.add(value); + } + + Collections.sort(sortedSectionsArray, new Comparator() { + @Override + public int compare(String s, String s2) { + char cv1 = s.charAt(0); + char cv2 = s2.charAt(0); + if (cv1 == '#') { + return 1; + } else if (cv2 == '#') { + return -1; + } + return s.compareTo(s2); + } + }); + + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + contacts = contactsArr; + contactsDict = contactsDictionary; + usersSectionsDict = sectionsDict; + sortedUsersSectionsArray = sortedSectionsArray; + if (from != 2) { + loadingContacts = false; + } + performWriteContactsToPhoneBook(); + updateUnregisteredContacts(contactsArr); + + NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded); + } + }); + + if (!delayedContactsUpdate.isEmpty() && contactsLoaded && contactsBookLoaded) { + applyContactsUpdates(delayedContactsUpdate, null, null, null); + delayedContactsUpdate.clear(); + } + + if (contactsByPhonesDictFinal != null) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + Utilities.globalQueue.postRunnable(new Runnable() { + @Override + public void run() { + contactsByPhone = contactsByPhonesDictFinal; + } + }); + if (contactsSyncInProgress) { + return; + } + contactsSyncInProgress = true; + MessagesStorage.Instance.getCachedPhoneBook(); + } + }); + } else { + contactsLoaded = true; } } }); @@ -768,12 +877,7 @@ public class ContactsController { if (user == null || user.phone == null || user.phone.length() == 0) { continue; } - if (user.phone.length() > 8) { - String shortPhone = user.phone.substring(user.phone.length() - 8); - contactsPhonesShort.put(shortPhone, value); - } else { - contactsPhonesShort.put(user.phone, value); - } + contactsPhonesShort.put(user.phone, value); } final HashMap> sectionsPhoneDict = new HashMap>(); @@ -926,33 +1030,34 @@ public class ContactsController { sortedUsersSectionsArray = sortedSectionsArray; } + private void performWriteContactsToPhoneBookInternal() { + try { + Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, currentAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, currentAccount.type).build(); + Cursor c1 = ApplicationLoader.applicationContext.getContentResolver().query(rawContactUri, new String[]{BaseColumns._ID, ContactsContract.RawContacts.SYNC2}, null, null, null); + HashMap bookContacts = new HashMap(); + if (c1 != null) { + while (c1.moveToNext()) { + bookContacts.put(c1.getInt(1), c1.getLong(0)); + } + c1.close(); + + for (TLRPC.TL_contact u : contacts) { + if (!bookContacts.containsKey(u.user_id)) { + TLRPC.User user = MessagesController.Instance.users.get(u.user_id); + addContactToPhoneBook(user, false); + } + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + private void performWriteContactsToPhoneBook() { Utilities.globalQueue.postRunnable(new Runnable() { @Override public void run() { - try { - if (ConnectionsManager.disableContactsImport) { - return; - } - Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, currentAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, currentAccount.type).build(); - Cursor c1 = ApplicationLoader.applicationContext.getContentResolver().query(rawContactUri, new String[]{BaseColumns._ID, ContactsContract.RawContacts.SYNC2}, null, null, null); - HashMap bookContacts = new HashMap(); - if (c1 != null) { - while (c1.moveToNext()) { - bookContacts.put(c1.getInt(1), c1.getLong(0)); - } - c1.close(); - - for (TLRPC.TL_contact u : contacts) { - if (!bookContacts.containsKey(u.user_id)) { - TLRPC.User user = MessagesController.Instance.users.get(u.user_id); - addContactToPhoneBook(user); - } - } - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } + performWriteContactsToPhoneBookInternal(); } }); } @@ -992,13 +1097,9 @@ public class ContactsController { continue; } - String phone = user.phone; - if (phone.length() > 8) { - phone = phone.substring(phone.length() - 8); - } - Contact contact = contactsBookSPhones.get(phone); + Contact contact = contactsBookSPhones.get(user.phone); if (contact != null) { - int index = contact.shortPhones.indexOf(phone); + int index = contact.shortPhones.indexOf(user.phone); if (index != -1) { contact.phoneDeleted.set(index, 0); } @@ -1006,7 +1107,7 @@ public class ContactsController { if (toAdd.length() != 0) { toAdd += ","; } - toAdd += phone; + toAdd += user.phone; } for (final Integer uid : contactsTD) { @@ -1032,13 +1133,9 @@ public class ContactsController { } if (user.phone != null && user.phone.length() > 0) { - String phone = user.phone; - if (phone.length() > 8) { - phone = phone.substring(phone.length() - 8); - } - Contact contact = contactsBookSPhones.get(phone); + Contact contact = contactsBookSPhones.get(user.phone); if (contact != null) { - int index = contact.shortPhones.indexOf(phone); + int index = contact.shortPhones.indexOf(user.phone); if (index != -1) { contact.phoneDeleted.set(index, 1); } @@ -1046,7 +1143,7 @@ public class ContactsController { if (toDelete.length() != 0) { toDelete += ","; } - toDelete += phone; + toDelete += user.phone; } } @@ -1058,7 +1155,7 @@ public class ContactsController { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - loadContacts(false); + loadContacts(false, true); } }); } else { @@ -1084,7 +1181,7 @@ public class ContactsController { updateUnregisteredContacts(contacts); performWriteContactsToPhoneBook(); } - performSyncPhoneBook(getContactsCopy(contactsBook), false, false); + performSyncPhoneBook(getContactsCopy(contactsBook), false, false, false); buildContactsSectionsArrays(!newContacts.isEmpty()); NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded); } @@ -1130,14 +1227,24 @@ public class ContactsController { } } - public long addContactToPhoneBook(TLRPC.User user) { - if (currentAccount == null || user == null || user.phone == null || user.phone.length() == 0 || ConnectionsManager.disableContactsImport) { + public long addContactToPhoneBook(TLRPC.User user, boolean check) { + if (currentAccount == null || user == null || user.phone == null || user.phone.length() == 0) { return -1; } long res = -1; synchronized (observerLock) { ignoreChanges = true; } + ContentResolver contentResolver = ApplicationLoader.applicationContext.getContentResolver(); + if (check) { + try { + Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, currentAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, currentAccount.type).build(); + int value = contentResolver.delete(rawContactUri, ContactsContract.RawContacts.SYNC2 + " = " + user.id, null); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + ArrayList query = new ArrayList(); ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI); @@ -1154,12 +1261,12 @@ public class ContactsController { builder.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, user.last_name); query.add(builder.build()); - builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI); - builder.withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, 0); - builder.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); - builder.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, "+" + user.phone); - builder.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); - query.add(builder.build()); +// builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI); +// builder.withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, 0); +// builder.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); +// builder.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, "+" + user.phone); +// builder.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); +// query.add(builder.build()); builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI); builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0); @@ -1170,7 +1277,7 @@ public class ContactsController { builder.withValue(ContactsContract.Data.DATA4, user.id); query.add(builder.build()); try { - ContentProviderResult[] result = ApplicationLoader.applicationContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, query); + ContentProviderResult[] result = contentResolver.applyBatch(ContactsContract.AUTHORITY, query); res = Long.parseLong(result[0].uri.getLastPathSegment()); } catch (Exception e) { FileLog.e("tmessages", e); @@ -1182,9 +1289,6 @@ public class ContactsController { } private void deleteContactFromPhoneBook(int uid) { - if (ConnectionsManager.disableContactsImport) { - return; - } ContentResolver contentResolver = ApplicationLoader.applicationContext.getContentResolver(); synchronized (observerLock) { ignoreChanges = true; @@ -1228,7 +1332,7 @@ public class ContactsController { Utilities.globalQueue.postRunnable(new Runnable() { @Override public void run() { - addContactToPhoneBook(u); + addContactToPhoneBook(u, true); } }); TLRPC.TL_contact newContact = new TLRPC.TL_contact(); @@ -1239,14 +1343,10 @@ public class ContactsController { if (u.phone != null && u.phone.length() > 0) { String name = Utilities.formatName(u.first_name, u.last_name); - String phone = u.phone; - if (phone.length() > 8) { - phone = phone.substring(phone.length() - 8); - } - MessagesStorage.Instance.applyPhoneBookUpdates(phone, ""); - Contact contact = contactsBookSPhones.get(phone); + MessagesStorage.Instance.applyPhoneBookUpdates(u.phone, ""); + Contact contact = contactsBookSPhones.get(u.phone); if (contact != null) { - int index = contact.shortPhones.indexOf(phone); + int index = contact.shortPhones.indexOf(u.phone); if (index != -1) { contact.phoneDeleted.set(index, 0); } @@ -1254,8 +1354,6 @@ public class ContactsController { } } - performSyncPhoneBook(getContactsCopy(contactsBook), false, false); - Utilities.RunOnUIThread(new Runnable() { @Override public void run() { @@ -1309,14 +1407,10 @@ public class ContactsController { for (TLRPC.User user : users) { if (user.phone != null && user.phone.length() > 0) { String name = Utilities.formatName(user.first_name, user.last_name); - String phone = user.phone; - if (phone.length() > 8) { - phone = phone.substring(phone.length() - 8); - } - MessagesStorage.Instance.applyPhoneBookUpdates(phone, ""); - Contact contact = contactsBookSPhones.get(phone); + MessagesStorage.Instance.applyPhoneBookUpdates(user.phone, ""); + Contact contact = contactsBookSPhones.get(user.phone); if (contact != null) { - int index = contact.shortPhones.indexOf(phone); + int index = contact.shortPhones.indexOf(user.phone); if (index != -1) { contact.phoneDeleted.set(index, 1); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java index 3953b7e59..d6dd088bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java @@ -11,7 +11,6 @@ package org.telegram.messenger; import android.content.Context; import android.content.SharedPreferences; -import org.telegram.TL.TLRPC; import org.telegram.ui.ApplicationLoader; import java.util.ArrayList; @@ -20,7 +19,7 @@ import java.util.Comparator; import java.util.HashMap; public class Datacenter { - private final int DATA_VERSION = 3; + private static final int DATA_VERSION = 4; public int datacenterId; public ArrayList addresses = new ArrayList(); @@ -31,7 +30,7 @@ public class Datacenter { public long authDownloadSessionId; public long authUploadSessionId; public byte[] authKey; - public byte[] authKeyId; + public long authKeyId; public int lastInitVersion = 0; private volatile int currentPortNum = 0; private volatile int currentAddressNum = 0; @@ -59,7 +58,7 @@ public class Datacenter { } len = data.readInt32(); if (len != 0) { - authKeyId = data.readData(len); + authKeyId = data.readInt64(); } authorized = data.readInt32() != 0; len = data.readInt32(); @@ -75,9 +74,9 @@ public class Datacenter { } } else if (version == 1) { int currentVersion = data.readInt32(); - if (currentVersion == 2 || currentVersion == 3) { + if (currentVersion == 2 || currentVersion == 3 || currentVersion == 4) { datacenterId = data.readInt32(); - if (currentVersion == 3) { + if (currentVersion >= 3) { lastInitVersion = data.readInt32(); } int len = data.readInt32(); @@ -91,9 +90,13 @@ public class Datacenter { if (len != 0) { authKey = data.readData(len); } - len = data.readInt32(); - if (len != 0) { - authKeyId = data.readData(len); + if (currentVersion == 4) { + authKeyId = data.readInt64(); + } else { + len = data.readInt32(); + if (len != 0) { + authKeyId = data.readInt64(); + } } authorized = data.readInt32() != 0; len = data.readInt32(); @@ -108,6 +111,8 @@ public class Datacenter { authServerSaltSet.add(salt); } } + } else if (version == 2) { + } readCurrentAddressAndPortNum(); } @@ -198,12 +203,7 @@ public class Datacenter { } else { stream.writeInt32(0); } - if (authKeyId != null) { - stream.writeInt32(authKeyId.length); - stream.writeRaw(authKeyId); - } else { - stream.writeInt32(0); - } + stream.writeInt64(authKeyId); stream.writeInt32(authorized ? 1 : 0); stream.writeInt32(authServerSaltSet.size()); for (ServerSalt salt : authServerSaltSet) { @@ -215,7 +215,7 @@ public class Datacenter { public void clear() { authKey = null; - authKeyId = null; + authKeyId = 0; authorized = false; authServerSaltSet.clear(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java index 355dd8512..1ded1deba 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java @@ -8,9 +8,6 @@ package org.telegram.messenger; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; - import java.util.HashMap; public class ExportAuthorizationAction extends Action { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index 5e79153f0..f0cdd28d5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -12,15 +12,13 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; - import java.io.RandomAccessFile; import java.net.URL; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URLConnection; +import java.nio.channels.FileChannel; import java.util.Scanner; public class FileLoadOperation { @@ -238,14 +236,18 @@ public class FileLoadOperation { opts.inSampleSize = (int)scaleFactor; } - opts.inPreferredConfig = Bitmap.Config.ARGB_8888; + if (filter == null) { + opts.inPreferredConfig = Bitmap.Config.ARGB_8888; + } else { + opts.inPreferredConfig = Bitmap.Config.RGB_565; + } opts.inDither = false; image = BitmapFactory.decodeStream(is, null, opts); is.close(); if (image == null) { - //if (!dontDelete) { - // cacheFileFinal.delete(); - //} + if (!dontDelete && cacheFileFinal.length() == 0) { + cacheFileFinal.delete(); + } } else { if (filter != null && image != null) { float bitmapW = image.getWidth(); @@ -278,9 +280,9 @@ public class FileLoadOperation { } }); } catch (Exception e) { - //if (!dontDelete) { - // cacheFileFinal.delete(); - //} + if (!dontDelete && cacheFileFinal.length() == 0) { + cacheFileFinal.delete(); + } FileLog.e("tmessages", e); } } @@ -436,7 +438,12 @@ public class FileLoadOperation { opts.inSampleSize = (int) scaleFactor; } - opts.inPreferredConfig = Bitmap.Config.ARGB_8888; + if (filter == null) { + opts.inPreferredConfig = Bitmap.Config.ARGB_8888; + } else { + opts.inPreferredConfig = Bitmap.Config.RGB_565; + } + opts.inDither = false; try { if (renamed) { @@ -558,13 +565,13 @@ public class FileLoadOperation { } TLRPC.TL_upload_getFile req = new TLRPC.TL_upload_getFile(); req.location = location; - if (totalBytesCount == -1) { - req.offset = 0; - req.limit = 0; - } else { + //if (totalBytesCount == -1) { + // req.offset = 0; + // req.limit = 0; + //} else { req.offset = downloadedBytes; req.limit = downloadChunkSize; - } + //} requestToken = ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { @@ -572,22 +579,22 @@ public class FileLoadOperation { if (error == null) { TLRPC.TL_upload_file res = (TLRPC.TL_upload_file)response; try { - if (res.bytes.length == 0) { + if (res.bytes.limit() == 0) { onFinishLoadingFile(); return; } if (key != null) { - res.bytes = Utilities.aesIgeEncryption(res.bytes, key, iv, false, true); + Utilities.aesIgeEncryption2(res.bytes.buffer, key, iv, false, true, res.bytes.limit()); } if (fileOutputStream != null) { - fileOutputStream.write(res.bytes); + FileChannel channel = fileOutputStream.getChannel(); + channel.write(res.bytes.buffer); } if (fiv != null) { fiv.seek(0); fiv.write(iv); } - downloadedBytes += res.bytes.length; - res.bytes = null; + downloadedBytes += res.bytes.limit(); if (totalBytesCount > 0) { delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float)downloadedBytes / (float)totalBytesCount)); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 1ff6dbd3b..b9cfaa617 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -16,7 +16,6 @@ import android.graphics.Matrix; import android.media.ExifInterface; import android.os.Build; -import org.telegram.TL.TLRPC; import org.telegram.objects.MessageObject; import org.telegram.ui.ApplicationLoader; import org.telegram.ui.Views.BackupImageView; @@ -45,6 +44,7 @@ public class FileLoader { private final int maxConcurentLoadingOpertaionsCount = 2; private Queue uploadOperationQueue; private ConcurrentHashMap uploadOperationPaths; + private ConcurrentHashMap uploadOperationPathsEnc; private int currentUploadOperationsCount = 0; private Queue loadOperationQueue; private ConcurrentHashMap loadOperationPaths; @@ -294,18 +294,27 @@ public class FileLoader { runningOperation = new LinkedList(); uploadOperationQueue = new LinkedList(); uploadOperationPaths = new ConcurrentHashMap(); + uploadOperationPathsEnc = new ConcurrentHashMap(); loadOperationPaths = new ConcurrentHashMap(); loadOperationQueue = new LinkedList(); } - public void cancelUploadFile(final String location) { + public void cancelUploadFile(final String location, final boolean enc) { Utilities.fileUploadQueue.postRunnable(new Runnable() { @Override public void run() { - FileUploadOperation operation = uploadOperationPaths.get(location); - if (operation != null) { - uploadOperationQueue.remove(operation); - operation.cancel(); + if (!enc) { + FileUploadOperation operation = uploadOperationPaths.get(location); + if (operation != null) { + uploadOperationQueue.remove(operation); + operation.cancel(); + } + } else { + FileUploadOperation operation = uploadOperationPathsEnc.get(location); + if (operation != null) { + uploadOperationQueue.remove(operation); + operation.cancel(); + } } } }); @@ -319,17 +328,39 @@ public class FileLoader { Utilities.fileUploadQueue.postRunnable(new Runnable() { @Override public void run() { + if (key != null) { + if (uploadOperationPathsEnc.containsKey(location)) { + return; + } + } else { + if (uploadOperationPaths.containsKey(location)) { + return; + } + } FileUploadOperation operation = new FileUploadOperation(location, key, iv); - uploadOperationPaths.put(location, operation); + if (key != null) { + uploadOperationPathsEnc.put(location, operation); + } else { + uploadOperationPaths.put(location, operation); + } operation.delegate = new FileUploadOperation.FileUploadOperationDelegate() { @Override public void didFinishUploadingFile(FileUploadOperation operation, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile) { - NotificationCenter.Instance.postNotificationName(FileDidUpload, location, inputFile, inputEncryptedFile); - fileProgresses.remove(location); Utilities.fileUploadQueue.postRunnable(new Runnable() { @Override public void run() { - uploadOperationPaths.remove(location); + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + NotificationCenter.Instance.postNotificationName(FileDidUpload, location, inputFile, inputEncryptedFile); + fileProgresses.remove(location); + } + }); + if (key != null) { + uploadOperationPathsEnc.remove(location); + } else { + uploadOperationPaths.remove(location); + } currentUploadOperationsCount--; if (currentUploadOperationsCount < 2) { FileUploadOperation operation = uploadOperationQueue.poll(); @@ -343,15 +374,24 @@ public class FileLoader { } @Override - public void didFailedUploadingFile(FileUploadOperation operation) { - fileProgresses.remove(location); - if (operation.state != 2) { - NotificationCenter.Instance.postNotificationName(FileDidFailUpload, location); - } + public void didFailedUploadingFile(final FileUploadOperation operation) { Utilities.fileUploadQueue.postRunnable(new Runnable() { @Override public void run() { - uploadOperationPaths.remove(location); + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + fileProgresses.remove(location); + if (operation.state != 2) { + NotificationCenter.Instance.postNotificationName(FileDidFailUpload, location, key != null); + } + } + }); + if (key != null) { + uploadOperationPathsEnc.remove(location); + } else { + uploadOperationPaths.remove(location); + } currentUploadOperationsCount--; if (currentUploadOperationsCount < 2) { FileUploadOperation operation = uploadOperationQueue.poll(); @@ -375,7 +415,7 @@ public class FileLoader { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { - NotificationCenter.Instance.postNotificationName(FileUploadProgressChanged, location, progress); + NotificationCenter.Instance.postNotificationName(FileUploadProgressChanged, location, progress, key != null); } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java index 9077a8a2e..46d08963b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java @@ -8,9 +8,6 @@ package org.telegram.messenger; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; - import java.io.File; import java.io.FileInputStream; import java.math.BigInteger; @@ -134,7 +131,7 @@ public class FileUploadOperation { } System.arraycopy(readBuffer, 0, sendBuffer, 0, readed); if (key != null) { - sendBuffer = Utilities.aesIgeEncryption(sendBuffer, key, iv, true, true); + sendBuffer = Utilities.aesIgeEncryption(sendBuffer, key, iv, true, true, 0); } mdEnc.update(sendBuffer, 0, readed + toAdd); if (isBigFile) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java index 9196e5649..3e614867b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java @@ -9,20 +9,50 @@ package org.telegram.messenger; import android.app.Activity; -import android.content.ComponentName; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.support.v4.content.WakefulBroadcastReceiver; +import android.os.PowerManager; -public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { +public class GcmBroadcastReceiver extends BroadcastReceiver { public static final int NOTIFICATION_ID = 1; @Override public void onReceive(final Context context, final Intent intent) { FileLog.d("tmessages", "GCM received intent: " + intent); - ComponentName comp = new ComponentName(context.getPackageName(), GcmService.class.getName()); - startWakefulService(context, (intent.setComponent(comp))); + + if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "lock"); + wl.acquire(); + +// SharedPreferences preferences = context.getSharedPreferences("Notifications", Context.MODE_PRIVATE); +// boolean globalEnabled = preferences.getBoolean("EnableAll", true); +// if (!globalEnabled) { +// FileLog.d("tmessages", "GCM disabled"); +// return; +// } + + Thread thread = new Thread(new Runnable() { + public void run() { + ConnectionsManager.Instance.resumeNetworkMaybe(); + wl.release(); + } + }); + thread.setPriority(Thread.MAX_PRIORITY); + thread.start(); + } else if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) { + String registration = intent.getStringExtra("registration_id"); + if (intent.getStringExtra("error") != null) { + FileLog.e("tmessages", "Registration failed, should try again later."); + } else if (intent.getStringExtra("unregistered") != null) { + FileLog.e("tmessages", "unregistration done, new messages from the authorized sender will be rejected"); + } else if (registration != null) { + FileLog.e("tmessages", "registration id = " + registration); + } + } + setResultCode(Activity.RESULT_OK); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/GcmService.java b/TMessagesProj/src/main/java/org/telegram/messenger/GcmService.java deleted file mode 100644 index c5639dbbf..000000000 --- a/TMessagesProj/src/main/java/org/telegram/messenger/GcmService.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.3.x. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013. - */ - -package org.telegram.messenger; - -import android.app.IntentService; -import android.content.Intent; - -public class GcmService extends IntentService { - - public GcmService() { - super("GcmService"); - } - - @Override - protected void onHandleIntent(Intent intent) { - if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { -// SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); -// boolean globalEnabled = preferences.getBoolean("EnableAll", true); -// if (!globalEnabled) { -// FileLog.d("tmessages", "GCM disabled"); -// return; -// } - ConnectionsManager.Instance.resumeNetworkMaybe(); - } else if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) { - String registration = intent.getStringExtra("registration_id"); - if (intent.getStringExtra("error") != null) { - FileLog.e("tmessages", "Registration failed, should try again later."); - } else if (intent.getStringExtra("unregistered") != null) { - FileLog.e("tmessages", "unregistration done, new messages from the authorized sender will be rejected"); - } else if (registration != null) { - FileLog.e("tmessages", "registration id = " + registration); - } - } - GcmBroadcastReceiver.completeWakefulIntent(intent); - } -} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java index 46a401137..82679649b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java @@ -8,12 +8,9 @@ package org.telegram.messenger; -import org.telegram.TL.TLClassStore; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; - import java.math.BigInteger; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -28,7 +25,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti private byte[] authNewNonce; private byte[] authKey; - private byte[] authKeyId; + private long authKeyId; private boolean processedPQRes; @@ -64,7 +61,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti authServerNonce = null; authNewNonce = null; authKey = null; - authKeyId = null; + authKeyId = 0; processedPQRes = false; reqPQMsgData = null; reqDHMsgData = null; @@ -325,7 +322,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti System.arraycopy(authNewNonce, 0, newNonce0_4, 0, 4); tmpAesIv.writeRaw(newNonce0_4); - byte[] answerWithHash = Utilities.aesIgeEncryption(serverDhParams.encrypted_answer, tmpAesKey.toByteArray(), tmpAesIv.toByteArray(), false, false); + byte[] answerWithHash = Utilities.aesIgeEncryption(serverDhParams.encrypted_answer, tmpAesKey.toByteArray(), tmpAesIv.toByteArray(), false, false, 0); byte[] answerHash = new byte[20]; System.arraycopy(answerWithHash, 0, answerHash, 0, 20); @@ -401,8 +398,11 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti authKey = correctedAuth; } byte[] authKeyHash = Utilities.computeSHA1(authKey); - authKeyId = new byte[8]; - System.arraycopy(authKeyHash, authKeyHash.length - 8, authKeyId, 0, 8); + byte[] authKeyArr = new byte[8]; + System.arraycopy(authKeyHash, authKeyHash.length - 8, authKeyArr, 0, 8); + ByteBuffer buffer = ByteBuffer.wrap(authKeyArr); + buffer.order(ByteOrder.LITTLE_ENDIAN); + authKeyId = buffer.getLong(); SerializedData serverSaltData = new SerializedData(); for (int i = 7; i >= 0; i--) { @@ -443,7 +443,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti TLRPC.TL_set_client_DH_params setClientDhParams = new TLRPC.TL_set_client_DH_params(); setClientDhParams.nonce = authNonce; setClientDhParams.server_nonce = authServerNonce; - setClientDhParams.encrypted_data = Utilities.aesIgeEncryption(clientDataWithHash.toByteArray(), tmpAesKey.toByteArray(), tmpAesIv.toByteArray(), true, false); + setClientDhParams.encrypted_data = Utilities.aesIgeEncryption(clientDataWithHash.toByteArray(), tmpAesKey.toByteArray(), tmpAesIv.toByteArray(), true, false, 0); TLRPC.TL_msgs_ack msgsAck = new TLRPC.TL_msgs_ack(); msgsAck.msg_ids = new ArrayList(); @@ -590,21 +590,20 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti } @Override - public void tcpConnectionReceivedData(TcpConnection connection, byte[] data) { - SerializedData is = new SerializedData(data); + public void tcpConnectionReceivedData(TcpConnection connection, ByteBufferDesc data, int length) { - long keyId = is.readInt64(); + long keyId = data.readInt64(); if (keyId == 0) { - long messageId = is.readInt64(); + long messageId = data.readInt64(); if (processedMessageIds.contains(messageId)) { FileLog.d("tmessages", String.format("===== Duplicate message id %d received, ignoring", messageId)); return; } - int messageLength = is.readInt32(); + int messageLength = data.readInt32(); - int constructor = is.readInt32(); - TLObject object = TLClassStore.Instance().TLdeserialize(is, constructor); + int constructor = data.readInt32(); + TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor); if (object != null) { processedMessageIds.add(messageId); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java new file mode 100644 index 000000000..c712ba34d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -0,0 +1,454 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.messenger; + +import android.content.Context; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.MediaRecorder; +import android.os.Vibrator; + +import org.telegram.objects.MessageObject; +import org.telegram.ui.ApplicationLoader; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Timer; +import java.util.TimerTask; + +public class MediaController implements NotificationCenter.NotificationCenterDelegate { + + public static interface FileDownloadProgressListener { + public void onFailedDownload(String fileName); + public void onSuccessDownload(String fileName); + public void onProgressDownload(String fileName, float progress); + public int getObserverTag(); + } + + public final static int audioProgressDidChanged = 50001; + public final static int audioDidReset = 50002; + public final static int recordProgressChanged = 50003; + + public static MediaController Instance = new MediaController(); + + private HashMap>> loadingFileObservers = new HashMap>>(); + private HashMap observersByTag = new HashMap(); + private boolean listenerInProgress = false; + private HashMap addLaterArray = new HashMap(); + private ArrayList deleteLaterArray = new ArrayList(); + + private boolean isPaused = false; + private MediaPlayer audioPlayer = null; + private int lastProgress = 0; + private MessageObject playingMessageObject; + + private MediaRecorder audioRecorder = null; + private TLRPC.TL_audio recordingAudio = null; + private File recordingAudioFile = null; + private long recordStartTime; + private long recordDialogId; + + private final Integer sync = 1; + + private int lastTag = 0; + + public MediaController () { + NotificationCenter.Instance.addObserver(this, FileLoader.FileDidFailedLoad); + NotificationCenter.Instance.addObserver(this, FileLoader.FileDidLoaded); + NotificationCenter.Instance.addObserver(this, FileLoader.FileLoadProgressChanged); + + Timer progressTimer = new Timer(); + progressTimer.schedule(new TimerTask() { + @Override + public void run() { + synchronized (sync) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + if (playingMessageObject != null && audioPlayer != null && !isPaused) { + try { + int progress = audioPlayer.getCurrentPosition(); + if (progress <= lastProgress) { + return; + } + lastProgress = progress; + final float value = (float)lastProgress / (float)audioPlayer.getDuration(); + playingMessageObject.audioProgress = value; + playingMessageObject.audioProgressSec = lastProgress / 1000; + NotificationCenter.Instance.postNotificationName(audioProgressDidChanged, playingMessageObject.messageOwner.id, value); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + if (audioRecorder != null) { + NotificationCenter.Instance.postNotificationName(recordProgressChanged, System.currentTimeMillis() - recordStartTime); + } + } + }); + } + } + }, 100, 17); + } + + public void cleanup() { + clenupPlayer(false); + } + + public int generateObserverTag() { + return lastTag++; + } + + public void addLoadingFileObserver(String fileName, FileDownloadProgressListener observer) { + if (listenerInProgress) { + addLaterArray.put(fileName, observer); + return; + } + removeLoadingFileObserver(observer); + + ArrayList> arrayList = loadingFileObservers.get(fileName); + if (arrayList == null) { + arrayList = new ArrayList>(); + loadingFileObservers.put(fileName, arrayList); + } + arrayList.add(new WeakReference(observer)); + + observersByTag.put(observer.getObserverTag(), fileName); + } + + public void removeLoadingFileObserver(FileDownloadProgressListener observer) { + if (listenerInProgress) { + deleteLaterArray.add(observer); + return; + } + String fileName = observersByTag.get(observer.getObserverTag()); + if (fileName != null) { + ArrayList> arrayList = loadingFileObservers.get(fileName); + if (arrayList != null) { + for (int a = 0; a < arrayList.size(); a++) { + WeakReference reference = arrayList.get(a); + if (reference.get() == null || reference.get() == observer) { + arrayList.remove(a); + a--; + } + } + if (arrayList.isEmpty()) { + loadingFileObservers.remove(fileName); + } + } + observersByTag.remove(observer.getObserverTag()); + } + } + + private void processLaterArrays() { + for (HashMap.Entry listener : addLaterArray.entrySet()) { + addLoadingFileObserver(listener.getKey(), listener.getValue()); + } + addLaterArray.clear(); + for (FileDownloadProgressListener listener : deleteLaterArray) { + removeLoadingFileObserver(listener); + } + deleteLaterArray.clear(); + } + + @Override + public void didReceivedNotification(int id, Object... args) { + if (id == FileLoader.FileDidFailedLoad) { + listenerInProgress = true; + String fileName = (String)args[0]; + ArrayList> arrayList = loadingFileObservers.get(fileName); + if (arrayList != null) { + for (WeakReference reference : arrayList) { + if (reference.get() != null) { + reference.get().onFailedDownload(fileName); + observersByTag.remove(reference.get().getObserverTag()); + } + } + loadingFileObservers.remove(fileName); + } + listenerInProgress = false; + processLaterArrays(); + } else if (id == FileLoader.FileDidLoaded) { + listenerInProgress = true; + String fileName = (String)args[0]; + ArrayList> arrayList = loadingFileObservers.get(fileName); + if (arrayList != null) { + for (WeakReference reference : arrayList) { + if (reference.get() != null) { + reference.get().onSuccessDownload(fileName); + observersByTag.remove(reference.get().getObserverTag()); + } + } + loadingFileObservers.remove(fileName); + } + listenerInProgress = false; + processLaterArrays(); + } else if (id == FileLoader.FileLoadProgressChanged) { + listenerInProgress = true; + String fileName = (String)args[0]; + ArrayList> arrayList = loadingFileObservers.get(fileName); + if (arrayList != null) { + Float progress = (Float)args[1]; + for (WeakReference reference : arrayList) { + if (reference.get() != null) { + reference.get().onProgressDownload(fileName, progress); + } + } + } + listenerInProgress = false; + processLaterArrays(); + } + } + + private void clenupPlayer(boolean notify) { + if (audioPlayer != null) { + try { + audioPlayer.stop(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + try { + audioPlayer.release(); + audioPlayer = null; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + lastProgress = 0; + isPaused = false; + MessageObject lastFile = playingMessageObject; + playingMessageObject.audioProgress = 0.0f; + playingMessageObject.audioProgressSec = 0; + playingMessageObject = null; + if (notify) { + NotificationCenter.Instance.postNotificationName(audioDidReset, lastFile.messageOwner.id); + } + } + } + + public boolean seekToProgress(MessageObject messageObject, float progress) { + if (audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.messageOwner.id != messageObject.messageOwner.id) { + return false; + } + try { + int seekTo = (int)(audioPlayer.getDuration() * progress); + audioPlayer.seekTo(seekTo); + lastProgress = seekTo; + } catch (Exception e) { + FileLog.e("tmessages", e); + return false; + } + return true; + } + + public boolean playAudio(MessageObject messageObject) { + if (messageObject == null) { + return false; + } + if (audioPlayer != null && playingMessageObject != null && messageObject.messageOwner.id == playingMessageObject.messageOwner.id) { + if (isPaused) { + resumeAudio(messageObject); + } + return true; + } + clenupPlayer(true); + try { + audioPlayer = new MediaPlayer(); + audioPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + File cacheFile = new File(Utilities.getCacheDir(), messageObject.getFileName()); + audioPlayer.setDataSource(cacheFile.getAbsolutePath()); + audioPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mediaPlayer) { + clenupPlayer(true); + } + }); + audioPlayer.prepare(); + audioPlayer.start(); + } catch (Exception e) { + FileLog.e("tmessages", e); + if (audioPlayer != null) { + audioPlayer.release(); + audioPlayer = null; + isPaused = false; + playingMessageObject = null; + } + return false; + } + + isPaused = false; + lastProgress = 0; + playingMessageObject = messageObject; + + try { + if (playingMessageObject.audioProgress != 0) { + int seekTo = (int)(audioPlayer.getDuration() * playingMessageObject.audioProgress); + audioPlayer.seekTo(seekTo); + } + } catch (Exception e2) { + playingMessageObject.audioProgress = 0; + playingMessageObject.audioProgressSec = 0; + FileLog.e("tmessages", e2); + } + + return true; + } + + public void stopAudio() { + if (audioPlayer == null || playingMessageObject == null) { + return; + } + try { + audioPlayer.stop(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + try { + audioPlayer.release(); + audioPlayer = null; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + playingMessageObject = null; + isPaused = false; + } + + public boolean pauseAudio(MessageObject messageObject) { + if (audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.messageOwner.id != messageObject.messageOwner.id) { + return false; + } + try { + audioPlayer.pause(); + isPaused = true; + } catch (Exception e) { + FileLog.e("tmessages", e); + isPaused = false; + return false; + } + return true; + } + + public boolean resumeAudio(MessageObject messageObject) { + if (audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.messageOwner.id != messageObject.messageOwner.id) { + return false; + } + try { + audioPlayer.start(); + isPaused = false; + } catch (Exception e) { + FileLog.e("tmessages", e); + return false; + } + return true; + } + + public boolean isPlayingAudio(MessageObject messageObject) { + return !(audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.messageOwner.id != messageObject.messageOwner.id); + } + + public boolean isAudioPaused() { + return isPaused; + } + + public boolean startRecording(long dialog_id) { + if (audioRecorder != null) { + return false; + } + + recordingAudio = new TLRPC.TL_audio(); + recordingAudio.dc_id = Integer.MIN_VALUE; + recordingAudio.id = UserConfig.lastLocalId; + recordingAudio.user_id = UserConfig.clientUserId; + UserConfig.lastLocalId--; + UserConfig.saveConfig(false); + + recordingAudioFile = new File(Utilities.getCacheDir(), MessageObject.getAttachFileName(recordingAudio)); + + audioRecorder = new MediaRecorder(); + audioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + audioRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + audioRecorder.setOutputFile(recordingAudioFile.getAbsolutePath()); + if(android.os.Build.VERSION.SDK_INT >= 10) { + audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + } else { + audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + } + audioRecorder.setAudioSamplingRate(24000); + audioRecorder.setAudioChannels(1); + audioRecorder.setAudioEncodingBitRate(16000); + + try { + audioRecorder.prepare(); + audioRecorder.start(); + recordStartTime = System.currentTimeMillis(); + recordDialogId = dialog_id; + } catch (Exception e) { + FileLog.e("tmessages", e); + recordingAudio = null; + recordingAudioFile.delete(); + recordingAudioFile = null; + try { + audioRecorder.release(); + audioRecorder = null; + } catch (Exception e2) { + FileLog.e("tmessages", e2); + } + return false; + } + try { + Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE); + v.vibrate(20); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return true; + } + + public void stopRecording(boolean send) { + if (audioRecorder == null) { + return; + } + try { + audioRecorder.stop(); + if (send) { + recordingAudio.date = ConnectionsManager.Instance.getCurrentTime(); + recordingAudio.size = (int)recordingAudioFile.length(); + recordingAudio.path = recordingAudioFile.getAbsolutePath(); + long duration = System.currentTimeMillis() - recordStartTime; + recordingAudio.duration = (int)(duration / 1000); + if (duration > 500) { + MessagesController.Instance.sendMessage(recordingAudio, recordDialogId); + } else { + recordingAudioFile.delete(); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + if (recordingAudioFile != null) { + recordingAudioFile.delete(); + } + } + try { + if (audioRecorder != null) { + audioRecorder.release(); + audioRecorder = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + recordingAudio = null; + recordingAudioFile = null; + try { + Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE); + v.vibrate(20); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index 9341b1759..d2b02040b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -32,9 +32,6 @@ import android.support.v7.app.ActionBarActivity; import android.text.Html; import android.util.SparseArray; -import org.telegram.TL.TLClassStore; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; import org.telegram.objects.MessageObject; import org.telegram.objects.PhotoObject; import org.telegram.ui.ApplicationLoader; @@ -63,7 +60,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public ConcurrentHashMap> printingUsers = new ConcurrentHashMap>(100, 1.0f, 2); public HashMap printingStrings = new HashMap(); - private HashMap delayedMessages = new HashMap(); + private HashMap> delayedMessages = new HashMap>(); public SparseArray sendingMessages = new SparseArray(); public SparseArray hidenAddToContacts = new SparseArray(); private SparseArray acceptingChats = new SparseArray(); @@ -85,12 +82,15 @@ public class MessagesController implements NotificationCenter.NotificationCenter public boolean registeringForPush = false; private long lastSoundPlay = 0; private long lastStatusUpdateTime = 0; - private boolean offlineSended = false; + private long statusRequest = 0; + private int statusSettingState = 0; + private boolean offlineSent = false; private String uploadingAvatar = null; private SoundPool soundPool; private int sound; public static SecureRandom random = new SecureRandom(); public boolean enableJoined = true; + public int fontSize = Utilities.dp(16); public long scheduleContactsReload = 0; private class UserActionUpdates extends TLRPC.Updates { @@ -183,6 +183,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter addSupportUser(); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); enableJoined = preferences.getBoolean("EnableContactJoined", true); + preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + fontSize = preferences.getInt("fons_size", 16); try { soundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0); @@ -226,7 +228,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (id == FileLoader.FileDidUpload) { fileDidUploaded((String)args[0], (TLRPC.InputFile)args[1], (TLRPC.InputEncryptedFile)args[2]); } else if (id == FileLoader.FileDidFailUpload) { - fileDidFailedUpload((String) args[0]); + fileDidFailedUpload((String) args[0], (Boolean) args[1]); } else if (id == messageReceivedByServer) { Integer msgId = (Integer)args[0]; MessageObject obj = dialogMessage.get(msgId); @@ -268,6 +270,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void cleanUp() { ContactsController.Instance.cleanup(); + MediaController.Instance.cleanup(); dialogs_dict.clear(); dialogs.clear(); @@ -297,9 +300,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter firstGettingTask = false; updatingState = false; lastStatusUpdateTime = 0; - offlineSended = false; + offlineSent = false; registeringForPush = false; uploadingAvatar = null; + statusRequest = 0; + statusSettingState = 0; addSupportUser(); } @@ -737,32 +742,56 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (UserConfig.clientUserId != 0) { if (scheduleContactsReload != 0 && currentTime > scheduleContactsReload) { - ContactsController.Instance.performSyncPhoneBook(ContactsController.Instance.getContactsCopy(ContactsController.Instance.contactsBook), true, false); + ContactsController.Instance.performSyncPhoneBook(ContactsController.Instance.getContactsCopy(ContactsController.Instance.contactsBook), true, false, true); scheduleContactsReload = 0; } if (ApplicationLoader.lastPauseTime == 0) { - if (lastStatusUpdateTime != -1 && (lastStatusUpdateTime == 0 || lastStatusUpdateTime <= System.currentTimeMillis() - 55000 || offlineSended)) { - lastStatusUpdateTime = -1; + if (statusSettingState != 1 && (lastStatusUpdateTime == 0 || lastStatusUpdateTime <= System.currentTimeMillis() - 55000 || offlineSent)) { + statusSettingState = 1; + + if (statusRequest != 0) { + ConnectionsManager.Instance.cancelRpc(statusRequest, true); + } + TLRPC.TL_account_updateStatus req = new TLRPC.TL_account_updateStatus(); req.offline = false; - ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { + statusRequest = ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { - lastStatusUpdateTime = System.currentTimeMillis(); + if (error == null) { + lastStatusUpdateTime = System.currentTimeMillis(); + offlineSent = false; + statusSettingState = 0; + } else { + if (lastStatusUpdateTime != 0) { + lastStatusUpdateTime += 5000; + } + } + statusRequest = 0; } }, null, true, RPCRequest.RPCRequestClassGeneric); - offlineSended = false; } - } else if (!offlineSended && ApplicationLoader.lastPauseTime <= System.currentTimeMillis() - 2000) { + } else if (statusSettingState != 2 && !offlineSent && ApplicationLoader.lastPauseTime <= System.currentTimeMillis() - 2000) { + statusSettingState = 2; + if (statusRequest != 0) { + ConnectionsManager.Instance.cancelRpc(statusRequest, true); + } TLRPC.TL_account_updateStatus req = new TLRPC.TL_account_updateStatus(); req.offline = true; - ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { + statusRequest = ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(TLObject response, TLRPC.TL_error error) { + if (error == null) { + offlineSent = true; + } else { + if (lastStatusUpdateTime != 0) { + lastStatusUpdateTime += 5000; + } + } + statusRequest = 0; } }, null, true, RPCRequest.RPCRequestClassGeneric); - offlineSended = true; } if (updatesStartWaitTime != 0 && updatesStartWaitTime + 1500 < currentTime) { @@ -972,7 +1001,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { for (TLRPC.User u : messagesRes.users) { if (isCache) { - if (u.id == UserConfig.clientUserId || u.id == 333000) { + if (u.id == UserConfig.clientUserId || u.id / 1000 == 333) { users.put(u.id, u); } else { users.putIfAbsent(u.id, u); @@ -1022,6 +1051,21 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } + public void processDialogsUpdateRead(final HashMapdialogsToUpdate) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + for (HashMap.Entry entry : dialogsToUpdate.entrySet()) { + TLRPC.TL_dialog currentDialog = dialogs_dict.get(entry.getKey()); + if (currentDialog != null) { + currentDialog.unread_count = entry.getValue(); + } + } + NotificationCenter.Instance.postNotificationName(dialogsNeedReload); + } + }); + } + public void processDialogsUpdate(final TLRPC.messages_Dialogs dialogsRes, ArrayList encChats) { Utilities.stageQueue.postRunnable(new Runnable() { @Override @@ -1130,7 +1174,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { for (TLRPC.User u : dialogsRes.users) { if (isCache) { - if (u.id == UserConfig.clientUserId || u.id == 333000) { + if (u.id == UserConfig.clientUserId || u.id / 1000 == 333) { users.put(u.id, u); } else { users.putIfAbsent(u.id, u); @@ -1198,7 +1242,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run() { for (TLRPC.User u : dialogsRes.users) { if (isCache) { - if (u.id == UserConfig.clientUserId || u.id == 333000) { + if (u.id == UserConfig.clientUserId || u.id / 1000 == 333) { users.put(u.id, u); } else { users.putIfAbsent(u.id, u); @@ -1454,18 +1498,29 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void cancelSendingMessage(MessageObject object) { String keyToRemvoe = null; - for (HashMap.Entry entry : delayedMessages.entrySet()) { - if (entry.getValue().obj.messageOwner.id == object.messageOwner.id) { - keyToRemvoe = entry.getKey(); - break; + boolean enc = false; + for (HashMap.Entry> entry : delayedMessages.entrySet()) { + ArrayList messages = entry.getValue(); + for (int a = 0; a < messages.size(); a++) { + DelayedMessage message = messages.get(a); + if (message.obj.messageOwner.id == object.messageOwner.id) { + messages.remove(a); + if (messages.size() == 0) { + keyToRemvoe = entry.getKey(); + if (message.sendEncryptedRequest != null) { + enc = true; + } + } + break; + } } } if (keyToRemvoe != null) { - ArrayList messages = new ArrayList(); - messages.add(object.messageOwner.id); - FileLoader.Instance.cancelUploadFile(keyToRemvoe); - deleteMessages(messages); + FileLoader.Instance.cancelUploadFile(keyToRemvoe, enc); } + ArrayList messages = new ArrayList(); + messages.add(object.messageOwner.id); + deleteMessages(messages); } private long getNextRandomId() { @@ -1592,6 +1647,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMsg.media = msgObj.messageOwner.media; newMsg.message = msgObj.messageOwner.message; newMsg.fwd_msg_id = msgObj.messageOwner.id; + newMsg.attachPath = msgObj.messageOwner.attachPath; type = 4; } else if (msgObj.type == 11) { newMsg.fwd_from_id = msgObj.messageOwner.from_id; @@ -1607,6 +1663,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMsg.media = msgObj.messageOwner.media; newMsg.message = msgObj.messageOwner.message; newMsg.fwd_msg_id = msgObj.messageOwner.id; + newMsg.attachPath = msgObj.messageOwner.attachPath; type = 4; } } else if (location != null) { @@ -1889,7 +1946,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } - private void processSendedMessage(TLRPC.Message newMsg, TLRPC.Message sentMessage, TLRPC.EncryptedFile file, TLRPC.DecryptedMessage decryptedMessage) { + private void processSentMessage(TLRPC.Message newMsg, TLRPC.Message sentMessage, TLRPC.EncryptedFile file, TLRPC.DecryptedMessage decryptedMessage) { if (sentMessage != null) { if (sentMessage.media instanceof TLRPC.TL_messageMediaPhoto && sentMessage.media.photo != null && newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) { for (TLRPC.PhotoSize size : sentMessage.media.photo.sizes) { @@ -1946,8 +2003,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter File cacheFile = new File(Utilities.getCacheDir(), fileName); File cacheFile2 = new File(Utilities.getCacheDir(), fileName2); cacheFile.renameTo(cacheFile2); - sentMessage.media.audio.dc_id = newMsg.media.audio.dc_id; - sentMessage.media.audio.id = newMsg.media.audio.id; + newMsg.media.audio.dc_id = sentMessage.media.audio.dc_id; + newMsg.media.audio.id = sentMessage.media.audio.id; } } else if (file != null) { if (newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) { @@ -2067,7 +2124,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter dataForEncryption.writeByte(b[0]); } - byte[] encryptedData = Utilities.aesIgeEncryption(dataForEncryption.toByteArray(), keyData.aesKey, keyData.aesIv, true, false); + byte[] encryptedData = Utilities.aesIgeEncryption(dataForEncryption.toByteArray(), keyData.aesKey, keyData.aesIv, true, false, 0); data = new SerializedData(); data.writeInt64(chat.key_fingerprint); @@ -2101,7 +2158,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter TLRPC.messages_SentEncryptedMessage res = (TLRPC.messages_SentEncryptedMessage)response; newMsgObj.messageOwner.date = res.date; if (res.file instanceof TLRPC.TL_encryptedFile) { - processSendedMessage(newMsgObj.messageOwner, null, res.file, req); + processSentMessage(newMsgObj.messageOwner, null, res.file, req); } MessagesStorage.Instance.updateMessageStateAndId(newMsgObj.messageOwner.random_id, newMsgObj.messageOwner.id, newMsgObj.messageOwner.id, res.date, true); Utilities.RunOnUIThread(new Runnable() { @@ -2132,7 +2189,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { final int oldId = newMsgObj.messageOwner.id; - ArrayList sendedMessages = new ArrayList(); + ArrayList sentMessages = new ArrayList(); if (response instanceof TLRPC.TL_messages_sentMessage) { TLRPC.TL_messages_sentMessage res = (TLRPC.TL_messages_sentMessage)response; @@ -2158,9 +2215,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } else if (response instanceof TLRPC.messages_StatedMessage) { TLRPC.messages_StatedMessage res = (TLRPC.messages_StatedMessage)response; - sendedMessages.add(res.message); + sentMessages.add(res.message); newMsgObj.messageOwner.id = res.message.id; - processSendedMessage(newMsgObj.messageOwner, res.message, null, null); + processSentMessage(newMsgObj.messageOwner, res.message, null, null); if(MessagesStorage.lastSeqValue + 1 == res.seq) { MessagesStorage.lastSeqValue = res.seq; MessagesStorage.lastPtsValue = res.pts; @@ -2185,8 +2242,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (!res.messages.isEmpty()) { TLRPC.Message message = res.messages.get(0); newMsgObj.messageOwner.id = message.id; - sendedMessages.add(message); - processSendedMessage(newMsgObj.messageOwner, message, null, null); + sentMessages.add(message); + processSentMessage(newMsgObj.messageOwner, message, null, null); } if (MessagesStorage.lastSeqValue + 1 == res.seq) { MessagesStorage.lastSeqValue = res.seq; @@ -2208,8 +2265,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } MessagesStorage.Instance.updateMessageStateAndId(newMsgObj.messageOwner.random_id, oldId, newMsgObj.messageOwner.id, 0, true); - if (!sendedMessages.isEmpty()) { - MessagesStorage.Instance.putMessages(sendedMessages, true, true); + if (!sentMessages.isEmpty()) { + MessagesStorage.Instance.putMessages(sentMessages, true, true); } Utilities.RunOnUIThread(new Runnable() { @Override @@ -2245,74 +2302,87 @@ public class MessagesController implements NotificationCenter.NotificationCenter }), true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors | RPCRequest.RPCRequestClassCanCompress, ConnectionsManager.DEFAULT_DATACENTER_ID); } - private void performSendDelayedMessage(final DelayedMessage message) { - Utilities.stageQueue.postRunnable(new Runnable() { - @Override - public void run() { - if (message.type == 0) { - String location = Utilities.getCacheDir() + "/" + message.location.volume_id + "_" + message.location.local_id + ".jpg"; - delayedMessages.put(location, message); - if (message.sendRequest != null) { - FileLoader.Instance.uploadFile(location, null, null); - } else { - FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); - } - } else if (message.type == 1) { - if (message.sendRequest != null) { - if (message.sendRequest.media.thumb == null) { - String location = Utilities.getCacheDir() + "/" + message.location.volume_id + "_" + message.location.local_id + ".jpg"; - delayedMessages.put(location, message); - FileLoader.Instance.uploadFile(location, null, null); - } else { - String location = message.videoLocation.path; - if (location == null) { - location = Utilities.getCacheDir() + "/" + message.videoLocation.id + ".mp4"; - } - delayedMessages.put(location, message); - FileLoader.Instance.uploadFile(location, null, null); - } - } else { - String location = message.videoLocation.path; - if (location == null) { - location = Utilities.getCacheDir() + "/" + message.videoLocation.id + ".mp4"; - } - delayedMessages.put(location, message); - FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); - } - } else if (message.type == 2) { - String location = message.documentLocation.path; - delayedMessages.put(location, message); - if (message.sendRequest != null) { - FileLoader.Instance.uploadFile(location, null, null); - } else { - FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); - } - } else if (message.type == 3) { - String location = message.audioLocation.path; - delayedMessages.put(location, message); - if (message.sendRequest != null) { - FileLoader.Instance.uploadFile(location, null, null); - } else { - FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); - } - } - } - }); + private void putToDelayedMessages(String location, DelayedMessage message) { + ArrayList arrayList = delayedMessages.get(location); + if (arrayList == null) { + arrayList = new ArrayList(); + delayedMessages.put(location, arrayList); + } + arrayList.add(message); } - public void fileDidFailedUpload(final String location) { + private void performSendDelayedMessage(final DelayedMessage message) { + if (message.type == 0) { + String location = Utilities.getCacheDir() + "/" + message.location.volume_id + "_" + message.location.local_id + ".jpg"; + putToDelayedMessages(location, message); + if (message.sendRequest != null) { + FileLoader.Instance.uploadFile(location, null, null); + } else { + FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); + } + } else if (message.type == 1) { + if (message.sendRequest != null) { + if (message.sendRequest.media.thumb == null) { + String location = Utilities.getCacheDir() + "/" + message.location.volume_id + "_" + message.location.local_id + ".jpg"; + putToDelayedMessages(location, message); + FileLoader.Instance.uploadFile(location, null, null); + } else { + String location = message.videoLocation.path; + if (location == null) { + location = Utilities.getCacheDir() + "/" + message.videoLocation.id + ".mp4"; + } + putToDelayedMessages(location, message); + FileLoader.Instance.uploadFile(location, null, null); + } + } else { + String location = message.videoLocation.path; + if (location == null) { + location = Utilities.getCacheDir() + "/" + message.videoLocation.id + ".mp4"; + } + putToDelayedMessages(location, message); + FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); + } + } else if (message.type == 2) { + String location = message.documentLocation.path; + putToDelayedMessages(location, message); + if (message.sendRequest != null) { + FileLoader.Instance.uploadFile(location, null, null); + } else { + FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); + } + } else if (message.type == 3) { + String location = message.audioLocation.path; + putToDelayedMessages(location, message); + if (message.sendRequest != null) { + FileLoader.Instance.uploadFile(location, null, null); + } else { + FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); + } + } + } + + public void fileDidFailedUpload(final String location, final boolean enc) { if (uploadingAvatar != null && uploadingAvatar.equals(location)) { uploadingAvatar = null; } else { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { - DelayedMessage obj = delayedMessages.get(location); - if (obj != null) { - obj.obj.messageOwner.send_state = MESSAGE_SEND_STATE_SEND_ERROR; - sendingMessages.remove(obj.obj.messageOwner.id); - NotificationCenter.Instance.postNotificationName(messageSendError, obj.obj.messageOwner.id); - delayedMessages.remove(location); + ArrayList arr = delayedMessages.get(location); + if (arr != null) { + for (int a = 0; a < arr.size(); a++) { + DelayedMessage obj = arr.get(a); + if (enc && obj.sendEncryptedRequest != null || !enc && obj.sendRequest != null) { + obj.obj.messageOwner.send_state = MESSAGE_SEND_STATE_SEND_ERROR; + sendingMessages.remove(obj.obj.messageOwner.id); + arr.remove(a); + a--; + NotificationCenter.Instance.postNotificationName(messageSendError, obj.obj.messageOwner.id); + } + } + if (arr.isEmpty()) { + delayedMessages.remove(location); + } } } }); @@ -2372,31 +2442,40 @@ public class MessagesController implements NotificationCenter.NotificationCenter Utilities.RunOnUIThread(new Runnable() { @Override public void run() { - DelayedMessage message = delayedMessages.get(location); - if (message != null) { - if (file != null) { - if (message.type == 0) { - message.sendRequest.media.file = file; - performSendMessageRequest(message.sendRequest, message.obj); - } else if (message.type == 1) { - if (message.sendRequest.media.thumb == null) { - message.sendRequest.media.thumb = file; - performSendDelayedMessage(message); - } else { + ArrayList arr = delayedMessages.get(location); + if (arr != null) { + for (int a = 0; a < arr.size(); a++) { + DelayedMessage message = arr.get(a); + if (file != null && message.sendRequest != null) { + if (message.type == 0) { + message.sendRequest.media.file = file; + performSendMessageRequest(message.sendRequest, message.obj); + } else if (message.type == 1) { + if (message.sendRequest.media.thumb == null) { + message.sendRequest.media.thumb = file; + performSendDelayedMessage(message); + } else { + message.sendRequest.media.file = file; + performSendMessageRequest(message.sendRequest, message.obj); + } + } else if (message.type == 2) { + message.sendRequest.media.file = file; + performSendMessageRequest(message.sendRequest, message.obj); + } else if (message.type == 3) { message.sendRequest.media.file = file; performSendMessageRequest(message.sendRequest, message.obj); } - } else if (message.type == 2) { - message.sendRequest.media.file = file; - performSendMessageRequest(message.sendRequest, message.obj); - } else if (message.type == 3) { - message.sendRequest.media.file = file; - performSendMessageRequest(message.sendRequest, message.obj); + arr.remove(a); + a--; + } else if (encryptedFile != null && message.sendEncryptedRequest != null) { + performSendEncryptedRequest(message.sendEncryptedRequest, message.obj, message.encryptedChat, encryptedFile); + arr.remove(a); + a--; } - } else if (encryptedFile != null) { - performSendEncryptedRequest(message.sendEncryptedRequest, message.obj, message.encryptedChat, encryptedFile); } - delayedMessages.remove(location); + if (arr.isEmpty()) { + delayedMessages.remove(location); + } } } }); @@ -2768,6 +2847,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public void unregistedPush() { + if (UserConfig.registeredForPush && UserConfig.pushString.length() == 0) { + TLRPC.TL_account_unregisterDevice req = new TLRPC.TL_account_unregisterDevice(); + req.token = UserConfig.pushString; + req.token_type = 2; + ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + + } + }, null, true, RPCRequest.RPCRequestClassGeneric); + } + TLRPC.TL_auth_logOut req2 = new TLRPC.TL_auth_logOut(); ConnectionsManager.Instance.performRpc(req2, new RPCRequest.RPCRequestDelegate() { @Override @@ -2775,19 +2866,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter } }, null, true, RPCRequest.RPCRequestClassGeneric); - - if (!UserConfig.registeredForPush || UserConfig.pushString.length() == 0) { - return; - } - TLRPC.TL_account_unregisterDevice req = new TLRPC.TL_account_unregisterDevice(); - req.token = UserConfig.pushString; - req.token_type = 2; - ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { - @Override - public void run(TLObject response, TLRPC.TL_error error) { - - } - }, null, true, RPCRequest.RPCRequestClassGeneric); } public void registerForPush(final String regid) { @@ -3831,22 +3909,12 @@ public class MessagesController implements NotificationCenter.NotificationCenter toDbUser.id = update.user_id; TLRPC.User currentUser = users.get(update.user_id); if (update instanceof TLRPC.TL_updateUserStatus) { - if (!(update.status instanceof TLRPC.TL_userStatusEmpty)) { - if (currentUser != null) { - currentUser.id = update.user_id; - currentUser.status = update.status; - if (update.status instanceof TLRPC.TL_userStatusOnline) { - currentUser.status.was_online = update.status.expires; - } else if (update.status instanceof TLRPC.TL_userStatusOffline) { - currentUser.status.expires = update.status.was_online; - } else { - currentUser.status.was_online = 0; - currentUser.status.expires = 0; - } - } - toDbUser.status = update.status; - dbUsersStatus.add(toDbUser); + if (currentUser != null) { + currentUser.id = update.user_id; + currentUser.status = update.status; } + toDbUser.status = update.status; + dbUsersStatus.add(toDbUser); } else if (update instanceof TLRPC.TL_updateUserName) { if (currentUser != null) { currentUser.first_name = update.first_name; @@ -3931,6 +3999,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter } if (!markAsReadMessages.isEmpty() || !markAsReadEncrypted.isEmpty()) { + if (!markAsReadMessages.isEmpty()) { + MessagesStorage.Instance.updateDialogsWithReadedMessages(markAsReadMessages, true); + } MessagesStorage.Instance.markMessagesAsRead(markAsReadMessages, markAsReadEncrypted, true); } if (!deletedMessages.isEmpty()) { @@ -3939,9 +4010,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (!deletedMessages.isEmpty()) { MessagesStorage.Instance.updateDialogsWithDeletedMessages(deletedMessages, true); } - if (!markAsReadMessages.isEmpty()) { - MessagesStorage.Instance.updateDialogsWithReadedMessages(markAsReadMessages, true); - } if (!tasks.isEmpty()) { for (TLRPC.TL_updateEncryptedMessagesRead update : tasks) { MessagesStorage.Instance.createTaskForDate(update.chat_id, update.max_date, update.date, 1); @@ -4426,7 +4494,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter MessageKeyData keyData = Utilities.generateMessageKeyData(chat.auth_key, messageKey, false); byte[] messageData = is.readData(message.bytes.length - 24); - messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); + messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false, 0); is = new SerializedData(messageData); int len = is.readInt32(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index 8d7b1c673..d34b204f6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -8,15 +8,15 @@ package org.telegram.messenger; +import android.content.Context; +import android.content.SharedPreferences; import android.text.Html; import android.util.SparseArray; +import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.SQLite.SQLiteCursor; import org.telegram.SQLite.SQLiteDatabase; import org.telegram.SQLite.SQLitePreparedStatement; -import org.telegram.TL.TLClassStore; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; import org.telegram.ui.ApplicationLoader; import java.io.File; @@ -40,6 +40,8 @@ public class MessagesStorage { public static final int wallpapersDidLoaded = 171; public static MessagesStorage Instance = new MessagesStorage(); + private boolean appliedDialogFix = false; + public MessagesStorage() { storageQueue.setPriority(Thread.MAX_PRIORITY); openDatabase(); @@ -48,10 +50,22 @@ public class MessagesStorage { public void openDatabase() { cacheFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "cache4.db"); + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dbconfig", Context.MODE_PRIVATE); + appliedDialogFix = preferences.getBoolean("appliedDialogFix", false); + boolean createTable = false; //cacheFile.delete(); if (!cacheFile.exists()) { createTable = true; + + try { + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("appliedDialogFix", true); + appliedDialogFix = true; + editor.commit(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } try { database = new SQLiteDatabase(cacheFile.getPath()); @@ -187,6 +201,45 @@ public class MessagesStorage { }); } + public void applyDialogsFix() { //server bug on 20.02.2014 + if (!appliedDialogFix) { + try { + SQLiteCursor cursor = database.queryFinalized("SELECT d.did, m.data FROM dialogs as d LEFT JOIN messages as m ON d.last_mid = m.mid WHERE m.mid < 0 AND m.date >= 1392930900 AND m.date <= 1392935700"); + String dids = ""; + while (cursor.next()) { + long did = cursor.longValue(0); + + byte[] messageData = cursor.byteArrayValue(1); + if (messageData != null) { + SerializedData data = new SerializedData(messageData); + TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + if (message != null) { + if (message.action != null && message.action instanceof TLRPC.TL_messageActionUserJoined) { + if (dids.length() != 0) { + dids += ","; + } + dids += "" + did; + } + } + } + } + cursor.dispose(); + if (dids.length() != 0) { + database.executeFast("DELETE FROM dialogs WHERE did IN(" + dids + ")").stepThis().dispose(); + } + + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dbconfig", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("appliedDialogFix", true); + appliedDialogFix = true; + editor.commit(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + + public void saveSecretParams(final int lsv, final int sg, final byte[] pbytes) { storageQueue.postRunnable(new Runnable() { @Override @@ -464,7 +517,7 @@ public class MessagesStorage { } state.dispose(); database.commitTransaction(); - database.executeFast(String.format(Locale.US, "UPDATE messages SET ttl = 0 WHERE mid IN (%s)", mids)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE messages SET ttl = 0 WHERE mid IN(%s)", mids)).stepThis().dispose(); MessagesController.Instance.didAddedNewTask(minDate); } } catch (Exception e) { @@ -474,7 +527,7 @@ public class MessagesStorage { }); } - private void updateDialogsWithReadedMessagesInternal(final ArrayList messages/*, final HashMap encryptedMessages*/) { + private void updateDialogsWithReadedMessagesInternal(final ArrayList messages) { if (Thread.currentThread().getId() != storageQueue.getId()) { throw new RuntimeException("wrong db thread"); } @@ -489,182 +542,57 @@ public class MessagesStorage { } ids += uid; } - SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid FROM messages WHERE mid IN(%s) AND out = 0", ids)); + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, read_state, out FROM messages WHERE mid IN(%s)", ids)); while (cursor.next()) { + int out = cursor.intValue(2); + if (out != 0) { + continue; + } + int read_state = cursor.intValue(1); + if (read_state != 0) { + continue; + } long uid = cursor.longValue(0); Integer currentCount = dialogsToUpdate.get(uid); if (currentCount == null) { dialogsToUpdate.put(uid, 1); + if (dialogsToReload.length() != 0) { + dialogsToReload += ","; + } + dialogsToReload += uid; } else { dialogsToUpdate.put(uid, currentCount + 1); } } cursor.dispose(); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT did, unread_count FROM dialogs WHERE did IN(%s)", dialogsToReload)); + while (cursor.next()) { + long did = cursor.longValue(0); + int count = cursor.intValue(1); + Integer currentCount = dialogsToUpdate.get(did); + if (currentCount != null) { + dialogsToUpdate.put(did, Math.max(0, count - currentCount)); + } else { + dialogsToUpdate.remove(did); + } + } + cursor.dispose(); + database.beginTransaction(); - SQLitePreparedStatement state = database.executeFast("UPDATE dialogs SET unread_count = max(0, (SELECT unread_count FROM dialogs WHERE did = ?) - ?) WHERE did = ?"); + SQLitePreparedStatement state = database.executeFast("UPDATE dialogs SET unread_count = ? WHERE did = ?"); for (HashMap.Entry entry : dialogsToUpdate.entrySet()) { state.requery(); - state.bindLong(1, entry.getKey()); - state.bindInteger(2, entry.getValue()); - state.bindLong(3, entry.getKey()); + state.bindInteger(1, entry.getValue()); + state.bindLong(2, entry.getKey()); state.step(); - if (dialogsToReload.length() != 0) { - dialogsToReload += ","; - } - dialogsToReload += entry.getKey(); } state.dispose(); database.commitTransaction(); } - /*if (encryptedMessages != null && !encryptedMessages.isEmpty()) { - database.beginTransaction(); - SQLitePreparedStatement state = database.executeFast("UPDATE dialogs SET unread_count = (SELECT COUNT(uid) FROM messages WHERE did = ? AND read_state = 0 AND out = 0) WHERE did = ?"); - for (HashMap.Entry entry : encryptedMessages.entrySet()) { - long dialog_id = ((long)entry.getKey()) << 32; - state.requery(); - state.bindLong(1, dialog_id); - state.bindLong(2, dialog_id); - state.step(); - if (dialogsToReload.length() != 0) { - dialogsToReload += ","; - } - dialogsToReload += dialog_id; - } - state.dispose(); - database.commitTransaction(); - }*/ - TLRPC.messages_Dialogs dialogs = new TLRPC.messages_Dialogs(); - ArrayList encryptedChats = new ArrayList(); - ArrayList usersToLoad = new ArrayList(); - ArrayList chatsToLoad = new ArrayList(); - ArrayList encryptedToLoad = new ArrayList(); - SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT d.did, d.last_mid, d.unread_count, d.date, m.data, m.read_state, m.mid, m.send_state FROM dialogs as d LEFT JOIN messages as m ON d.last_mid = m.mid WHERE d.did IN(%s)", dialogsToReload)); - while (cursor.next()) { - TLRPC.TL_dialog dialog = new TLRPC.TL_dialog(); - dialog.id = cursor.longValue(0); - dialog.top_message = cursor.intValue(1); - dialog.unread_count = cursor.intValue(2); - dialog.last_message_date = cursor.intValue(3); - dialogs.dialogs.add(dialog); - - byte[] messageData = cursor.byteArrayValue(4); - if (messageData != null) { - SerializedData data = new SerializedData(messageData); - TLRPC.Message message = (TLRPC.Message)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - message.unread = (cursor.intValue(5) != 1); - message.id = cursor.intValue(6); - message.send_state = cursor.intValue(7); - dialogs.messages.add(message); - - if (!usersToLoad.contains(message.from_id)) { - usersToLoad.add(message.from_id); - } - if (message.action != null && message.action.user_id != 0) { - if (!usersToLoad.contains(message.action.user_id)) { - usersToLoad.add(message.action.user_id); - } - } - if (message.fwd_from_id != 0) { - if (!usersToLoad.contains(message.fwd_from_id)) { - usersToLoad.add(message.fwd_from_id); - } - } - } - - int lower_id = (int)dialog.id; - if (lower_id != 0) { - if (lower_id > 0) { - if (!usersToLoad.contains(lower_id)) { - usersToLoad.add(lower_id); - } - } else { - if (!chatsToLoad.contains(-lower_id)) { - chatsToLoad.add(-lower_id); - } - } - } else { - int encryptedId = (int)(dialog.id >> 32); - if (!encryptedToLoad.contains(encryptedId)) { - encryptedToLoad.add(encryptedId); - } - } - } - cursor.dispose(); - - if (!encryptedToLoad.isEmpty()) { - String toLoad = ""; - for (int uid : encryptedToLoad) { - if (toLoad.length() != 0) { - toLoad += ","; - } - toLoad += uid; - } - cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, user, g, authkey, ttl FROM enc_chats WHERE uid IN(%s)", toLoad)); - while (cursor.next()) { - byte[] chatData = cursor.byteArrayValue(0); - if (chatData != null) { - SerializedData data = new SerializedData(chatData); - TLRPC.EncryptedChat chat = (TLRPC.EncryptedChat)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - encryptedChats.add(chat); - chat.user_id = cursor.intValue(1); - if (!usersToLoad.contains(chat.user_id)) { - usersToLoad.add(chat.user_id); - } - chat.a_or_b = cursor.byteArrayValue(2); - chat.auth_key = cursor.byteArrayValue(3); - chat.ttl = cursor.intValue(4); - } - } - cursor.dispose(); - } - - if (!chatsToLoad.isEmpty()) { - String toLoad = ""; - for (int uid : chatsToLoad) { - if (toLoad.length() != 0) { - toLoad += ","; - } - toLoad += uid; - } - cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM chats WHERE uid IN(%s)", toLoad)); - while (cursor.next()) { - byte[] chatData = cursor.byteArrayValue(0); - if (chatData != null) { - SerializedData data = new SerializedData(chatData); - TLRPC.Chat chat = (TLRPC.Chat)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - dialogs.chats.add(chat); - } - } - cursor.dispose(); - } - - if (!usersToLoad.isEmpty()) { - String toLoad = ""; - for (int uid : usersToLoad) { - if (toLoad.length() != 0) { - toLoad += ","; - } - toLoad += uid; - } - cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, status FROM users WHERE uid IN(%s)", toLoad)); - while (cursor.next()) { - byte[] userData = cursor.byteArrayValue(0); - if (userData != null) { - SerializedData data = new SerializedData(userData); - TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); - } - dialogs.users.add(user); - } - } - cursor.dispose(); - } - - if (!dialogs.dialogs.isEmpty() || !encryptedChats.isEmpty()) { - MessagesController.Instance.processDialogsUpdate(dialogs, encryptedChats); + if (!dialogsToUpdate.isEmpty()) { + MessagesController.Instance.processDialogsUpdateRead(dialogsToUpdate); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -808,7 +736,7 @@ public class MessagesStorage { TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); loadedUsers.add(user); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } } } @@ -900,7 +828,7 @@ public class MessagesStorage { continue; } if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q)); resultArray.add(user); @@ -931,7 +859,7 @@ public class MessagesStorage { SerializedData data2 = new SerializedData(userData); TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data2, data2.readInt32()); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(7); + user.status.expires = cursor.intValue(7); } resultArrayNames.add(Html.fromHtml("" + Utilities.formatName(user.first_name, user.last_name) + "")); resultArray.add(chat); @@ -1013,7 +941,7 @@ public class MessagesStorage { } ids += "" + uid; } - database.executeFast("DELETE FROM contacts WHERE uid IN (" + ids + ")").stepThis().dispose(); + database.executeFast("DELETE FROM contacts WHERE uid IN(" + ids + ")").stepThis().dispose(); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -1030,10 +958,10 @@ public class MessagesStorage { public void run() { try { if (adds.length() != 0) { - database.executeFast(String.format(Locale.US, "UPDATE user_phones_v6 SET deleted = 0 WHERE sphone IN (%s)", adds)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE user_phones_v6 SET deleted = 0 WHERE sphone IN(%s)", adds)).stepThis().dispose(); } if (deletes.length() != 0) { - database.executeFast(String.format(Locale.US, "UPDATE user_phones_v6 SET deleted = 1 WHERE sphone IN (%s)", deletes)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE user_phones_v6 SET deleted = 1 WHERE sphone IN(%s)", deletes)).stepThis().dispose(); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -1054,6 +982,9 @@ public class MessagesStorage { SQLitePreparedStatement state2 = database.executeFast("REPLACE INTO user_phones_v6 VALUES(?, ?, ?, ?)"); for (HashMap.Entry entry : contactHashMap.entrySet()) { ContactsController.Contact contact = entry.getValue(); + if (contact.phones.isEmpty() || contact.shortPhones.isEmpty()) { + continue; + } state.requery(); state.bindInteger(1, contact.id); state.bindString(2, contact.first_name); @@ -1095,8 +1026,19 @@ public class MessagesStorage { contact.id = uid; contactHashMap.put(uid, contact); } - contact.phones.add(cursor.stringValue(3)); - contact.shortPhones.add(cursor.stringValue(4)); + String phone = cursor.stringValue(3); + if (phone == null) { + continue; + } + contact.phones.add(phone); + String sphone = cursor.stringValue(4); + if (sphone == null) { + continue; + } + if (sphone.length() == 8 && phone.length() != 8) { + sphone = PhoneFormat.stripExceptNumbers(phone); + } + contact.shortPhones.add(sphone); contact.phoneDeleted.add(cursor.intValue(5)); contact.phoneTypes.add(""); } @@ -1105,7 +1047,7 @@ public class MessagesStorage { contactHashMap.clear(); FileLog.e("tmessages", e); } - ContactsController.Instance.performSyncPhoneBook(contactHashMap, true, true); + ContactsController.Instance.performSyncPhoneBook(contactHashMap, true, true, false); } }); } @@ -1144,7 +1086,7 @@ public class MessagesStorage { TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); users.add(user); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } } } @@ -1265,7 +1207,7 @@ public class MessagesStorage { TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); loadedUsers.add(user.id); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } res.users.add(user); } @@ -1425,6 +1367,9 @@ public class MessagesStorage { if (message.media != null && message.media.user_id != 0) { fromUser.add(message.media.user_id); } + if (message.media != null && message.media.audio != null && message.media.audio.user_id != 0) { + fromUser.add(message.media.audio.user_id); + } if (message.fwd_from_id != 0) { fromUser.add(message.fwd_from_id); } @@ -1455,7 +1400,7 @@ public class MessagesStorage { TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); loadedUsers.add(user.id); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } res.users.add(user); } @@ -1601,7 +1546,7 @@ public class MessagesStorage { SerializedData data = new SerializedData(userData); TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } result.add(user); } @@ -1693,7 +1638,7 @@ public class MessagesStorage { state.bindString(2, ""); } if (user.status != null) { - state.bindInteger(3, (user.status.was_online != 0 ? user.status.was_online : user.status.expires)); + state.bindInteger(3, user.status.expires); } else { state.bindInteger(3, 0); } @@ -2071,7 +2016,7 @@ public class MessagesStorage { for (TLRPC.User user : users) { state.requery(); if (user.status != null) { - state.bindInteger(1, (user.status.was_online != 0 ? user.status.was_online : user.status.expires)); + state.bindInteger(1, user.status.expires); } else { state.bindInteger(1, 0); } @@ -2101,7 +2046,7 @@ public class MessagesStorage { TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); loadedUsers.add(user); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } TLRPC.User updateUser = usersDict.get(user.id); if (updateUser.first_name != null && updateUser.last_name != null) { @@ -2130,7 +2075,7 @@ public class MessagesStorage { state.bindString(2, ""); } if (user.status != null) { - state.bindInteger(3, (user.status.was_online != 0 ? user.status.was_online : user.status.expires)); + state.bindInteger(3, user.status.expires); } else { state.bindInteger(3, 0); } @@ -2177,7 +2122,7 @@ public class MessagesStorage { } ids += uid; } - database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = 1 WHERE mid IN (%s)", ids)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "UPDATE messages SET read_state = 1 WHERE mid IN(%s)", ids)).stepThis().dispose(); } if (encryptedMessages != null && !encryptedMessages.isEmpty()) { for (HashMap.Entry entry : encryptedMessages.entrySet()) { @@ -2221,8 +2166,8 @@ public class MessagesStorage { } ids += uid; } - database.executeFast(String.format(Locale.US, "DELETE FROM messages WHERE mid IN (%s)", ids)).stepThis().dispose(); - database.executeFast(String.format(Locale.US, "DELETE FROM media WHERE mid IN (%s)", ids)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM messages WHERE mid IN(%s)", ids)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM media WHERE mid IN(%s)", ids)).stepThis().dispose(); database.executeFast("DELETE FROM media_counts WHERE 1").stepThis().dispose(); } catch (Exception e) { @@ -2388,7 +2333,7 @@ public class MessagesStorage { SerializedData data = new SerializedData(userData); TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } dialogs.users.add(user); } @@ -2489,7 +2434,7 @@ public class MessagesStorage { state.bindString(2, ""); } if (user.status != null) { - state.bindInteger(3, (user.status.was_online != 0 ? user.status.was_online : user.status.expires)); + state.bindInteger(3, user.status.expires); } else { state.bindInteger(3, 0); } @@ -2667,7 +2612,7 @@ public class MessagesStorage { TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); if (user != null) { if (user.status != null) { - user.status.was_online = user.status.expires = cursor.intValue(1); + user.status.expires = cursor.intValue(1); } dialogs.users.add(user); } @@ -2771,7 +2716,7 @@ public class MessagesStorage { state.bindString(2, ""); } if (user.status != null) { - state.bindInteger(3, (user.status.was_online != 0 ? user.status.was_online : user.status.expires)); + state.bindInteger(3, user.status.expires); } else { state.bindInteger(3, 0); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java b/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java index d2e3567ed..a44ef7664 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NetworkMessage.java @@ -8,8 +8,6 @@ package org.telegram.messenger; -import org.telegram.TL.TLRPC; - public class NetworkMessage { public TLRPC.TL_protoMessage protoMessage; public Object rawRequest; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java b/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java index 5dd298265..c6f93e5f5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/RPCRequest.java @@ -8,9 +8,6 @@ package org.telegram.messenger; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; - import java.util.ArrayList; public class RPCRequest { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java index db3abdc93..924f99f7c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java @@ -14,9 +14,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.IOException; -public class SerializedData { +public class SerializedData extends AbsSerializedData { protected boolean isOut = true; private ByteArrayOutputStream outbuf; private DataOutputStream out; @@ -44,13 +43,13 @@ public class SerializedData { out = new DataOutputStream(outbuf); } - public SerializedData(byte[] data){ + public SerializedData(byte[] data) { isOut = false; inbuf = new ByteArrayInputStream(data); in = new DataInputStream(inbuf); } - public SerializedData(File file) throws IOException { + public SerializedData(File file) throws Exception { FileInputStream is = new FileInputStream(file); byte[] data = new byte[(int)file.length()]; new DataInputStream(is).readFully(data); @@ -74,7 +73,7 @@ public class SerializedData { for(int i = 0; i < 4; i++) { out.write(x >> (i * 8)); } - } catch(IOException gfdsgd) { + } catch(Exception e) { FileLog.e("tmessages", "write int32 error"); } } @@ -89,10 +88,10 @@ public class SerializedData { private void writeInt64(long x, DataOutputStream out) { try { - for(int i = 0; i < 8; i++){ + for(int i = 0; i < 8; i++) { out.write((int)(x >> (i * 8))); } - } catch(IOException gfdsgd) { + } catch(Exception e) { FileLog.e("tmessages", "write int64 error"); } } @@ -127,14 +126,14 @@ public class SerializedData { public int readInt32(boolean[] error) { try { int i = 0; - for(int j = 0; j < 4; j++){ + for(int j = 0; j < 4; j++) { i |= (in.read() << (j * 8)); } if (error != null) { error[0] = false; } return i; - } catch(IOException x) { + } catch(Exception x) { if (error != null) { error[0] = true; } @@ -150,14 +149,14 @@ public class SerializedData { public long readInt64(boolean[] error) { try { long i = 0; - for(int j = 0; j < 8; j++){ + for(int j = 0; j < 8; j++) { i |= ((long)in.read() << (j * 8)); } if (error != null) { error[0] = false; } return i; - } catch(IOException x) { + } catch (Exception x) { if (error != null) { error[0] = true; } @@ -173,7 +172,7 @@ public class SerializedData { } else { len += b.length; } - } catch(Exception x) { + } catch (Exception x) { FileLog.e("tmessages", "write raw error"); } } @@ -185,7 +184,7 @@ public class SerializedData { } else { len += count; } - } catch(Exception x) { + } catch (Exception x) { FileLog.e("tmessages", "write raw error"); } } @@ -217,7 +216,7 @@ public class SerializedData { public void readRaw(byte[] b) { try { in.read(b); - } catch(Exception x) { + } catch (Exception x) { FileLog.e("tmessages", "read raw error"); } } @@ -232,7 +231,7 @@ public class SerializedData { try { int sl = 1; int l = in.read(); - if(l >= 254){ + if(l >= 254) { l = in.read() | (in.read() << 8) | (in.read() << 16); sl = 4; } @@ -244,7 +243,7 @@ public class SerializedData { i++; } return new String(b, "UTF-8"); - } catch(Exception x) { + } catch (Exception x) { FileLog.e("tmessages", "read string error"); } return null; @@ -254,27 +253,31 @@ public class SerializedData { try { int sl = 1; int l = in.read(); - if (l >= 254){ + if (l >= 254) { l = in.read() | (in.read() << 8) | (in.read() << 16); sl = 4; } byte[] b = new byte[l]; in.read(b); int i = sl; - while((l + i) % 4 != 0){ + while((l + i) % 4 != 0) { in.read(); i++; } return b; - } catch(Exception x) { + } catch (Exception x) { FileLog.e("tmessages", "read byte array error"); } return null; } + public ByteBufferDesc readByteBuffer() { + throw new RuntimeException("SerializedData don't support readByteBuffer"); + } + public void writeByteArray(byte[] b) { try { - if (b.length <= 253){ + if (b.length <= 253) { if (!justCalc) { out.write(b.length); } else { @@ -296,7 +299,7 @@ public class SerializedData { len += b.length; } int i = b.length <= 253 ? 1 : 4; - while((b.length + i) % 4 != 0){ + while((b.length + i) % 4 != 0) { if (!justCalc) { out.write(0); } else { @@ -304,12 +307,12 @@ public class SerializedData { } i++; } - } catch(Exception x) { + } catch (Exception x) { FileLog.e("tmessages", "write byte array error"); } } - public void writeString(String s){ + public void writeString(String s) { try { writeByteArray(s.getBytes("UTF-8")); } catch(Exception x) { @@ -319,7 +322,7 @@ public class SerializedData { public void writeByteArray(byte[] b, int offset, int count) { try { - if(count <= 253){ + if(count <= 253) { if (!justCalc) { out.write(count); } else { @@ -341,7 +344,7 @@ public class SerializedData { len += count; } int i = count <= 253 ? 1 : 4; - while ((count + i) % 4 != 0){ + while ((count + i) % 4 != 0) { if (!justCalc) { out.write(0); } else { @@ -349,7 +352,7 @@ public class SerializedData { } i++; } - } catch(Exception x) { + } catch (Exception x) { FileLog.e("tmessages", "write byte array error"); } } diff --git a/TMessagesProj/src/main/java/org/telegram/TL/TLClassStore.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java similarity index 98% rename from TMessagesProj/src/main/java/org/telegram/TL/TLClassStore.java rename to TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java index e67ac98f5..a3a12718a 100644 --- a/TMessagesProj/src/main/java/org/telegram/TL/TLClassStore.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLClassStore.java @@ -6,10 +6,7 @@ * Copyright Nikolai Kudashov, 2013. */ -package org.telegram.TL; - -import org.telegram.messenger.FileLog; -import org.telegram.messenger.SerializedData; +package org.telegram.messenger; import java.util.HashMap; @@ -317,6 +314,7 @@ public class TLClassStore { classStore.put(TLRPC.TL_req_DH_params.constructor, TLRPC.TL_req_DH_params.class); classStore.put(TLRPC.TL_set_client_DH_params.constructor, TLRPC.TL_set_client_DH_params.class); classStore.put(TLRPC.TL_ping.constructor, TLRPC.TL_ping.class); + classStore.put(TLRPC.TL_ping_delay_disconnect.constructor, TLRPC.TL_ping_delay_disconnect.class); classStore.put(TLRPC.TL_destroy_session.constructor, TLRPC.TL_destroy_session.class); classStore.put(TLRPC.TL_destroy_sessions.constructor, TLRPC.TL_destroy_sessions.class); classStore.put(TLRPC.TL_get_future_salts.constructor, TLRPC.TL_get_future_salts.class); @@ -408,6 +406,8 @@ public class TLClassStore { classStore.put(TLRPC.TL_messages_sendEncryptedService.constructor, TLRPC.TL_messages_sendEncryptedService.class); classStore.put(TLRPC.TL_messages_receivedQueue.constructor, TLRPC.TL_messages_receivedQueue.class); classStore.put(TLRPC.TL_upload_saveBigFilePart.constructor, TLRPC.TL_upload_saveBigFilePart.class); + classStore.put(TLRPC.TL_help_support.constructor, TLRPC.TL_help_support.class); + classStore.put(TLRPC.TL_help_getSupport.constructor, TLRPC.TL_help_getSupport.class); classStore.put(TLRPC.TL_msg_container.constructor, TLRPC.TL_msg_container.class); classStore.put(TLRPC.TL_fileEncryptedLocation.constructor, TLRPC.TL_fileEncryptedLocation.class); @@ -434,7 +434,7 @@ public class TLClassStore { return store; } - public TLObject TLdeserialize(SerializedData stream, int constructor) { + public TLObject TLdeserialize(AbsSerializedData stream, int constructor) { try { return TLdeserialize(stream, constructor, null); } catch (Exception e) { @@ -442,7 +442,7 @@ public class TLClassStore { } } - public TLObject TLdeserialize(SerializedData stream, int constructor, TLObject request) { + public TLObject TLdeserialize(AbsSerializedData stream, int constructor, TLObject request) { Class objClass = classStore.get(constructor); if (objClass != null) { try { diff --git a/TMessagesProj/src/main/java/org/telegram/TL/TLObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java similarity index 66% rename from TMessagesProj/src/main/java/org/telegram/TL/TLObject.java rename to TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java index 8d4ced3b0..1aceb8283 100644 --- a/TMessagesProj/src/main/java/org/telegram/TL/TLObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLObject.java @@ -6,16 +6,14 @@ * Copyright Nikolai Kudashov, 2013. */ -package org.telegram.TL; - -import org.telegram.messenger.SerializedData; +package org.telegram.messenger; public class TLObject { public TLObject () { } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { } @@ -23,7 +21,7 @@ public class TLObject { return null; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { } @@ -35,7 +33,11 @@ public class TLObject { return 11; } - public void parseVector(TLRPC.Vector vector, SerializedData data) { + public void parseVector(TLRPC.Vector vector, AbsSerializedData data) { + + } + + public void freeResources() { } } diff --git a/TMessagesProj/src/main/java/org/telegram/TL/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java similarity index 83% rename from TMessagesProj/src/main/java/org/telegram/TL/TLRPC.java rename to TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java index bbc4ab981..9f8e28acc 100644 --- a/TMessagesProj/src/main/java/org/telegram/TL/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java @@ -6,10 +6,7 @@ * Copyright Nikolai Kudashov, 2013. */ -package org.telegram.TL; - -import org.telegram.messenger.ConnectionsManager; -import org.telegram.messenger.SerializedData; +package org.telegram.messenger; import java.util.ArrayList; @@ -24,7 +21,7 @@ public class TLRPC { public static int constructor = 0x37c1011c; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -33,12 +30,12 @@ public class TLRPC { public static int constructor = 0x6153276a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); photo_small.serializeToStream(stream); photo_big.serializeToStream(stream); @@ -56,13 +53,13 @@ public class TLRPC { public static int constructor = 0xa7eff811; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { bad_msg_id = stream.readInt64(); bad_msg_seqno = stream.readInt32(); error_code = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(bad_msg_id); stream.writeInt32(bad_msg_seqno); @@ -74,14 +71,14 @@ public class TLRPC { public static int constructor = 0xedab447b; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { bad_msg_id = stream.readInt64(); bad_msg_seqno = stream.readInt32(); error_code = stream.readInt32(); new_server_salt = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(bad_msg_id); stream.writeInt32(bad_msg_seqno); @@ -96,12 +93,12 @@ public class TLRPC { public int code; public String text; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { code = stream.readInt32(); text = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(code); stream.writeString(text); @@ -117,11 +114,11 @@ public class TLRPC { public static int constructor = 0x560f8935; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(date); } @@ -131,12 +128,12 @@ public class TLRPC { public static int constructor = 0x9493ff32; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { date = stream.readInt32(); file = (EncryptedFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(date); file.serializeToStream(stream); @@ -149,12 +146,12 @@ public class TLRPC { public boolean phone_registered; public boolean phone_invited; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_registered = stream.readBool(); phone_invited = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeBool(phone_registered); stream.writeBool(phone_invited); @@ -166,7 +163,7 @@ public class TLRPC { public ArrayList msg_ids = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -174,7 +171,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = msg_ids.size(); @@ -192,7 +189,7 @@ public class TLRPC { public ArrayList chats = new ArrayList(); public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { full_chat = (TL_chatFull)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -206,7 +203,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); full_chat.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -230,12 +227,12 @@ public class TLRPC { public int user_id; public int expires; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); expires = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt32(expires); @@ -248,12 +245,12 @@ public class TLRPC { public int expires; public User user; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { expires = stream.readInt32(); user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(expires); user.serializeToStream(stream); @@ -271,7 +268,7 @@ public class TLRPC { public static int constructor = 0x8c718e87; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -289,7 +286,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -316,7 +313,7 @@ public class TLRPC { public static int constructor = 0xb446ae3; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { count = stream.readInt32(); stream.readInt32(); int count = stream.readInt32(); @@ -335,7 +332,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(count); stream.writeInt32(0x1cb5c415); @@ -369,7 +366,7 @@ public class TLRPC { public static int constructor = 0x5e2ad36e; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -378,13 +375,13 @@ public class TLRPC { public static int constructor = 0xa43ad8b7; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { msg_id = stream.readInt64(); seq_no = stream.readInt32(); bytes = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(msg_id); stream.writeInt32(seq_no); @@ -396,7 +393,7 @@ public class TLRPC { public static int constructor = 0xcd78e586; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -408,13 +405,13 @@ public class TLRPC { public contacts_ForeignLink foreign_link; public User user; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { my_link = (contacts_MyLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); foreign_link = (contacts_ForeignLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); my_link.serializeToStream(stream); foreign_link.serializeToStream(stream); @@ -431,11 +428,11 @@ public class TLRPC { public static int constructor = 0x9db1bc6d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -445,11 +442,11 @@ public class TLRPC { public static int constructor = 0xbad0e5bb; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); } @@ -467,7 +464,7 @@ public class TLRPC { public static int constructor = 0x4a70994c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); size = stream.readInt32(); @@ -475,7 +472,7 @@ public class TLRPC { key_fingerprint = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -489,7 +486,7 @@ public class TLRPC { public static int constructor = 0xc21f497e; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -502,11 +499,11 @@ public class TLRPC { public static int constructor = 0xe22045fc; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { session_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(session_id); } @@ -516,11 +513,11 @@ public class TLRPC { public static int constructor = 0x62d350c9; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { session_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(session_id); } @@ -542,12 +539,12 @@ public class TLRPC { public static int constructor = 0x5d75a138; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { date = stream.readInt32(); seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(date); stream.writeInt32(seq); @@ -558,7 +555,7 @@ public class TLRPC { public static int constructor = 0xa8fb1981; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -587,7 +584,7 @@ public class TLRPC { intermediate_state = (TL_updates_state)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = new_messages.size(); @@ -627,7 +624,7 @@ public class TLRPC { public static int constructor = 0xf49ca0; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -656,7 +653,7 @@ public class TLRPC { state = (TL_updates_state)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = new_messages.size(); @@ -701,7 +698,7 @@ public class TLRPC { public static int constructor = 0x1117dd5f; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -710,12 +707,12 @@ public class TLRPC { public static int constructor = 0x2049d70c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { _long = stream.readDouble(); lat = stream.readDouble(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeDouble(_long); stream.writeDouble(lat); @@ -733,14 +730,14 @@ public class TLRPC { public static int constructor = 0x8987f311; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); critical = stream.readBool(); url = stream.readString(); text = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeBool(critical); @@ -753,7 +750,7 @@ public class TLRPC { public static int constructor = 0xc45a6536; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -762,11 +759,11 @@ public class TLRPC { public static int constructor = 0x83e5de54; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); } @@ -776,7 +773,7 @@ public class TLRPC { public static int constructor = 0x9f8d60bb; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); from_id = stream.readInt32(); to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); @@ -786,7 +783,7 @@ public class TLRPC { action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt32(from_id); @@ -806,14 +803,14 @@ public class TLRPC { public String first_name; public String last_name; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { client_id = stream.readInt64(); phone = stream.readString(); first_name = stream.readString(); last_name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(client_id); stream.writeString(phone); @@ -828,12 +825,12 @@ public class TLRPC { public long msg_id; public TLObject query; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { msg_id = stream.readInt64(); query = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(msg_id); query.serializeToStream(stream); @@ -857,11 +854,11 @@ public class TLRPC { public static int constructor = 0xa2d24290; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { video = (Video)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); video.serializeToStream(stream); } @@ -871,11 +868,11 @@ public class TLRPC { public static int constructor = 0xc8c45a2a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); photo.serializeToStream(stream); } @@ -885,11 +882,11 @@ public class TLRPC { public static int constructor = 0x2fda2204; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { document = (Document)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); document.serializeToStream(stream); } @@ -899,11 +896,11 @@ public class TLRPC { public static int constructor = 0x56e0d474; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { geo = (GeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); geo.serializeToStream(stream); } @@ -913,7 +910,7 @@ public class TLRPC { public static int constructor = 0x3ded6320; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -922,11 +919,11 @@ public class TLRPC { public static int constructor = 0xc6b68300; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { audio = (Audio)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); audio.serializeToStream(stream); } @@ -936,14 +933,14 @@ public class TLRPC { public static int constructor = 0x5e7d2f39; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); first_name = stream.readString(); last_name = stream.readString(); user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); stream.writeString(first_name); @@ -956,31 +953,37 @@ public class TLRPC { public static int constructor = 0x29632a36; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { bytes = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeByteArray(bytes); } } public static class TL_auth_sentCode extends TLObject { - public static int constructor = 0x2215bcbd; + public static int constructor = 0xefed51d9; public boolean phone_registered; public String phone_code_hash; + public int send_call_timeout; + public boolean is_password; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_registered = stream.readBool(); phone_code_hash = stream.readString(); + send_call_timeout = stream.readInt32(); + is_password = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeBool(phone_registered); stream.writeString(phone_code_hash); + stream.writeInt32(send_call_timeout); + stream.writeBool(is_password); } } @@ -995,7 +998,7 @@ public class TLRPC { public static int constructor = 0x70a68512; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1004,14 +1007,14 @@ public class TLRPC { public static int constructor = 0x8d5e11ee; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { mute_until = stream.readInt32(); sound = stream.readString(); show_previews = stream.readBool(); events_mask = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(mute_until); stream.writeString(sound); @@ -1025,7 +1028,7 @@ public class TLRPC { public ArrayList msg_ids = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1033,7 +1036,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = msg_ids.size(); @@ -1051,13 +1054,13 @@ public class TLRPC { public int wait_after; public int max_wait; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { max_delay = stream.readInt32(); wait_after = stream.readInt32(); max_wait = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(max_delay); stream.writeInt32(wait_after); @@ -1075,7 +1078,7 @@ public class TLRPC { public static int constructor = 0x1c138d15; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1088,7 +1091,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = blocked.size(); @@ -1109,7 +1112,7 @@ public class TLRPC { public static int constructor = 0x900802a1; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { count = stream.readInt32(); stream.readInt32(); int count = stream.readInt32(); @@ -1123,7 +1126,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(count); stream.writeInt32(0x1cb5c415); @@ -1150,12 +1153,12 @@ public class TLRPC { public static int constructor = 0xf3b7acc9; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { lat = stream.readDouble(); _long = stream.readDouble(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeDouble(lat); stream.writeDouble(_long); @@ -1166,7 +1169,7 @@ public class TLRPC { public static int constructor = 0xe4c123d6; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1176,11 +1179,11 @@ public class TLRPC { public String message; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(message); } @@ -1197,11 +1200,11 @@ public class TLRPC { public static int constructor = 0xc0e24635; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { random = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeByteArray(random); } @@ -1211,14 +1214,14 @@ public class TLRPC { public static int constructor = 0x2c221edd; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { g = stream.readInt32(); p = stream.readByteArray(); version = stream.readInt32(); random = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(g); stream.writeByteArray(p); @@ -1231,11 +1234,11 @@ public class TLRPC { public static int constructor = 0x586988d8; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); } @@ -1245,7 +1248,7 @@ public class TLRPC { public static int constructor = 0x427425e7; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); user_id = stream.readInt32(); @@ -1255,7 +1258,7 @@ public class TLRPC { dc_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -1272,14 +1275,14 @@ public class TLRPC { public ArrayList destroy_results = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { int count = stream.readInt32(); for (int a = 0; a < count; a++) { destroy_results.add((DestroySessionRes)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); int count = destroy_results.size(); stream.writeInt32(count); @@ -1298,7 +1301,7 @@ public class TLRPC { public static int constructor = 0x6f8b8cb2; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1311,7 +1314,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = contacts.size(); @@ -1332,7 +1335,7 @@ public class TLRPC { public static int constructor = 0xb74ba9d2; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1347,7 +1350,7 @@ public class TLRPC { public static int constructor = 0x8dca6aa5; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1360,7 +1363,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = photos.size(); @@ -1381,7 +1384,7 @@ public class TLRPC { public static int constructor = 0x15051f54; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { count = stream.readInt32(); stream.readInt32(); int count = stream.readInt32(); @@ -1395,7 +1398,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(count); stream.writeInt32(0x1cb5c415); @@ -1421,14 +1424,14 @@ public class TLRPC { public Photo chat_photo; public PeerNotifySettings notify_settings; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); participants = (ChatParticipants)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); chat_photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); notify_settings = (PeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); participants.serializeToStream(stream); @@ -1443,7 +1446,7 @@ public class TLRPC { public ArrayList msg_ids = new ArrayList(); public String info; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1452,7 +1455,7 @@ public class TLRPC { info = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = msg_ids.size(); @@ -1472,14 +1475,14 @@ public class TLRPC { public boolean show_previews; public int events_mask; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { mute_until = stream.readInt32(); sound = stream.readString(); show_previews = stream.readBool(); events_mask = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(mute_until); stream.writeString(sound); @@ -1492,7 +1495,7 @@ public class TLRPC { public static int constructor = 0x56730bcc; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1506,7 +1509,7 @@ public class TLRPC { public static int constructor = 0xf7c1b13f; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1515,12 +1518,12 @@ public class TLRPC { public static int constructor = 0x655e74ff; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt64(access_hash); @@ -1531,7 +1534,7 @@ public class TLRPC { public static int constructor = 0xb98886cf; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1540,11 +1543,11 @@ public class TLRPC { public static int constructor = 0x86e94f65; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -1560,7 +1563,7 @@ public class TLRPC { public byte[] server_nonce; public byte[] new_nonce; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { pq = stream.readByteArray(); p = stream.readByteArray(); q = stream.readByteArray(); @@ -1569,7 +1572,7 @@ public class TLRPC { new_nonce = stream.readData(32); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeByteArray(pq); stream.writeByteArray(p); @@ -1585,7 +1588,7 @@ public class TLRPC { public ArrayList msg_ids = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1593,7 +1596,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = msg_ids.size(); @@ -1611,7 +1614,7 @@ public class TLRPC { public static int constructor = 0x997275b5; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1620,7 +1623,7 @@ public class TLRPC { public static int constructor = 0xbc799737; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1631,12 +1634,12 @@ public class TLRPC { public int id; public byte[] bytes; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); bytes = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeByteArray(bytes); @@ -1656,7 +1659,7 @@ public class TLRPC { public static int constructor = 0x3e74f5c6; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1681,7 +1684,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -1716,7 +1719,7 @@ public class TLRPC { public static int constructor = 0x969478bb; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -1736,7 +1739,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -1768,7 +1771,7 @@ public class TLRPC { public static int constructor = 0x4a95e84e; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1778,11 +1781,11 @@ public class TLRPC { public InputPeer peer; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); } @@ -1792,7 +1795,7 @@ public class TLRPC { public static int constructor = 0x193b4417; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1802,11 +1805,11 @@ public class TLRPC { public TL_inputGeoChat peer; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); } @@ -1816,7 +1819,7 @@ public class TLRPC { public static int constructor = 0xa429b886; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -1833,12 +1836,12 @@ public class TLRPC { public static int constructor = 0x74dc404d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -1849,12 +1852,12 @@ public class TLRPC { public static int constructor = 0xf5235d55; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -1865,12 +1868,12 @@ public class TLRPC { public static int constructor = 0x3d0364ec; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -1881,12 +1884,12 @@ public class TLRPC { public static int constructor = 0x4e45abe9; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -1897,13 +1900,13 @@ public class TLRPC { public static int constructor = 0x14637196; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { volume_id = stream.readInt64(); local_id = stream.readInt32(); secret = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(volume_id); stream.writeInt32(local_id); @@ -1917,7 +1920,7 @@ public class TLRPC { public Photo photo; public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -1926,7 +1929,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); photo.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -1953,7 +1956,7 @@ public class TLRPC { public static int constructor = 0xf2fb8319; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); first_name = stream.readString(); last_name = stream.readString(); @@ -1963,7 +1966,7 @@ public class TLRPC { status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(first_name); @@ -1979,7 +1982,7 @@ public class TLRPC { public static int constructor = 0x22e8ceb0; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); first_name = stream.readString(); last_name = stream.readString(); @@ -1989,7 +1992,7 @@ public class TLRPC { status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(first_name); @@ -2005,7 +2008,7 @@ public class TLRPC { public static int constructor = 0x5214c89d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); first_name = stream.readString(); last_name = stream.readString(); @@ -2014,7 +2017,7 @@ public class TLRPC { status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(first_name); @@ -2029,13 +2032,13 @@ public class TLRPC { public static int constructor = 0xb29ad7cc; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); first_name = stream.readString(); last_name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(first_name); @@ -2047,7 +2050,7 @@ public class TLRPC { public static int constructor = 0x720535ec; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); first_name = stream.readString(); last_name = stream.readString(); @@ -2057,7 +2060,7 @@ public class TLRPC { inactive = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(first_name); @@ -2083,7 +2086,7 @@ public class TLRPC { public static int constructor = 0x4505f8e1; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); id = stream.readInt32(); from_id = stream.readInt32(); @@ -2092,7 +2095,7 @@ public class TLRPC { media = (MessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(id); @@ -2107,7 +2110,7 @@ public class TLRPC { public static int constructor = 0xd34fa24e; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); id = stream.readInt32(); from_id = stream.readInt32(); @@ -2115,7 +2118,7 @@ public class TLRPC { action = (MessageAction)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(id); @@ -2129,12 +2132,12 @@ public class TLRPC { public static int constructor = 0x60311a9b; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(id); @@ -2147,12 +2150,12 @@ public class TLRPC { public long msg_id; public long ping_id; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { msg_id = stream.readInt64(); ping_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(msg_id); stream.writeInt64(ping_id); @@ -2163,11 +2166,11 @@ public class TLRPC { public static int constructor = 0x7fcb13a8; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); photo.serializeToStream(stream); } @@ -2177,11 +2180,11 @@ public class TLRPC { public static int constructor = 0xb2ae9b0c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -2191,7 +2194,7 @@ public class TLRPC { public static int constructor = 0x95e3fbef; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2200,11 +2203,11 @@ public class TLRPC { public static int constructor = 0x5e3cfc4b; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -2214,7 +2217,7 @@ public class TLRPC { public static int constructor = 0xa6638b9a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { title = stream.readString(); stream.readInt32(); int count = stream.readInt32(); @@ -2223,7 +2226,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(title); stream.writeInt32(0x1cb5c415); @@ -2239,7 +2242,7 @@ public class TLRPC { public static int constructor = 0xb6aef7b0; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2248,11 +2251,11 @@ public class TLRPC { public static int constructor = 0xb5a1ce5a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { title = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(title); } @@ -2262,12 +2265,12 @@ public class TLRPC { public static int constructor = 0x6f038ebc; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { title = stream.readString(); address = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(title); stream.writeString(address); @@ -2278,7 +2281,7 @@ public class TLRPC { public static int constructor = 0xc7d53de; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2291,7 +2294,7 @@ public class TLRPC { public static int constructor = 0x1bea8ce1; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2300,7 +2303,7 @@ public class TLRPC { public static int constructor = 0x133421f8; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2309,11 +2312,11 @@ public class TLRPC { public static int constructor = 0xa7801f47; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { has_phone = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeBool(has_phone); } @@ -2331,13 +2334,13 @@ public class TLRPC { public static int constructor = 0x46dc1fb9; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); new_nonce_hash2 = stream.readData(16); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -2349,13 +2352,13 @@ public class TLRPC { public static int constructor = 0xa69dae02; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); new_nonce_hash3 = stream.readData(16); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -2367,13 +2370,13 @@ public class TLRPC { public static int constructor = 0x3bcbf734; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); new_nonce_hash1 = stream.readData(16); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -2388,7 +2391,7 @@ public class TLRPC { public static int constructor = 0xadd53cb3; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2397,7 +2400,7 @@ public class TLRPC { public static int constructor = 0x6d1ded88; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2408,12 +2411,12 @@ public class TLRPC { public int chat_id; public int distance; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); distance = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(distance); @@ -2432,13 +2435,13 @@ public class TLRPC { public static int constructor = 0xaa48327d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { random_id = stream.readInt64(); random_bytes = stream.readByteArray(); action = (TL_decryptedMessageActionSetMessageTTL)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(random_id); stream.writeByteArray(random_bytes); @@ -2450,14 +2453,14 @@ public class TLRPC { public static int constructor = 0x1f814f1f; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { random_id = stream.readInt64(); random_bytes = stream.readByteArray(); message = stream.readString(); media = (DecryptedMessageMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(random_id); stream.writeByteArray(random_bytes); @@ -2473,7 +2476,7 @@ public class TLRPC { public static int constructor = 0xe86a2c74; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2482,7 +2485,7 @@ public class TLRPC { public static int constructor = 0xf03064d8; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2495,14 +2498,14 @@ public class TLRPC { public long retry_id; public byte[] g_b; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); retry_id = stream.readInt64(); g_b = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -2515,7 +2518,7 @@ public class TLRPC { public static int constructor = 0x5a04a49f; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); user_id = stream.readInt32(); @@ -2529,7 +2532,7 @@ public class TLRPC { h = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -2549,11 +2552,11 @@ public class TLRPC { public static int constructor = 0xc10658a8; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); } @@ -2565,12 +2568,12 @@ public class TLRPC { public int user_id; public int date; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt32(date); @@ -2586,7 +2589,7 @@ public class TLRPC { public static int constructor = 0x72f0eaae; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2595,12 +2598,12 @@ public class TLRPC { public static int constructor = 0x18798952; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -2615,14 +2618,14 @@ public class TLRPC { public long peer; public String data; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { time = stream.readDouble(); type = stream.readString(); peer = stream.readInt64(); data = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeDouble(time); stream.writeString(type); @@ -2638,13 +2641,13 @@ public class TLRPC { public int seq; public int offset; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { pts = stream.readInt32(); seq = stream.readInt32(); offset = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(pts); stream.writeInt32(seq); @@ -2656,11 +2659,11 @@ public class TLRPC { public static int constructor = 0x36f8c871; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); } @@ -2670,7 +2673,7 @@ public class TLRPC { public static int constructor = 0x9efc6326; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); user_id = stream.readInt32(); @@ -2682,7 +2685,7 @@ public class TLRPC { dc_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -2714,13 +2717,13 @@ public class TLRPC { public static int constructor = 0xa6e45987; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); first_name = stream.readString(); last_name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); stream.writeString(first_name); @@ -2732,14 +2735,14 @@ public class TLRPC { public static int constructor = 0x3e46de5d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); thumb = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); file_name = stream.readString(); mime_type = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); thumb.serializeToStream(stream); @@ -2753,11 +2756,11 @@ public class TLRPC { public InputAudio id; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputAudio)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -2768,11 +2771,11 @@ public class TLRPC { public InputDocument id; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputDocument)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -2783,11 +2786,11 @@ public class TLRPC { public InputVideo id; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputVideo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -2797,11 +2800,11 @@ public class TLRPC { public static int constructor = 0xf9c44144; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); geo_point.serializeToStream(stream); } @@ -2811,7 +2814,7 @@ public class TLRPC { public static int constructor = 0x9664f57f; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -2820,7 +2823,7 @@ public class TLRPC { public static int constructor = 0xe628a145; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); thumb = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); duration = stream.readInt32(); @@ -2828,7 +2831,7 @@ public class TLRPC { h = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); thumb.serializeToStream(stream); @@ -2842,11 +2845,11 @@ public class TLRPC { public static int constructor = 0x2dc53a7d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); } @@ -2856,12 +2859,12 @@ public class TLRPC { public static int constructor = 0x61a6d436; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); duration = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); stream.writeInt32(duration); @@ -2872,14 +2875,14 @@ public class TLRPC { public static int constructor = 0x4847d92a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); duration = stream.readInt32(); w = stream.readInt32(); h = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); stream.writeInt32(duration); @@ -2892,13 +2895,13 @@ public class TLRPC { public static int constructor = 0x34e794bd; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); file_name = stream.readString(); mime_type = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); stream.writeString(file_name); @@ -2911,11 +2914,11 @@ public class TLRPC { public InputPhoto id; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -2932,7 +2935,7 @@ public class TLRPC { public static int constructor = 0xbc5863e8; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { count = stream.readInt32(); stream.readInt32(); int count = stream.readInt32(); @@ -2951,7 +2954,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(count); stream.writeInt32(0x1cb5c415); @@ -2979,7 +2982,7 @@ public class TLRPC { public static int constructor = 0xd1526db1; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -2997,7 +3000,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -3032,14 +3035,14 @@ public class TLRPC { public static int constructor = 0xd1f4d35c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); date = stream.readInt32(); pts = stream.readInt32(); seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt32(date); @@ -3052,7 +3055,7 @@ public class TLRPC { public static int constructor = 0xe9db4a3f; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); date = stream.readInt32(); pts = stream.readInt32(); @@ -3064,7 +3067,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt32(date); @@ -3091,14 +3094,14 @@ public class TLRPC { public static int constructor = 0x23734b06; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { random_id = stream.readInt64(); chat_id = stream.readInt32(); date = stream.readInt32(); bytes = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(random_id); stream.writeInt32(chat_id); @@ -3111,7 +3114,7 @@ public class TLRPC { public static int constructor = 0xed18c118; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { random_id = stream.readInt64(); chat_id = stream.readInt32(); date = stream.readInt32(); @@ -3119,7 +3122,7 @@ public class TLRPC { file = (EncryptedFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(random_id); stream.writeInt32(chat_id); @@ -3135,12 +3138,12 @@ public class TLRPC { public int user_id; public int mutual_contacts; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); mutual_contacts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt32(mutual_contacts); @@ -3158,13 +3161,13 @@ public class TLRPC { public static int constructor = 0x79cb045d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); new_nonce_hash = stream.readData(16); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -3176,13 +3179,13 @@ public class TLRPC { public static int constructor = 0xd0e8075c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); encrypted_answer = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -3190,58 +3193,16 @@ public class TLRPC { } } - public static class UserStatus extends TLObject { - public int expires; - public int was_online; - } - - public static class TL_userStatusEmpty extends UserStatus { - public static int constructor = 0x9d05049; - - - public void serializeToStream(SerializedData stream) { - stream.writeInt32(constructor); - } - } - - public static class TL_userStatusOnline extends UserStatus { - public static int constructor = 0xedb93949; - - - public void readParams(SerializedData stream) { - expires = stream.readInt32(); - } - - public void serializeToStream(SerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(expires); - } - } - - public static class TL_userStatusOffline extends UserStatus { - public static int constructor = 0x8c703f; - - - public void readParams(SerializedData stream) { - was_online = stream.readInt32(); - } - - public void serializeToStream(SerializedData stream) { - stream.writeInt32(constructor); - stream.writeInt32(was_online); - } - } - public static class TL_msg_copy extends TLObject { public static int constructor = 0xe06046b2; public TL_protoMessage orig_message; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { orig_message = (TL_protoMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); orig_message.serializeToStream(stream); } @@ -3253,7 +3214,7 @@ public class TLRPC { public ArrayList imported = new ArrayList(); public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -3266,7 +3227,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = imported.size(); @@ -3290,13 +3251,13 @@ public class TLRPC { public int valid_until; public long salt; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { valid_since = stream.readInt32(); valid_until = stream.readInt32(); salt = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(valid_since); stream.writeInt32(valid_until); @@ -3335,13 +3296,13 @@ public class TLRPC { public static int constructor = 0x38fe25b7; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); max_date = stream.readInt32(); date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(max_date); @@ -3353,13 +3314,13 @@ public class TLRPC { public static int constructor = 0x51a48a9a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); my_link = (contacts_MyLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); foreign_link = (contacts_ForeignLink)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); my_link.serializeToStream(stream); @@ -3371,7 +3332,7 @@ public class TLRPC { public static int constructor = 0xc6649e31; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -3380,7 +3341,7 @@ public class TLRPC { pts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -3396,13 +3357,13 @@ public class TLRPC { public static int constructor = 0x6e5f8c22; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); user_id = stream.readInt32(); version = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(user_id); @@ -3414,7 +3375,7 @@ public class TLRPC { public static int constructor = 0xd15de04d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -3423,7 +3384,7 @@ public class TLRPC { pts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -3439,11 +3400,11 @@ public class TLRPC { public static int constructor = 0x6baa8508; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -3453,12 +3414,12 @@ public class TLRPC { public static int constructor = 0x3c46cfe6; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(user_id); @@ -3469,13 +3430,13 @@ public class TLRPC { public static int constructor = 0xda22d9ad; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); first_name = stream.readString(); last_name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeString(first_name); @@ -3488,12 +3449,12 @@ public class TLRPC { public EncryptedMessage message; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = (EncryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); qts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); message.serializeToStream(stream); stream.writeInt32(qts); @@ -3505,12 +3466,12 @@ public class TLRPC { public Message message; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); pts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); message.serializeToStream(stream); stream.writeInt32(pts); @@ -3521,12 +3482,12 @@ public class TLRPC { public static int constructor = 0x4e90bfd6; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); random_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt64(random_id); @@ -3537,7 +3498,7 @@ public class TLRPC { public static int constructor = 0xa92bfe26; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -3546,7 +3507,7 @@ public class TLRPC { pts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -3562,11 +3523,11 @@ public class TLRPC { public static int constructor = 0x1710f156; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); } @@ -3576,7 +3537,7 @@ public class TLRPC { public static int constructor = 0x8e5e9873; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -3584,7 +3545,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = dc_options.size(); @@ -3599,11 +3560,11 @@ public class TLRPC { public static int constructor = 0x7761198; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { participants = (ChatParticipants)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); participants.serializeToStream(stream); } @@ -3613,12 +3574,12 @@ public class TLRPC { public static int constructor = 0xb4a2e88d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat = (EncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); chat.serializeToStream(stream); stream.writeInt32(date); @@ -3629,11 +3590,11 @@ public class TLRPC { public static int constructor = 0x6f690963; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -3643,14 +3604,14 @@ public class TLRPC { public static int constructor = 0x8f06529a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { auth_key_id = stream.readInt64(); date = stream.readInt32(); device = stream.readString(); location = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(auth_key_id); stream.writeInt32(date); @@ -3664,11 +3625,11 @@ public class TLRPC { public GeoChatMessage message; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = (GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); message.serializeToStream(stream); } @@ -3678,14 +3639,14 @@ public class TLRPC { public static int constructor = 0x95313b0c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); date = stream.readInt32(); photo = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); previous = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt32(date); @@ -3698,12 +3659,12 @@ public class TLRPC { public static int constructor = 0x2575bbb9; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt32(date); @@ -3714,14 +3675,14 @@ public class TLRPC { public static int constructor = 0x3a0eeb22; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); user_id = stream.readInt32(); inviter_id = stream.readInt32(); version = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(user_id); @@ -3734,12 +3695,12 @@ public class TLRPC { public static int constructor = 0x1bfbd823; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); status.serializeToStream(stream); @@ -3752,7 +3713,7 @@ public class TLRPC { public ArrayList results = new ArrayList(); public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -3765,7 +3726,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = results.size(); @@ -3792,12 +3753,12 @@ public class TLRPC { public static int constructor = 0x2144ca19; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { error_code = stream.readInt32(); error_message = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(error_code); stream.writeString(error_message); @@ -3808,13 +3769,13 @@ public class TLRPC { public static int constructor = 0x7ae432f5; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { query_id = stream.readInt64(); error_code = stream.readInt32(); error_message = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(query_id); stream.writeInt32(error_code); @@ -3834,12 +3795,12 @@ public class TLRPC { public static int constructor = 0x5a17b5e5; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -3850,13 +3811,13 @@ public class TLRPC { public static int constructor = 0x2dc173c8; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); parts = stream.readInt32(); key_fingerprint = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt32(parts); @@ -3868,7 +3829,7 @@ public class TLRPC { public static int constructor = 0x1837c364; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -3877,14 +3838,14 @@ public class TLRPC { public static int constructor = 0x64bd0306; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); parts = stream.readInt32(); md5_checksum = stream.readString(); key_fingerprint = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt32(parts); @@ -3898,11 +3859,11 @@ public class TLRPC { public int ttl_seconds; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { ttl_seconds = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(ttl_seconds); } @@ -3916,11 +3877,11 @@ public class TLRPC { public static int constructor = 0x6c69efee; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { contact = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeBool(contact); } @@ -3930,7 +3891,7 @@ public class TLRPC { public static int constructor = 0xc240ebd9; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -3939,7 +3900,7 @@ public class TLRPC { public static int constructor = 0xd22a1c60; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -3954,7 +3915,7 @@ public class TLRPC { public byte[] g_a; public int server_time; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); g = stream.readInt32(); @@ -3963,7 +3924,7 @@ public class TLRPC { server_time = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -3981,13 +3942,13 @@ public class TLRPC { public long unique_id; public long server_salt; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { first_msg_id = stream.readInt64(); unique_id = stream.readInt64(); server_salt = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(first_msg_id); stream.writeInt64(unique_id); @@ -4005,7 +3966,7 @@ public class TLRPC { public static int constructor = 0x4f11bae1; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4014,13 +3975,13 @@ public class TLRPC { public static int constructor = 0xd559d8c8; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { photo_id = stream.readInt64(); photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(photo_id); photo_small.serializeToStream(stream); @@ -4042,7 +4003,7 @@ public class TLRPC { public static int constructor = 0x22b56751; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); user_id = stream.readInt32(); @@ -4056,7 +4017,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -4077,11 +4038,11 @@ public class TLRPC { public static int constructor = 0x2331b22d; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); } @@ -4091,7 +4052,7 @@ public class TLRPC { public static int constructor = 0x3bf703dc; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); access_hash = stream.readInt64(); date = stream.readInt32(); @@ -4099,7 +4060,7 @@ public class TLRPC { participant_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt64(access_hash); @@ -4113,11 +4074,11 @@ public class TLRPC { public static int constructor = 0xab7ec0a0; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); } @@ -4127,11 +4088,11 @@ public class TLRPC { public static int constructor = 0x13d6dd27; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); } @@ -4141,7 +4102,7 @@ public class TLRPC { public static int constructor = 0xfa56ce36; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); access_hash = stream.readInt64(); date = stream.readInt32(); @@ -4151,7 +4112,7 @@ public class TLRPC { key_fingerprint = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt64(access_hash); @@ -4167,7 +4128,7 @@ public class TLRPC { public static int constructor = 0xc878527e; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); access_hash = stream.readInt64(); date = stream.readInt32(); @@ -4176,7 +4137,7 @@ public class TLRPC { g_a = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt64(access_hash); @@ -4195,7 +4156,7 @@ public class TLRPC { public ArrayList users = new ArrayList(); public int seq; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = (GeoChatMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -4210,7 +4171,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); message.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -4235,12 +4196,12 @@ public class TLRPC { public int user_id; public boolean mutual; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); mutual = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeBool(mutual); @@ -4248,15 +4209,16 @@ public class TLRPC { } public static class TL_config extends TLObject { - public static int constructor = 0x232d5905; + public static int constructor = 0x2e54dd74; public int date; public boolean test_mode; public int this_dc; public ArrayList dc_options = new ArrayList(); public int chat_size_max; + public int broadcast_size_max; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { date = stream.readInt32(); test_mode = stream.readBool(); this_dc = stream.readInt32(); @@ -4266,9 +4228,10 @@ public class TLRPC { dc_options.add((TL_dcOption)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32())); } chat_size_max = stream.readInt32(); + broadcast_size_max = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(date); stream.writeBool(test_mode); @@ -4280,6 +4243,37 @@ public class TLRPC { dc_option.serializeToStream(stream); } stream.writeInt32(chat_size_max); + stream.writeInt32(broadcast_size_max); + } + } + + public static class TL_help_support extends TLObject { + public static int constructor = 0x17c6b5f6; + + public String phone_number; + public User user; + + public void readParams(AbsSerializedData stream) { + phone_number = stream.readString(); + user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(phone_number); + user.serializeToStream(stream); + } + } + + public static class TL_help_getSupport extends TLObject { + public static int constructor = 0x9cdf08cd; + + public Class responseClass () { + return TL_help_support.class; + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); } } @@ -4292,12 +4286,12 @@ public class TLRPC { public static int constructor = 0x77d440ff; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -4308,7 +4302,7 @@ public class TLRPC { public static int constructor = 0xd95adc84; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4319,7 +4313,7 @@ public class TLRPC { public ArrayList chats = new ArrayList(); public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -4332,7 +4326,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = chats.size(); @@ -4355,7 +4349,7 @@ public class TLRPC { public ArrayList results = new ArrayList(); public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -4368,7 +4362,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = results.size(); @@ -4396,7 +4390,7 @@ public class TLRPC { public static int constructor = 0x7841b415; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); admin_id = stream.readInt32(); stream.readInt32(); @@ -4407,7 +4401,7 @@ public class TLRPC { version = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(admin_id); @@ -4425,11 +4419,11 @@ public class TLRPC { public static int constructor = 0xfd2bb8a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); } @@ -4459,7 +4453,7 @@ public class TLRPC { public static int constructor = 0xb095434b; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { thumb = stream.readByteArray(); thumb_w = stream.readInt32(); thumb_h = stream.readInt32(); @@ -4470,7 +4464,7 @@ public class TLRPC { iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeByteArray(thumb); stream.writeInt32(thumb_w); @@ -4487,12 +4481,12 @@ public class TLRPC { public static int constructor = 0x35480a59; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { lat = stream.readDouble(); _long = stream.readDouble(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeDouble(lat); stream.writeDouble(_long); @@ -4503,14 +4497,14 @@ public class TLRPC { public static int constructor = 0x6080758f; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { duration = stream.readInt32(); size = stream.readInt32(); key = stream.readByteArray(); iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(duration); stream.writeInt32(size); @@ -4523,7 +4517,7 @@ public class TLRPC { public static int constructor = 0x4cee6ef3; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { thumb = stream.readByteArray(); thumb_w = stream.readInt32(); thumb_h = stream.readInt32(); @@ -4535,7 +4529,7 @@ public class TLRPC { iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeByteArray(thumb); stream.writeInt32(thumb_w); @@ -4553,14 +4547,14 @@ public class TLRPC { public static int constructor = 0x588a0a97; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); first_name = stream.readString(); last_name = stream.readString(); user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); stream.writeString(first_name); @@ -4573,7 +4567,7 @@ public class TLRPC { public static int constructor = 0x89f5c4a; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4582,7 +4576,7 @@ public class TLRPC { public static int constructor = 0x32798a8c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { thumb = stream.readByteArray(); thumb_w = stream.readInt32(); thumb_h = stream.readInt32(); @@ -4593,7 +4587,7 @@ public class TLRPC { iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeByteArray(thumb); stream.writeInt32(thumb_w); @@ -4613,13 +4607,13 @@ public class TLRPC { public int inviter_id; public int date; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); inviter_id = stream.readInt32(); date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt32(inviter_id); @@ -4646,13 +4640,13 @@ public class TLRPC { public static int constructor = 0xfb0ccc41; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); title = stream.readString(); date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(title); @@ -4664,7 +4658,7 @@ public class TLRPC { public static int constructor = 0x75eaea5a; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); access_hash = stream.readInt64(); title = stream.readString(); @@ -4678,7 +4672,7 @@ public class TLRPC { version = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt64(access_hash); @@ -4697,7 +4691,7 @@ public class TLRPC { public static int constructor = 0x6e9c9bc7; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); title = stream.readString(); photo = (ChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); @@ -4707,7 +4701,7 @@ public class TLRPC { version = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(title); @@ -4726,7 +4720,7 @@ public class TLRPC { public static int constructor = 0xaa963b05; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4735,7 +4729,7 @@ public class TLRPC { public static int constructor = 0x1081464c; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4744,7 +4738,7 @@ public class TLRPC { public static int constructor = 0xa4f63c0; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4753,7 +4747,7 @@ public class TLRPC { public static int constructor = 0xcae1aadf; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4762,7 +4756,7 @@ public class TLRPC { public static int constructor = 0x4b09ebbc; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4771,7 +4765,7 @@ public class TLRPC { public static int constructor = 0x528a0677; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4780,7 +4774,7 @@ public class TLRPC { public static int constructor = 0x7efe0e; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4789,7 +4783,7 @@ public class TLRPC { public static int constructor = 0x40bc6f52; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4798,7 +4792,7 @@ public class TLRPC { public static int constructor = 0xb3cea0e4; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4810,7 +4804,7 @@ public class TLRPC { public static int constructor = 0x9fc00e65; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4819,7 +4813,7 @@ public class TLRPC { public static int constructor = 0x57e2f66c; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4828,7 +4822,7 @@ public class TLRPC { public static int constructor = 0x9609a51c; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4837,7 +4831,7 @@ public class TLRPC { public static int constructor = 0x56e9f0e4; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4848,51 +4842,30 @@ public class TLRPC { public long req_msg_id; public String info; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { req_msg_id = stream.readInt64(); info = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(req_msg_id); stream.writeString(info); } } - public static class TL_upload_file extends TLObject { - public static int constructor = 0x96a18d5; - - public storage_FileType type; - public int mtime; - public byte[] bytes; - - public void readParams(SerializedData stream) { - type = (storage_FileType)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); - mtime = stream.readInt32(); - bytes = stream.readByteArray(); - } - - public void serializeToStream(SerializedData stream) { - stream.writeInt32(constructor); - type.serializeToStream(stream); - stream.writeInt32(mtime); - stream.writeByteArray(bytes); - } - } - public static class TL_fileLocation extends FileLocation { public static int constructor = 0x53d69076; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { dc_id = stream.readInt32(); volume_id = stream.readInt64(); local_id = stream.readInt32(); secret = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(dc_id); stream.writeInt64(volume_id); @@ -4905,13 +4878,13 @@ public class TLRPC { public static int constructor = 0x7c596b46; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { volume_id = stream.readInt64(); local_id = stream.readInt32(); secret = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(volume_id); stream.writeInt32(local_id); @@ -4929,7 +4902,7 @@ public class TLRPC { public static int constructor = 0x3f4e0648; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -4938,7 +4911,7 @@ public class TLRPC { public static int constructor = 0xff90c417; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -4952,7 +4925,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); message.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -4978,7 +4951,7 @@ public class TLRPC { public ArrayList chats = new ArrayList(); public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -5001,7 +4974,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = results.size(); @@ -5036,12 +5009,12 @@ public class TLRPC { public int chat_id; public long access_hash; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt64(access_hash); @@ -5056,14 +5029,14 @@ public class TLRPC { public int bytes; public TLObject body; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { msg_id = stream.readInt64(); seqno = stream.readInt32(); bytes = stream.readInt32(); body = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(msg_id); stream.writeInt32(seqno); @@ -5085,7 +5058,7 @@ public class TLRPC { public static int constructor = 0x77bfb61b; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { type = stream.readString(); location = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); w = stream.readInt32(); @@ -5093,7 +5066,7 @@ public class TLRPC { size = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(type); location.serializeToStream(stream); @@ -5107,11 +5080,11 @@ public class TLRPC { public static int constructor = 0xe17e23c; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { type = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(type); } @@ -5121,7 +5094,7 @@ public class TLRPC { public static int constructor = 0xe9a734fa; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { type = stream.readString(); location = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); w = stream.readInt32(); @@ -5129,7 +5102,7 @@ public class TLRPC { bytes = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(type); location.serializeToStream(stream); @@ -5144,11 +5117,11 @@ public class TLRPC { public int user_id; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -5165,13 +5138,13 @@ public class TLRPC { public static int constructor = 0xfa4f0bb5; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); parts = stream.readInt32(); name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt32(parts); @@ -5183,14 +5156,14 @@ public class TLRPC { public static int constructor = 0xf52ff27f; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); parts = stream.readInt32(); name = stream.readString(); md5_checksum = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt32(parts); @@ -5212,7 +5185,7 @@ public class TLRPC { public static int constructor = 0xa9af2881; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -5233,7 +5206,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); message.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -5263,7 +5236,7 @@ public class TLRPC { public static int constructor = 0xd07ae726; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { message = (Message)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -5279,7 +5252,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); message.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -5310,7 +5283,7 @@ public class TLRPC { public String real_first_name; public String real_last_name; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user = (User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); link = (TL_contacts_link)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); profile_photo = (Photo)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); @@ -5320,7 +5293,7 @@ public class TLRPC { real_last_name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); user.serializeToStream(stream); link.serializeToStream(stream); @@ -5341,7 +5314,7 @@ public class TLRPC { public int seq; public int unread_count; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { pts = stream.readInt32(); qts = stream.readInt32(); date = stream.readInt32(); @@ -5349,7 +5322,7 @@ public class TLRPC { unread_count = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(pts); stream.writeInt32(qts); @@ -5367,7 +5340,7 @@ public class TLRPC { public byte[] pq; public ArrayList server_public_key_fingerprints = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); pq = stream.readByteArray(); @@ -5378,7 +5351,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -5411,7 +5384,7 @@ public class TLRPC { public static int constructor = 0x2b2fbd4e; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); from_id = stream.readInt32(); chat_id = stream.readInt32(); @@ -5421,7 +5394,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt32(from_id); @@ -5437,7 +5410,7 @@ public class TLRPC { public static int constructor = 0x74ae4240; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -5457,7 +5430,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = updates.size(); @@ -5486,7 +5459,7 @@ public class TLRPC { public static int constructor = 0xd3f45784; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); from_id = stream.readInt32(); message = stream.readString(); @@ -5495,7 +5468,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt32(from_id); @@ -5510,12 +5483,12 @@ public class TLRPC { public static int constructor = 0x78d4dec1; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { update = (Update)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); update.serializeToStream(stream); stream.writeInt32(date); @@ -5526,7 +5499,7 @@ public class TLRPC { public static int constructor = 0x725b04c3; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -5547,7 +5520,7 @@ public class TLRPC { seq = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = updates.size(); @@ -5577,7 +5550,7 @@ public class TLRPC { public static int constructor = 0xe317af7e; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -5588,7 +5561,7 @@ public class TLRPC { public Chat chat; public ArrayList users = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat = (Chat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -5597,7 +5570,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); chat.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -5621,7 +5594,7 @@ public class TLRPC { public static int constructor = 0xccb03657; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); title = stream.readString(); stream.readInt32(); @@ -5632,7 +5605,7 @@ public class TLRPC { color = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(title); @@ -5650,14 +5623,14 @@ public class TLRPC { public static int constructor = 0x63117f24; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); title = stream.readString(); bg_color = stream.readInt32(); color = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(title); @@ -5677,13 +5650,13 @@ public class TLRPC { public static int constructor = 0x809db6df; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { answer_msg_id = stream.readInt64(); bytes = stream.readInt32(); status = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(answer_msg_id); stream.writeInt32(bytes); @@ -5695,14 +5668,14 @@ public class TLRPC { public static int constructor = 0x276d3ec6; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { msg_id = stream.readInt64(); answer_msg_id = stream.readInt64(); bytes = stream.readInt32(); status = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(msg_id); stream.writeInt64(answer_msg_id); @@ -5717,12 +5690,12 @@ public class TLRPC { public int chat_id; public long access_hash; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt64(access_hash); @@ -5739,12 +5712,12 @@ public class TLRPC { public static int constructor = 0xb2e1bf08; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); crop.serializeToStream(stream); @@ -5755,7 +5728,7 @@ public class TLRPC { public static int constructor = 0x1ca48f57; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -5764,12 +5737,12 @@ public class TLRPC { public static int constructor = 0x94254732; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); crop.serializeToStream(stream); @@ -5785,7 +5758,7 @@ public class TLRPC { public static int constructor = 0x5508ec75; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -5794,12 +5767,12 @@ public class TLRPC { public static int constructor = 0xee579652; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -5813,13 +5786,13 @@ public class TLRPC { public int this_dc; public int nearest_dc; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { country = stream.readString(); this_dc = stream.readInt32(); nearest_dc = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(country); stream.writeInt32(this_dc); @@ -5836,7 +5809,7 @@ public class TLRPC { public static int constructor = 0x1cd7bf0d; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -5845,12 +5818,12 @@ public class TLRPC { public static int constructor = 0xfb95c6c4; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -5863,12 +5836,12 @@ public class TLRPC { public int user_id; public long client_id; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); client_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt64(client_id); @@ -5885,11 +5858,11 @@ public class TLRPC { public static int constructor = 0x1023dbe8; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); } @@ -5899,11 +5872,11 @@ public class TLRPC { public static int constructor = 0x179be863; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); } @@ -5913,7 +5886,7 @@ public class TLRPC { public static int constructor = 0x7f3b18ea; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -5922,7 +5895,7 @@ public class TLRPC { public static int constructor = 0x7da07ec9; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -5931,12 +5904,12 @@ public class TLRPC { public static int constructor = 0x9b447325; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = stream.readInt32(); access_hash = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(user_id); stream.writeInt64(access_hash); @@ -5951,14 +5924,14 @@ public class TLRPC { public String ip_address; public int port; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); hostname = stream.readString(); ip_address = stream.readString(); port = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeString(hostname); @@ -5973,12 +5946,12 @@ public class TLRPC { public int layer; public DecryptedMessage message; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { layer = stream.readInt32(); message = (DecryptedMessage)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(layer); message.serializeToStream(stream); @@ -5995,7 +5968,7 @@ public class TLRPC { public static int constructor = 0xade6b004; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -6004,13 +5977,13 @@ public class TLRPC { public static int constructor = 0xd9915325; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { crop_left = stream.readDouble(); crop_top = stream.readDouble(); crop_width = stream.readDouble(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeDouble(crop_left); stream.writeDouble(crop_top); @@ -6030,7 +6003,7 @@ public class TLRPC { public static int constructor = 0x15ba6c40; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -6053,7 +6026,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = dialogs.size(); @@ -6086,7 +6059,7 @@ public class TLRPC { public static int constructor = 0x71e094f3; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { count = stream.readInt32(); stream.readInt32(); int count = stream.readInt32(); @@ -6110,7 +6083,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(count); stream.writeInt32(0x1cb5c415); @@ -6149,11 +6122,11 @@ public class TLRPC { return TL_resPQ.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); } @@ -6173,7 +6146,7 @@ public class TLRPC { return Server_DH_Params.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); p = stream.readByteArray(); @@ -6182,7 +6155,7 @@ public class TLRPC { encrypted_data = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -6204,13 +6177,13 @@ public class TLRPC { return Set_client_DH_params_answer.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { nonce = stream.readData(16); server_nonce = stream.readData(16); encrypted_data = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeRaw(nonce); stream.writeRaw(server_nonce); @@ -6227,11 +6200,11 @@ public class TLRPC { return TL_auth_checkedPhone.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); } @@ -6250,7 +6223,7 @@ public class TLRPC { return TL_auth_sentCode.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); sms_type = stream.readInt32(); api_id = stream.readInt32(); @@ -6258,7 +6231,7 @@ public class TLRPC { lang_code = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); stream.writeInt32(sms_type); @@ -6278,12 +6251,12 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); phone_code_hash = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); stream.writeString(phone_code_hash); @@ -6303,7 +6276,7 @@ public class TLRPC { return TL_auth_authorization.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); phone_code_hash = stream.readString(); phone_code = stream.readString(); @@ -6311,7 +6284,7 @@ public class TLRPC { last_name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); stream.writeString(phone_code_hash); @@ -6332,13 +6305,13 @@ public class TLRPC { return TL_auth_authorization.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { phone_number = stream.readString(); phone_code_hash = stream.readString(); phone_code = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(phone_number); stream.writeString(phone_code_hash); @@ -6354,7 +6327,7 @@ public class TLRPC { return Bool.class; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -6367,7 +6340,7 @@ public class TLRPC { return Bool.class; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -6382,7 +6355,7 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -6391,7 +6364,7 @@ public class TLRPC { message = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = phone_numbers.size(); @@ -6412,11 +6385,11 @@ public class TLRPC { return TL_auth_exportedAuthorization.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { dc_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(dc_id); } @@ -6432,12 +6405,12 @@ public class TLRPC { return TL_auth_authorization.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); bytes = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeByteArray(bytes); @@ -6459,7 +6432,7 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { token_type = stream.readInt32(); token = stream.readString(); device_model = stream.readString(); @@ -6469,7 +6442,7 @@ public class TLRPC { lang_code = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(token_type); stream.writeString(token); @@ -6491,12 +6464,12 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { token_type = stream.readInt32(); token = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(token_type); stream.writeString(token); @@ -6513,12 +6486,12 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputNotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); settings = (TL_inputPeerNotifySettings)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); settings.serializeToStream(stream); @@ -6534,11 +6507,11 @@ public class TLRPC { return PeerNotifySettings.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputNotifyPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); } @@ -6552,7 +6525,7 @@ public class TLRPC { return Bool.class; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -6567,12 +6540,12 @@ public class TLRPC { return User.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { first_name = stream.readString(); last_name = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(first_name); stream.writeString(last_name); @@ -6588,11 +6561,11 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { offline = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeBool(offline); } @@ -6603,7 +6576,7 @@ public class TLRPC { public ArrayList id = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -6611,7 +6584,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = id.size(); @@ -6631,11 +6604,11 @@ public class TLRPC { return TL_userFull.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -6645,7 +6618,7 @@ public class TLRPC { public static int constructor = 0xc4a353ee; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -6659,11 +6632,11 @@ public class TLRPC { return contacts_Contacts.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { hash = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(hash); } @@ -6679,7 +6652,7 @@ public class TLRPC { return TL_contacts_importedContacts.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -6688,7 +6661,7 @@ public class TLRPC { replace = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = contacts.size(); @@ -6710,12 +6683,12 @@ public class TLRPC { return TL_contacts_found.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { q = stream.readString(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(q); stream.writeInt32(limit); @@ -6731,11 +6704,11 @@ public class TLRPC { return TL_contacts_suggested.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(limit); } @@ -6750,11 +6723,11 @@ public class TLRPC { return TL_contacts_link.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -6769,7 +6742,7 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -6777,7 +6750,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = id.size(); @@ -6797,11 +6770,11 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -6816,11 +6789,11 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); } @@ -6836,12 +6809,12 @@ public class TLRPC { return contacts_Blocked.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { offset = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(offset); stream.writeInt32(limit); @@ -6857,7 +6830,7 @@ public class TLRPC { return messages_Messages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -6865,7 +6838,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = id.size(); @@ -6887,13 +6860,13 @@ public class TLRPC { return messages_Dialogs.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { offset = stream.readInt32(); max_id = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(offset); stream.writeInt32(max_id); @@ -6913,14 +6886,14 @@ public class TLRPC { return messages_Messages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); offset = stream.readInt32(); max_id = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(offset); @@ -6945,7 +6918,7 @@ public class TLRPC { return messages_Messages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); q = stream.readString(); filter = (MessagesFilter)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); @@ -6956,7 +6929,7 @@ public class TLRPC { limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeString(q); @@ -6980,13 +6953,13 @@ public class TLRPC { return TL_messages_affectedHistory.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); max_id = stream.readInt32(); offset = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(max_id); @@ -7004,12 +6977,12 @@ public class TLRPC { return TL_messages_affectedHistory.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); offset = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(offset); @@ -7026,12 +6999,12 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); typing = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeBool(typing); @@ -7049,13 +7022,13 @@ public class TLRPC { return messages_SentMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); message = stream.readString(); random_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeString(message); @@ -7074,13 +7047,13 @@ public class TLRPC { return messages_StatedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); random_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); media.serializeToStream(stream); @@ -7098,7 +7071,7 @@ public class TLRPC { return messages_StatedMessages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); stream.readInt32(); int count = stream.readInt32(); @@ -7107,7 +7080,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -7128,7 +7101,7 @@ public class TLRPC { return TL_messages_chats.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -7136,7 +7109,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = id.size(); @@ -7156,11 +7129,11 @@ public class TLRPC { return TL_messages_chatFull.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); } @@ -7176,12 +7149,12 @@ public class TLRPC { return messages_StatedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); title = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeString(title); @@ -7198,12 +7171,12 @@ public class TLRPC { return messages_StatedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); photo = (InputChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); photo.serializeToStream(stream); @@ -7221,13 +7194,13 @@ public class TLRPC { return messages_StatedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); fwd_limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); user_id.serializeToStream(stream); @@ -7245,12 +7218,12 @@ public class TLRPC { return messages_StatedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); user_id.serializeToStream(stream); @@ -7267,7 +7240,7 @@ public class TLRPC { return messages_StatedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -7276,7 +7249,7 @@ public class TLRPC { title = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = users.size(); @@ -7296,7 +7269,7 @@ public class TLRPC { return TL_updates_state.class; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -7312,13 +7285,13 @@ public class TLRPC { return updates_Difference.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { pts = stream.readInt32(); date = stream.readInt32(); qts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(pts); stream.writeInt32(date); @@ -7336,12 +7309,12 @@ public class TLRPC { return UserProfilePhoto.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = (InputPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); id.serializeToStream(stream); crop.serializeToStream(stream); @@ -7360,14 +7333,14 @@ public class TLRPC { return TL_photos_photo.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file = (InputFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); caption = stream.readString(); geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); crop = (InputPhotoCrop)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); file.serializeToStream(stream); stream.writeString(caption); @@ -7387,13 +7360,13 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file_id = stream.readInt64(); file_part = stream.readInt32(); bytes = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(file_id); stream.writeInt32(file_part); @@ -7412,13 +7385,13 @@ public class TLRPC { return TL_upload_file.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { location = (InputFileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); offset = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); location.serializeToStream(stream); stream.writeInt32(offset); @@ -7434,7 +7407,7 @@ public class TLRPC { return TL_config.class; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -7447,7 +7420,7 @@ public class TLRPC { return TL_nearestDc.class; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -7464,14 +7437,14 @@ public class TLRPC { return help_AppUpdate.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { device_model = stream.readString(); system_version = stream.readString(); app_version = stream.readString(); lang_code = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(device_model); stream.writeString(system_version); @@ -7489,7 +7462,7 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -7497,7 +7470,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = events.size(); @@ -7517,11 +7490,11 @@ public class TLRPC { return TL_help_inviteText.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { lang_code = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(lang_code); } @@ -7539,14 +7512,14 @@ public class TLRPC { return photos_Photos.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); offset = stream.readInt32(); max_id = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); user_id.serializeToStream(stream); stream.writeInt32(offset); @@ -7566,13 +7539,13 @@ public class TLRPC { return messages_StatedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (InputPeer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); id = stream.readInt32(); random_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(id); @@ -7591,7 +7564,7 @@ public class TLRPC { return messages_StatedMessages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -7601,7 +7574,7 @@ public class TLRPC { media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = contacts.size(); @@ -7625,13 +7598,13 @@ public class TLRPC { return TL_geochats_located.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); radius = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); geo_point.serializeToStream(stream); stream.writeInt32(radius); @@ -7649,12 +7622,12 @@ public class TLRPC { return geochats_Messages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { offset = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(offset); stream.writeInt32(limit); @@ -7670,11 +7643,11 @@ public class TLRPC { return TL_geochats_statedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); } @@ -7689,11 +7662,11 @@ public class TLRPC { return TL_messages_chatFull.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); } @@ -7710,13 +7683,13 @@ public class TLRPC { return TL_geochats_statedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); title = stream.readString(); address = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeString(title); @@ -7734,12 +7707,12 @@ public class TLRPC { return TL_geochats_statedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); photo = (InputChatPhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); photo.serializeToStream(stream); @@ -7762,7 +7735,7 @@ public class TLRPC { return geochats_Messages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); q = stream.readString(); filter = (MessagesFilter)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); @@ -7773,7 +7746,7 @@ public class TLRPC { limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeString(q); @@ -7798,14 +7771,14 @@ public class TLRPC { return geochats_Messages.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); offset = stream.readInt32(); max_id = stream.readInt32(); limit = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(offset); @@ -7824,12 +7797,12 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); typing = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeBool(typing); @@ -7847,13 +7820,13 @@ public class TLRPC { return TL_geochats_statedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); message = stream.readString(); random_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeString(message); @@ -7872,13 +7845,13 @@ public class TLRPC { return TL_geochats_statedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputGeoChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); media = (InputMedia)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); random_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); media.serializeToStream(stream); @@ -7898,14 +7871,14 @@ public class TLRPC { return TL_geochats_statedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { title = stream.readString(); geo_point = (InputGeoPoint)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); address = stream.readString(); venue = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(title); geo_point.serializeToStream(stream); @@ -7924,12 +7897,12 @@ public class TLRPC { return messages_DhConfig.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { version = stream.readInt32(); random_length = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(version); stream.writeInt32(random_length); @@ -7947,13 +7920,13 @@ public class TLRPC { return EncryptedChat.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { user_id = (InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); random_id = stream.readInt32(); g_a = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); user_id.serializeToStream(stream); stream.writeInt32(random_id); @@ -7972,13 +7945,13 @@ public class TLRPC { return EncryptedChat.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); g_b = stream.readByteArray(); key_fingerprint = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeByteArray(g_b); @@ -7995,11 +7968,11 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { chat_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); } @@ -8015,12 +7988,12 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); typing = stream.readBool(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeBool(typing); @@ -8037,12 +8010,12 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); max_date = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(max_date); @@ -8060,13 +8033,13 @@ public class TLRPC { return messages_SentEncryptedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); random_id = stream.readInt64(); data = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt64(random_id); @@ -8086,14 +8059,14 @@ public class TLRPC { return messages_SentEncryptedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); random_id = stream.readInt64(); data = stream.readByteArray(); file = (InputEncryptedFile)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt64(random_id); @@ -8113,13 +8086,13 @@ public class TLRPC { return messages_SentEncryptedMessage.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (TL_inputEncryptedChat)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); random_id = stream.readInt64(); data = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt64(random_id); @@ -8139,14 +8112,14 @@ public class TLRPC { return Bool.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { file_id = stream.readInt64(); file_part = stream.readInt32(); file_total_parts = stream.readInt32(); bytes = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(file_id); stream.writeInt32(file_part); @@ -8157,6 +8130,69 @@ public class TLRPC { //manually created + public static class UserStatus extends TLObject { + public int expires; + } + + public static class TL_userStatusEmpty extends UserStatus { + public static int constructor = 0x9d05049; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_userStatusOnline extends UserStatus { + public static int constructor = 0xedb93949; + + + public void readParams(AbsSerializedData stream) { + expires = stream.readInt32(); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(expires); + } + } + + public static class TL_userStatusOffline extends UserStatus { + public static int constructor = 0x8c703f; + + + public void readParams(AbsSerializedData stream) { + expires = stream.readInt32(); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(expires); + } + } + + public static class TL_upload_file extends TLObject { + public static int constructor = 0x96a18d5; + + public storage_FileType type; + public int mtime; + public ByteBufferDesc bytes; + + public void readParams(AbsSerializedData stream) { + type = (storage_FileType)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); + mtime = stream.readInt32(); + bytes = stream.readByteBuffer(); + } + + @Override + public void freeResources() { + if (bytes != null) { + BuffersStorage.Instance.reuseFreeBuffer(bytes); + bytes = null; + } + } + } + public static class TL_messages_receivedQueue extends TLObject { public static int constructor = 0x55a5bb66; @@ -8166,18 +8202,18 @@ public class TLRPC { return Vector.class; } - public void parseVector(Vector vector, SerializedData data) { + public void parseVector(Vector vector, AbsSerializedData data) { int size = data.readInt32(); for (int a = 0; a < size; a++) { vector.objects.add(data.readInt64()); } } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { max_qts = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(max_qts); } @@ -8190,11 +8226,11 @@ public class TLRPC { return Vector.class; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } - public void parseVector(Vector vector, SerializedData data) { + public void parseVector(Vector vector, AbsSerializedData data) { int size = data.readInt32(); for (int a = 0; a < size; a++) { vector.objects.add(TLClassStore.Instance().TLdeserialize(data, data.readInt32())); @@ -8215,11 +8251,11 @@ public class TLRPC { return TL_futuresalts.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { num = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(num); } @@ -8238,11 +8274,11 @@ public class TLRPC { return RpcDropAnswer.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { req_msg_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(req_msg_id); } @@ -8253,7 +8289,7 @@ public class TLRPC { public static int constructor = 0x73f1f8dc; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { messages = new ArrayList(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -8268,7 +8304,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(messages.size()); for (TLObject obj : messages) { @@ -8287,17 +8323,24 @@ public class TLRPC { public long req_msg_id; public TLObject result; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { req_msg_id = stream.readInt64(); TLObject request = ConnectionsManager.Instance.getRequestWithMessageId(req_msg_id); result = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32(), request); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(req_msg_id); result.serializeToStream(stream); } + + @Override + public void freeResources() { + if (result != null) { + result.freeResources(); + } + } } public static class TL_futuresalts extends TLObject { @@ -8307,7 +8350,7 @@ public class TLRPC { public int now; public ArrayList salts = new ArrayList(); - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { req_msg_id = stream.readInt64(); now = stream.readInt32(); int count = stream.readInt32(); @@ -8317,7 +8360,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(req_msg_id); stream.writeInt32(now); @@ -8334,11 +8377,11 @@ public class TLRPC { public byte[] packed_data; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { packed_data = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeByteArray(packed_data); } @@ -8369,7 +8412,7 @@ public class TLRPC { public static int constructor = 0x5f46804; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); fwd_from_id = stream.readInt32(); fwd_date = stream.readInt32(); @@ -8383,9 +8426,12 @@ public class TLRPC { if (id < 0) { fwd_msg_id = stream.readInt32(); } + if (id < 0 || (media != null && !(media instanceof TL_messageMediaEmpty) && message != null && message.length() != 0 && message.equals("-1"))) { + attachPath = stream.readString(); + } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt32(fwd_from_id); @@ -8400,13 +8446,14 @@ public class TLRPC { if (id < 0) { stream.writeInt32(fwd_msg_id); } + stream.writeString(attachPath); } } public static class TL_message extends Message { public static int constructor = 0x22eb6aba; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); from_id = stream.readInt32(); to_id = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); @@ -8420,7 +8467,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); stream.writeInt32(from_id); @@ -8443,7 +8490,7 @@ public class TLRPC { return Vector.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -8451,7 +8498,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = id.size(); @@ -8461,7 +8508,7 @@ public class TLRPC { } } - public void parseVector(Vector vector, SerializedData data) { + public void parseVector(Vector vector, AbsSerializedData data) { int size = data.readInt32(); for (int a = 0; a < size; a++) { vector.objects.add(data.readInt32()); @@ -8478,7 +8525,7 @@ public class TLRPC { return Vector.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { stream.readInt32(); int count = stream.readInt32(); for (int a = 0; a < count; a++) { @@ -8486,7 +8533,7 @@ public class TLRPC { } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(0x1cb5c415); int count = id.size(); @@ -8496,7 +8543,7 @@ public class TLRPC { } } - public void parseVector(Vector vector, SerializedData data) { + public void parseVector(Vector vector, AbsSerializedData data) { int size = data.readInt32(); for (int a = 0; a < size; a++) { vector.objects.add(data.readInt32()); @@ -8513,16 +8560,16 @@ public class TLRPC { return Vector.class; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { max_id = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(max_id); } - public void parseVector(Vector vector, SerializedData data) { + public void parseVector(Vector vector, AbsSerializedData data) { int size = data.readInt32(); for (int a = 0; a < size; a++) { vector.objects.add(data.readInt32()); @@ -8539,7 +8586,7 @@ public class TLRPC { public static int constructor = 0x200250ba; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); first_name = "DELETED"; @@ -8548,7 +8595,7 @@ public class TLRPC { status = new TL_userStatusEmpty(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); } @@ -8558,13 +8605,13 @@ public class TLRPC { public static int constructor = 0x9ba2d800; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); title = "DELETED"; } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(id); } @@ -8574,12 +8621,12 @@ public class TLRPC { public static int constructor = 0x990d1493; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { photo_small = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); photo_big = (FileLocation)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); photo_small.serializeToStream(stream); photo_big.serializeToStream(stream); @@ -8599,16 +8646,42 @@ public class TLRPC { return 0; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { ping_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(ping_id); } } + public static class TL_ping_delay_disconnect extends TLObject { + public static int constructor = 0xf3427b8c; + + public long ping_id; + public int disconnect_delay; + + public Class responseClass () { + return TL_pong.class; + } + + public int layer () { + return 0; + } + + public void readParams(AbsSerializedData stream) { + ping_id = stream.readInt64(); + disconnect_delay = stream.readInt32(); + } + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(ping_id); + stream.writeInt32(disconnect_delay); + } + } + public static class TL_destroy_session extends TLObject { public static int constructor = 0xe7512126; @@ -8622,11 +8695,11 @@ public class TLRPC { return 0; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { session_id = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(session_id); } @@ -8645,14 +8718,14 @@ public class TLRPC { return 0; } - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { int count = stream.readInt32(); for (int a = 0; a < count; a++) { session_ids.add(stream.readInt64()); } } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); int count = session_ids.size(); stream.writeInt32(count); @@ -8672,13 +8745,13 @@ public class TLRPC { public long id; public int last_read; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { peer = (Peer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); top_message = stream.readInt32(); unread_count = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(top_message); @@ -8714,7 +8787,7 @@ public class TLRPC { public static int constructor = 0x55555554; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { dc_id = stream.readInt32(); volume_id = stream.readInt64(); local_id = stream.readInt32(); @@ -8723,7 +8796,7 @@ public class TLRPC { iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(dc_id); stream.writeInt64(volume_id); @@ -8792,11 +8865,11 @@ public class TLRPC { public static class TL_messageActionTTLChange extends MessageAction { public static int constructor = 0x55555552; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { ttl = stream.readInt32(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(ttl); } @@ -8806,7 +8879,7 @@ public class TLRPC { public static int constructor = 0x55555556; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); user_id = stream.readInt32(); @@ -8820,7 +8893,7 @@ public class TLRPC { iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -8840,7 +8913,7 @@ public class TLRPC { public static int constructor = 0x55555553; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); user_id = stream.readInt32(); @@ -8856,7 +8929,7 @@ public class TLRPC { iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -8878,7 +8951,7 @@ public class TLRPC { public static int constructor = 0x555555F6; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt64(); access_hash = stream.readInt64(); user_id = stream.readInt32(); @@ -8890,7 +8963,7 @@ public class TLRPC { iv = stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -8907,11 +8980,11 @@ public class TLRPC { public static class TL_messageActionUserUpdatedPhoto extends MessageAction { public static int constructor = 0x55555551; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { newUserPhoto = (UserProfilePhoto)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); newUserPhoto.serializeToStream(stream); } @@ -8920,11 +8993,11 @@ public class TLRPC { public static class TL_messageActionUserJoined extends MessageAction { public static int constructor = 0x55555550; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); } } @@ -8932,24 +9005,24 @@ public class TLRPC { public static class TL_messageActionLoginUnknownLocation extends MessageAction { public static int constructor = 0x555555F5; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { title = stream.readString(); address = stream.readString(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeString(title); stream.writeString(address); } } - public static class invokeWithLayer11 extends TLObject { - public static int constructor = 0xa6b88fdf; + public static class invokeWithLayer12 extends TLObject { + public static int constructor = 0xdda60d3c; public TLObject query; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); query.serializeToStream(stream); } @@ -8965,7 +9038,7 @@ public class TLRPC { public String lang_code; public TLObject query; - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(api_id); stream.writeString(device_model); @@ -8982,12 +9055,12 @@ public class TLRPC { public int layer; public TLObject message; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { layer = stream.readInt32(); message = TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(layer); message.serializeToStream(stream); @@ -8998,7 +9071,7 @@ public class TLRPC { public static int constructor = 0x6601d14f; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); access_hash = stream.readInt64(); date = stream.readInt32(); @@ -9009,7 +9082,7 @@ public class TLRPC { key_fingerprint = stream.readInt64(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(TL_encryptedChat.constructor); stream.writeInt32(id); stream.writeInt64(access_hash); @@ -9025,7 +9098,7 @@ public class TLRPC { public static int constructor = 0xfda9a7b7; - public void readParams(SerializedData stream) { + public void readParams(AbsSerializedData stream) { id = stream.readInt32(); access_hash = stream.readInt64(); date = stream.readInt32(); @@ -9035,7 +9108,7 @@ public class TLRPC { stream.readByteArray(); } - public void serializeToStream(SerializedData stream) { + public void serializeToStream(AbsSerializedData stream) { stream.writeInt32(TL_encryptedChatRequested.constructor); stream.writeInt32(id); stream.writeInt64(access_hash); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java index f111120c6..8f1aa83e7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java @@ -33,7 +33,7 @@ public class TcpConnection extends PyroClientAdapter { public abstract void tcpConnectionClosed(TcpConnection connection); public abstract void tcpConnectionConnected(TcpConnection connection); public abstract void tcpConnectionQuiackAckReceived(TcpConnection connection, int ack); - public abstract void tcpConnectionReceivedData(TcpConnection connection, byte[] data); + public abstract void tcpConnectionReceivedData(TcpConnection connection, ByteBufferDesc data, int length); public abstract void tcpConnectionProgressChanged(TcpConnection connection, long messageId, int currentSize, int length); } @@ -46,13 +46,14 @@ public class TcpConnection extends PyroClientAdapter { private int datacenterId; private int failedConnectionCount; public TcpConnectionDelegate delegate; - private ByteBuffer restOfTheData; + private ByteBufferDesc restOfTheData; private long lastMessageId = 0; private boolean hasSomeDataSinceLastConnect = false; private int willRetryConnectCount = 5; private boolean isNextPort = false; private final Integer timerSync = 1; private boolean wasConnected; + private int lastPacketLength; public int transportRequestClass; @@ -64,6 +65,7 @@ public class TcpConnection extends PyroClientAdapter { if (selector == null) { selector = new PyroSelector(); selector.spawnNetworkThread("network thread"); + BuffersStorage storage = BuffersStorage.Instance; } datacenterId = did; connectionState = TcpConnectionState.TcpConnectionStageIdle; @@ -103,7 +105,11 @@ public class TcpConnection extends PyroClientAdapter { hostPort = datacenter.getCurrentPort(); FileLog.d("tmessages", String.format(TcpConnection.this + " Connecting (%s:%d)", hostAddress, hostPort)); firstPacket = true; - restOfTheData = null; + if (restOfTheData != null) { + BuffersStorage.Instance.reuseFreeBuffer(restOfTheData); + restOfTheData = null; + } + lastPacketLength = 0; wasConnected = false; hasSomeDataSinceLastConnect = false; if (client != null) { @@ -218,7 +224,11 @@ public class TcpConnection extends PyroClientAdapter { }); } firstPacket = true; - restOfTheData = null; + if (restOfTheData != null) { + BuffersStorage.Instance.reuseFreeBuffer(restOfTheData); + restOfTheData = null; + } + lastPacketLength = 0; channelToken = 0; wasConnected = false; } @@ -294,15 +304,71 @@ public class TcpConnection extends PyroClientAdapter { } private void readData(ByteBuffer buffer) throws Exception { + buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.rewind(); + + ByteBuffer parseLaterBuffer = null; if (restOfTheData != null) { - ByteBuffer newBuffer = ByteBuffer.allocate(restOfTheData.limit() + buffer.limit()); - newBuffer.put(restOfTheData); - newBuffer.put(buffer); - buffer = newBuffer; - restOfTheData = null; + if (lastPacketLength == 0) { + //FileLog.e("tmessages", this + " write addition data to restOfTheData"); + if (restOfTheData.capacity() - restOfTheData.position() >= buffer.limit()) { + restOfTheData.limit(restOfTheData.position() + buffer.limit()); + restOfTheData.put(buffer); + buffer = restOfTheData.buffer; + //FileLog.e("tmessages", this + " no need to recreate buffer"); + } else { + ByteBufferDesc newBuffer = BuffersStorage.Instance.getFreeBuffer(restOfTheData.limit() + buffer.limit()); + restOfTheData.rewind(); + newBuffer.put(restOfTheData.buffer); + newBuffer.put(buffer); + buffer = newBuffer.buffer; + BuffersStorage.Instance.reuseFreeBuffer(restOfTheData); + restOfTheData = newBuffer; + //FileLog.e("tmessages", this + " NEED to recreate buffer"); + } + } else { + //FileLog.e("tmessages", this + " write buffer to restOfTheData buffer of len = " + lastPacketLength); + int len = 0; + if (lastPacketLength - restOfTheData.position() <= buffer.limit()) { + len = lastPacketLength - restOfTheData.position(); + //FileLog.e("tmessages", this + " received buffer - OK!"); + } else { + len = buffer.limit(); + //FileLog.e("tmessages", this + " received buffer less than need"); + } + int oldLimit = buffer.limit(); + buffer.limit(len); + restOfTheData.put(buffer); + buffer.limit(oldLimit); + if (restOfTheData.position() != lastPacketLength) { + //FileLog.e("tmessages", this + " don't get much data to restOfTheData"); + if (lastMessageId != -1 && lastMessageId != 0) { + if (delegate != null) { + final TcpConnectionDelegate finalDelegate = delegate; + final int arg2 = restOfTheData.position(); + final int arg3 = lastPacketLength; + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + finalDelegate.tcpConnectionProgressChanged(TcpConnection.this, lastMessageId, arg2, arg3); + } + }); + } + } + return; + } else { + //FileLog.e("tmessages", this + " get much data to restOfTheData - OK!"); + if (buffer.hasRemaining()) { + parseLaterBuffer = buffer; + //FileLog.e("tmessages", this + " something remain in the received buffer"); + } else { + parseLaterBuffer = null; + } + buffer = restOfTheData.buffer; + } + } } - buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.rewind(); while (buffer.hasRemaining()) { @@ -310,7 +376,7 @@ public class TcpConnection extends PyroClientAdapter { Datacenter datacenter = ConnectionsManager.Instance.datacenterWithId(datacenterId); datacenter.storeCurrentAddressAndPortNum(); isNextPort = false; - client.setTimeout(20000); + client.setTimeout(25000); } hasSomeDataSinceLastConnect = true; @@ -321,9 +387,16 @@ public class TcpConnection extends PyroClientAdapter { if ((fByte & (1 << 7)) != 0) { buffer.reset(); if (buffer.remaining() < 4) { - restOfTheData = ByteBuffer.allocate(buffer.remaining()); + ByteBufferDesc reuseLater = restOfTheData; + restOfTheData = BuffersStorage.Instance.getFreeBuffer(16384); restOfTheData.put(buffer); - restOfTheData.rewind(); + restOfTheData.limit(restOfTheData.position()); + lastPacketLength = 0; + //FileLog.e("tmessages", this + " 1 - size less than 4 bytes - write to free buffer"); + if (reuseLater != null) { + BuffersStorage.Instance.reuseFreeBuffer(reuseLater); + //FileLog.e("tmessages", this + " 1 - reuse later buffer1"); + } break; } buffer.order(ByteOrder.BIG_ENDIAN); @@ -346,16 +419,27 @@ public class TcpConnection extends PyroClientAdapter { } else { buffer.reset(); if (buffer.remaining() < 4) { - restOfTheData = ByteBuffer.allocate(buffer.remaining()); - restOfTheData.put(buffer); - restOfTheData.rewind(); + //FileLog.e("tmessages", this + " 2 - size less than 4 bytes - write to free buffer"); + if (restOfTheData == null || restOfTheData != null && restOfTheData.position() != 0) { + ByteBufferDesc reuseLater = restOfTheData; + restOfTheData = BuffersStorage.Instance.getFreeBuffer(16384); + restOfTheData.put(buffer); + restOfTheData.limit(restOfTheData.position()); + lastPacketLength = 0; + if (reuseLater != null) { + BuffersStorage.Instance.reuseFreeBuffer(reuseLater); + //FileLog.e("tmessages", this + " 2 - reuse later buffer1"); + } + } else { + restOfTheData.position(restOfTheData.limit()); + } break; } currentPacketLength = (buffer.getInt() >> 8) * 4; } if (currentPacketLength % 4 != 0 || currentPacketLength > 2 * 1024 * 1024) { - FileLog.e("tmessages", "Invalid packet length"); + //FileLog.e("tmessages", "Invalid packet length"); reconnect(); return; } @@ -388,26 +472,68 @@ public class TcpConnection extends PyroClientAdapter { } } } - buffer.reset(); - restOfTheData = ByteBuffer.allocate(buffer.remaining()); - restOfTheData.order(ByteOrder.LITTLE_ENDIAN); - restOfTheData.put(buffer); - restOfTheData.rewind(); + + ByteBufferDesc reuseLater = null; + int len = currentPacketLength + (fByte != 0x7f ? 1 : 4); + if (restOfTheData != null && restOfTheData.capacity() < len) { + reuseLater = restOfTheData; + restOfTheData = null; + //FileLog.e("tmessages", this + " not enough space for len, recreate buffer = " + len); + } + if (restOfTheData == null) { + //FileLog.e("tmessages", this + " write to restOfTheData, get buffer len = " + len); + buffer.reset(); + restOfTheData = BuffersStorage.Instance.getFreeBuffer(len); + restOfTheData.put(buffer); + } else { + restOfTheData.position(restOfTheData.limit()); + restOfTheData.limit(len); + } + lastPacketLength = len; + if (reuseLater != null) { + BuffersStorage.Instance.reuseFreeBuffer(reuseLater); + //FileLog.e("tmessages", this + " 3 - reuse later buffer1"); + } return; } - final byte[] packetData = new byte[currentPacketLength]; - buffer.get(packetData); + final int length = currentPacketLength; + final ByteBufferDesc toProceed = BuffersStorage.Instance.getFreeBuffer(currentPacketLength); + int old = buffer.limit(); + buffer.limit(buffer.position() + currentPacketLength); + toProceed.put(buffer); + buffer.limit(old); + toProceed.rewind(); if (delegate != null) { final TcpConnectionDelegate finalDelegate = delegate; Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - finalDelegate.tcpConnectionReceivedData(TcpConnection.this, packetData); + finalDelegate.tcpConnectionReceivedData(TcpConnection.this, toProceed, length); + BuffersStorage.Instance.reuseFreeBuffer(toProceed); } }); } + + if (restOfTheData != null) { + if (lastPacketLength != 0 && restOfTheData.position() == lastPacketLength || lastPacketLength == 0 && !restOfTheData.hasRemaining()) { + BuffersStorage.Instance.reuseFreeBuffer(restOfTheData); + restOfTheData = null; + //FileLog.e("tmessages", this + " restOfTheData parsed null it"); + } else { + restOfTheData.compact(); + restOfTheData.limit(restOfTheData.position()); + restOfTheData.position(0); + //FileLog.e("tmessages", this + " restOfTheData NOT parsed, compact"); + } + } + + if (parseLaterBuffer != null) { + //FileLog.e("tmessages", this + " there is parseLaterBuffer"); + buffer = parseLaterBuffer; + parseLaterBuffer = null; + } } } @@ -425,8 +551,12 @@ public class TcpConnection extends PyroClientAdapter { } boolean switchToNextPort = wasConnected && !hasSomeDataSinceLastConnect && timedout; firstPacket = true; - restOfTheData = null; + if (restOfTheData != null) { + BuffersStorage.Instance.reuseFreeBuffer(restOfTheData); + restOfTheData = null; + } channelToken = 0; + lastPacketLength = 0; wasConnected = false; if (connectionState != TcpConnectionState.TcpConnectionStageSuspended && connectionState != TcpConnectionState.TcpConnectionStageIdle) { connectionState = TcpConnectionState.TcpConnectionStageIdle; @@ -527,7 +657,7 @@ public class TcpConnection extends PyroClientAdapter { failedConnectionCount = 0; readData(data); } catch (Exception e) { - FileLog.d("tmessages", "read data error"); + FileLog.e("tmessages", e); reconnect(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index 2d2de9144..8aaf1242d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -12,8 +12,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Base64; -import org.telegram.TL.TLClassStore; -import org.telegram.TL.TLRPC; import org.telegram.ui.ApplicationLoader; import java.io.File; @@ -30,6 +28,7 @@ public class UserConfig { public static String importHash = ""; private final static Integer sync = 1; public static boolean saveIncomingPhotos = false; + public static int contactsVersion = 1; public static int getNewMessageId() { int id; @@ -49,14 +48,15 @@ public class UserConfig { try { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("userconfing", Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("registeredForPush", registeredForPush); + editor.putString("pushString", pushString); + editor.putInt("lastSendMessageId", lastSendMessageId); + editor.putInt("lastLocalId", lastLocalId); + editor.putString("contactsHash", contactsHash); + editor.putString("importHash", importHash); + editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos); + editor.putInt("contactsVersion", contactsVersion); if (currentUser != null) { - editor.putBoolean("registeredForPush", registeredForPush); - editor.putString("pushString", pushString); - editor.putInt("lastSendMessageId", lastSendMessageId); - editor.putInt("lastLocalId", lastLocalId); - editor.putString("contactsHash", contactsHash); - editor.putString("importHash", importHash); - editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos); if (withFile) { SerializedData data = new SerializedData(); currentUser.serializeToStream(data); @@ -66,13 +66,6 @@ public class UserConfig { editor.putString("user", userString); } } else { - editor.putBoolean("registeredForPush", registeredForPush); - editor.putString("pushString", pushString); - editor.putInt("lastSendMessageId", lastSendMessageId); - editor.putInt("lastLocalId", lastLocalId); - editor.putString("contactsHash", contactsHash); - editor.putString("importHash", importHash); - editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos); editor.remove("user"); } editor.commit(); @@ -107,13 +100,7 @@ public class UserConfig { contactsHash = data.readString(); importHash = data.readString(); saveIncomingPhotos = data.readBool(); - if (currentUser.status != null) { - if (currentUser.status.expires != 0) { - currentUser.status.was_online = currentUser.status.expires; - } else { - currentUser.status.expires = currentUser.status.was_online; - } - } + contactsVersion = 0; MessagesStorage.lastQtsValue = data.readInt32(); MessagesStorage.lastSecretVersion = data.readInt32(); int val = data.readInt32(); @@ -141,6 +128,7 @@ public class UserConfig { contactsHash = preferences.getString("contactsHash", ""); importHash = preferences.getString("importHash", ""); saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false); + contactsVersion = preferences.getInt("contactsVersion", 0); } if (lastLocalId > -210000) { lastLocalId = -210000; @@ -166,6 +154,7 @@ public class UserConfig { contactsHash = preferences.getString("contactsHash", ""); importHash = preferences.getString("importHash", ""); saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false); + contactsVersion = preferences.getInt("contactsVersion", 0); String user = preferences.getString("user", null); if (user != null) { byte[] userBytes = Base64.decode(user, Base64.DEFAULT); @@ -193,6 +182,7 @@ public class UserConfig { importHash = ""; lastLocalId = -210000; lastSendMessageId = -210000; + contactsVersion = 1; saveIncomingPhotos = false; saveConfig(true); MessagesController.Instance.deleteAllAppAccounts(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index 2eb549822..5212fabde 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -15,6 +15,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; +import android.graphics.Point; import android.graphics.Typeface; import android.net.Uri; import android.os.Build; @@ -27,11 +28,11 @@ import android.text.SpannableStringBuilder; import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Base64; +import android.view.Display; import android.view.View; +import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; -import org.telegram.TL.TLClassStore; -import org.telegram.TL.TLObject; import org.telegram.ui.ApplicationLoader; import java.io.ByteArrayInputStream; @@ -65,6 +66,7 @@ public class Utilities { public static Handler applicationHandler; public static int statusBarHeight = 0; public static float density = 1; + public static Point displaySize = new Point(); public static boolean isRTL = false; public static Pattern pattern = Pattern.compile("[0-9]+"); private final static Integer lock = 1; @@ -85,7 +87,8 @@ public class Utilities { public static DispatchQueue fileUploadQueue = new DispatchQueue("fileUploadQueue"); public native static long doPQNative(long _what); - public native static byte[] aesIgeEncryption(byte[] _what, byte[] _key, byte[] _iv, boolean encrypt, boolean changeIv); + public native static byte[] aesIgeEncryption(byte[] _what, byte[] _key, byte[] _iv, boolean encrypt, boolean changeIv, int len); + public native static void aesIgeEncryption2(ByteBuffer _what, byte[] _key, byte[] _iv, boolean encrypt, boolean changeIv, int len); public static boolean isWaitingForSms() { boolean value = false; @@ -111,6 +114,14 @@ public class Utilities { return val; } + public static String parseIntToString(String value) { + Matcher matcher = pattern.matcher(value); + if (matcher.find()) { + return matcher.group(0); + } + return null; + } + static { density = ApplicationLoader.applicationContext.getResources().getDisplayMetrics().density; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("primes", Context.MODE_PRIVATE); @@ -164,6 +175,10 @@ public class Utilities { return (int)(density * value); } + public static int dpf(float value) { + return (int)Math.ceil(density * value); + } + public static boolean isGoodPrime(byte[] prime, int g) { if (!(g >= 2 && g <= 7)) { return false; @@ -282,6 +297,23 @@ public class Utilities { return null; } + public static byte[] computeSHA1(ByteBuffer convertme, int offset, int len) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + int oldp = convertme.position(); + int oldl = convertme.limit(); + convertme.position(offset); + convertme.limit(len); + md.update(convertme); + convertme.position(oldp); + convertme.limit(oldl); + return md.digest(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + public static byte[] computeSHA1(byte[] convertme) { try { MessageDigest md = MessageDigest.getInstance("SHA-1"); @@ -533,8 +565,28 @@ public class Utilities { } } + public static void checkDisplaySize() { + try { + WindowManager manager = (WindowManager)ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); + if (manager != null) { + Display display = manager.getDefaultDisplay(); + if (display != null) { + if(android.os.Build.VERSION.SDK_INT < 13) { + displaySize.set(display.getWidth(), display.getHeight()); + } else { + display.getSize(displaySize); + } + FileLog.e("tmessages", "display size = " + displaySize.x + " " + displaySize.y); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + static { recreateFormatters(); + Utilities.checkDisplaySize(); } public static String formatDateChat(long date) { @@ -678,14 +730,14 @@ public class Utilities { } public static int getColorForId(int id) { - if (id == 333000) { + if (id / 1000 == 333) { return 0xff0f94ed; } return arrColors[getColorIndex(id)]; } public static int getUserAvatarForId(int id) { - if (id == 333000) { + if (id / 1000 == 333) { return R.drawable.telegram_avatar; } return arrUsersAvatars[getColorIndex(id)]; @@ -807,9 +859,12 @@ public class Utilities { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } + } catch (Exception e) { + FileLog.e("tmessages", e); } finally { - if (cursor != null) + if (cursor != null) { cursor.close(); + } } return null; } diff --git a/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java index b86f42264..5e85b9b46 100644 --- a/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java @@ -9,9 +9,16 @@ package org.telegram.objects; import android.graphics.Bitmap; +import android.graphics.Paint; +import android.text.Layout; +import android.text.Spannable; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.util.Linkify; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.Emoji; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; @@ -33,9 +40,33 @@ public class MessageObject { public PhotoObject previewPhoto; public String dateKey; public boolean deleted = false; - public Object TAG; + public float audioProgress; + public int audioProgressSec; + + private static TextPaint textPaint; + public int lastLineWidth; + public int textWidth; + public int textHeight; + public int blockHeight = Integer.MAX_VALUE; + + public static class TextLayoutBlock { + public StaticLayout textLayout; + public float textXOffset = 0; + public float textYOffset = 0; + public int charactersOffset = 0; + } + + private static final int LINES_PER_BLOCK = 10; + + public ArrayList textLayoutBlocks; public MessageObject(TLRPC.Message message, AbstractMap users) { + if (textPaint == null) { + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setColor(0xff000000); + textPaint.linkColor = 0xff316f9f; + } + messageOwner = message; if (message instanceof TLRPC.TL_messageService) { @@ -222,7 +253,6 @@ public class MessageObject { } messageText = Emoji.replaceEmoji(messageText); - if (message instanceof TLRPC.TL_message || (message instanceof TLRPC.TL_messageForwarded && (message.media == null || !(message.media instanceof TLRPC.TL_messageMediaEmpty)))) { if (message.media == null || message.media instanceof TLRPC.TL_messageMediaEmpty) { if (message.from_id == UserConfig.clientUserId) { @@ -268,9 +298,9 @@ public class MessageObject { } } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaAudio) { if (message.from_id == UserConfig.clientUserId) { - type = 0; + type = 18; } else { - type = 1; + type = 19; } } } else if (message instanceof TLRPC.TL_messageService) { @@ -295,6 +325,8 @@ public class MessageObject { int dateYear = rightNow.get(Calendar.YEAR); int dateMonth = rightNow.get(Calendar.MONTH); dateKey = String.format("%d_%02d_%02d", dateYear, dateMonth, dateDay); + + generateLayout(); } public String getFileName() { @@ -335,4 +367,122 @@ public class MessageObject { } return ""; } + + private void generateLayout() { + if (type != 0 && type != 1 && type != 8 && type != 9 || messageOwner.to_id == null || messageText == null || messageText.length() == 0 || !(messageOwner.media instanceof TLRPC.TL_messageMediaEmpty) && !(messageOwner.media instanceof TLRPC.TL_messageMediaUnsupported) && !(messageOwner.media == null)) { + return; + } + + textLayoutBlocks = new ArrayList(); + + if (messageText instanceof Spannable) { + if (messageOwner.message != null && messageOwner.message.contains(".")) { + Linkify.addLinks((Spannable)messageText, Linkify.WEB_URLS); + } else if (messageText.length() < 400) { + Linkify.addLinks((Spannable)messageText, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS); + } + } + + textPaint.setTextSize(Utilities.dp(MessagesController.Instance.fontSize)); + + int maxWidth; + if (messageOwner.to_id.chat_id != 0) { + maxWidth = Math.min(Utilities.displaySize.x, Utilities.displaySize.y) - Utilities.dp(122); + } else { + maxWidth = Math.min(Utilities.displaySize.x, Utilities.displaySize.y) - Utilities.dp(80); + } + + StaticLayout textLayout = new StaticLayout(messageText, textPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + textHeight = textLayout.getHeight(); + int linesCount = textLayout.getLineCount(); + + int blocksCount = (int)Math.ceil((float)linesCount / LINES_PER_BLOCK); + int linesOffset = 0; + + for (int a = 0; a < blocksCount; a++) { + + int currentBlockLinesCount = Math.min(LINES_PER_BLOCK, linesCount - linesOffset); + TextLayoutBlock block = new TextLayoutBlock(); + + if (blocksCount == 1) { + block.textLayout = textLayout; + block.textYOffset = 0; + block.charactersOffset = 0; + blockHeight = textHeight; + } else { + int startCharacter = textLayout.getLineStart(linesOffset); + int endCharacter = textLayout.getLineEnd(linesOffset + currentBlockLinesCount - 1); + if (endCharacter < startCharacter) { + continue; + } + block.charactersOffset = startCharacter; + CharSequence str = messageText.subSequence(startCharacter, endCharacter); + block.textLayout = new StaticLayout(str, textPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + block.textYOffset = textLayout.getLineTop(linesOffset); + if (a != blocksCount - 1) { + blockHeight = Math.min(blockHeight, block.textLayout.getHeight()); + } + } + + textLayoutBlocks.add(block); + + float lastLeft = block.textXOffset = block.textLayout.getLineLeft(currentBlockLinesCount - 1); + float lastLine = block.textLayout.getLineWidth(currentBlockLinesCount - 1); + int linesMaxWidth; + int lastLineWidthWithLeft; + int linesMaxWidthWithLeft; + boolean hasNonRTL = false; + + linesMaxWidth = (int)Math.ceil(lastLine); + + if (a == blocksCount - 1) { + lastLineWidth = linesMaxWidth; + } + + linesMaxWidthWithLeft = lastLineWidthWithLeft = (int)Math.ceil(lastLine + lastLeft); + if (lastLeft == 0) { + hasNonRTL = true; + } + + if (currentBlockLinesCount > 1) { + float textRealMaxWidth = 0, textRealMaxWidthWithLeft = 0, lineWidth, lineLeft; + for (int n = 0; n < currentBlockLinesCount; ++n) { + try { + lineWidth = block.textLayout.getLineWidth(n); + lineLeft = block.textLayout.getLineLeft(n); + block.textXOffset = Math.min(block.textXOffset, lineLeft); + } catch (Exception e) { + FileLog.e("tmessages", e); + return; + } + + if (lineLeft == 0) { + hasNonRTL = true; + } + textRealMaxWidth = Math.max(textRealMaxWidth, lineWidth); + textRealMaxWidthWithLeft = Math.max(textRealMaxWidthWithLeft, lineWidth + lineLeft); + linesMaxWidth = Math.max(linesMaxWidth, (int)Math.ceil(lineWidth)); + linesMaxWidthWithLeft = Math.max(linesMaxWidthWithLeft, (int)Math.ceil(lineWidth + lineLeft)); + } + if (hasNonRTL) { + textRealMaxWidth = textRealMaxWidthWithLeft; + if (a == blocksCount - 1) { + lastLineWidth = lastLineWidthWithLeft; + } + linesMaxWidth = linesMaxWidthWithLeft; + } else if (a == blocksCount - 1) { + lastLineWidth = linesMaxWidth; + } + textWidth = Math.max(textWidth, (int)Math.ceil(textRealMaxWidth)); + } else { + textWidth = Math.max(textWidth, Math.min(maxWidth, linesMaxWidth)); + } + + if (hasNonRTL) { + block.textXOffset = 0; + } + + linesOffset += currentBlockLinesCount; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java b/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java index e78c03a22..93e5aee2c 100644 --- a/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java +++ b/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java @@ -11,7 +11,7 @@ package org.telegram.objects; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLoader; import java.util.ArrayList; @@ -62,7 +62,7 @@ public class PhotoObject { for (TLRPC.PhotoSize obj : sizes) { int diffW = Math.abs(obj.w - width); int diffH = Math.abs(obj.h - height); - if (closestObject == null || closestWidth > diffW && closestHeight > diffH) { + if (closestObject == null || closestWidth > diffW || closestHeight > diffH) { closestObject = obj; closestWidth = diffW; closestHeight = diffH; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseFragmentAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseFragmentAdapter.java new file mode 100644 index 000000000..1ab8d8b60 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseFragmentAdapter.java @@ -0,0 +1,41 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Adapters; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +public class BaseFragmentAdapter extends BaseAdapter { + public void onFragmentCreate() { + } + + public void onFragmentDestroy() { + } + + @Override + public int getCount() { + return 0; + } + + @Override + public Object getItem(int i) { + return null; + } + + @Override + public long getItemId(int i) { + return 0; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + return null; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsActivityAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsActivityAdapter.java new file mode 100644 index 000000000..1c1d8190d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsActivityAdapter.java @@ -0,0 +1,231 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Adapters; + +import android.content.Context; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.telegram.messenger.TLRPC; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.ui.Cells.ChatOrUserCell; +import org.telegram.ui.Views.SectionedBaseAdapter; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ContactsActivityAdapter extends SectionedBaseAdapter { + private Context mContext; + private boolean onlyUsers; + private boolean usersAsSections; + private HashMap ignoreUsers; + + public ContactsActivityAdapter(Context context, boolean arg1, boolean arg2, HashMap arg3) { + mContext = context; + onlyUsers = arg1; + usersAsSections = arg2; + ignoreUsers = arg3; + } + + @Override + public Object getItem(int section, int position) { + return null; + } + + @Override + public long getItemId(int section, int position) { + return 0; + } + + @Override + public int getSectionCount() { + int count = 0; + if (usersAsSections) { + count += ContactsController.Instance.sortedUsersSectionsArray.size(); + } else { + count++; + } + if (!onlyUsers) { + count += ContactsController.Instance.sortedContactsSectionsArray.size(); + } + return count; + } + + @Override + public int getCountForSection(int section) { + if (usersAsSections) { + if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { + ArrayList arr = ContactsController.Instance.usersSectionsDict.get(ContactsController.Instance.sortedUsersSectionsArray.get(section)); + return arr.size(); + } + } else { + if (section == 0) { + return ContactsController.Instance.contacts.size() + 1; + } + } + ArrayList arr = ContactsController.Instance.contactsSectionsDict.get(ContactsController.Instance.sortedContactsSectionsArray.get(section - 1)); + return arr.size(); + } + + @Override + public View getItemView(int section, int position, View convertView, ViewGroup parent) { + + TLRPC.User user = null; + int count = 0; + if (usersAsSections) { + if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { + ArrayList arr = ContactsController.Instance.usersSectionsDict.get(ContactsController.Instance.sortedUsersSectionsArray.get(section)); + user = MessagesController.Instance.users.get(arr.get(position).user_id); + count = arr.size(); + } + } else { + if (section == 0) { + if (position == 0) { + if (convertView == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = li.inflate(R.layout.contacts_invite_row_layout, parent, false); + } + View divider = convertView.findViewById(R.id.settings_row_divider); + if (ContactsController.Instance.contacts.isEmpty()) { + divider.setVisibility(View.INVISIBLE); + } else { + divider.setVisibility(View.VISIBLE); + } + return convertView; + } + user = MessagesController.Instance.users.get(ContactsController.Instance.contacts.get(position - 1).user_id); + count = ContactsController.Instance.contacts.size(); + } + } + if (user != null) { + if (convertView == null) { + convertView = new ChatOrUserCell(mContext); + ((ChatOrUserCell)convertView).useBoldFont = true; + ((ChatOrUserCell)convertView).usePadding = false; + } + + ((ChatOrUserCell)convertView).setData(user, null, null, null, null); + + if (ignoreUsers != null) { + if (ignoreUsers.containsKey(user.id)) { + ((ChatOrUserCell)convertView).drawAlpha = 0.5f; + } else { + ((ChatOrUserCell)convertView).drawAlpha = 1.0f; + } + } + + ((ChatOrUserCell) convertView).useSeparator = position != count - 1; + + return convertView; + } + + TextView textView; + if (convertView == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = li.inflate(R.layout.settings_row_button_layout, parent, false); + textView = (TextView)convertView.findViewById(R.id.settings_row_text); + } else { + textView = (TextView)convertView.findViewById(R.id.settings_row_text); + } + + View divider = convertView.findViewById(R.id.settings_row_divider); + ArrayList arr = ContactsController.Instance.contactsSectionsDict.get(ContactsController.Instance.sortedContactsSectionsArray.get(section - 1)); + ContactsController.Contact contact = arr.get(position); + if (divider != null) { + if (position == arr.size() - 1) { + divider.setVisibility(View.INVISIBLE); + } else { + divider.setVisibility(View.VISIBLE); + } + } + if (contact.first_name != null && contact.last_name != null) { + textView.setText(Html.fromHtml(contact.first_name + " " + contact.last_name + "")); + } else if (contact.first_name != null && contact.last_name == null) { + textView.setText(Html.fromHtml("" + contact.first_name + "")); + } else { + textView.setText(Html.fromHtml("" + contact.last_name + "")); + } + return convertView; + } + + @Override + public int getItemViewType(int section, int position) { + if (usersAsSections) { + if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { + return 0; + } + } else if (section == 0) { + if (position == 0) { + return 2; + } + return 0; + } + return 1; + } + + @Override + public int getItemViewTypeCount() { + return 3; + } + + @Override + public int getSectionHeaderViewType(int section) { + if (usersAsSections) { + if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { + return 1; + } + } else if (section == 0) { + return 0; + } + return 1; + } + + @Override + public int getSectionHeaderViewTypeCount() { + return 2; + } + + @Override + public View getSectionHeaderView(int section, View convertView, ViewGroup parent) { + if (usersAsSections) { + if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { + if (convertView == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = li.inflate(R.layout.settings_section_layout, parent, false); + convertView.setBackgroundColor(0xffffffff); + } + TextView textView = (TextView)convertView.findViewById(R.id.settings_section_text); + textView.setText(ContactsController.Instance.sortedUsersSectionsArray.get(section)); + return convertView; + } + } else { + if (section == 0) { + if (convertView == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = li.inflate(R.layout.empty_layout, parent, false); + } + return convertView; + } + } + + if (convertView == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = li.inflate(R.layout.settings_section_layout, parent, false); + convertView.setBackgroundColor(0xffffffff); + } + TextView textView = (TextView)convertView.findViewById(R.id.settings_section_text); + textView.setText(ContactsController.Instance.sortedContactsSectionsArray.get(section - 1)); + return convertView; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsActivitySearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsActivitySearchAdapter.java new file mode 100644 index 000000000..774eb4ac6 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsActivitySearchAdapter.java @@ -0,0 +1,187 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Adapters; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +import org.telegram.messenger.TLRPC; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Cells.ChatOrUserCell; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Timer; +import java.util.TimerTask; + +public class ContactsActivitySearchAdapter extends BaseFragmentAdapter { + private Context mContext; + private HashMap ignoreUsers; + private ArrayList searchResult; + private ArrayList searchResultNames; + private Timer searchDialogsTimer; + + public ContactsActivitySearchAdapter(Context context, HashMap arg1) { + mContext = context; + ignoreUsers = arg1; + } + + public void searchDialogs(final String query) { + if (query == null) { + searchResult = null; + searchResultNames = null; + notifyDataSetChanged(); + } else { + try { + if (searchDialogsTimer != null) { + searchDialogsTimer.cancel(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + searchDialogsTimer = new Timer(); + searchDialogsTimer.schedule(new TimerTask() { + @Override + public void run() { + try { + searchDialogsTimer.cancel(); + searchDialogsTimer = null; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + processSearch(query); + } + }, 100, 300); + } + } + + private void processSearch(final String query) { + Utilities.globalQueue.postRunnable(new Runnable() { + @Override + public void run() { + String q = query.trim().toLowerCase(); + if (q.length() == 0) { + updateSearchResults(new ArrayList(), new ArrayList()); + return; + } + long time = System.currentTimeMillis(); + ArrayList resultArray = new ArrayList(); + ArrayList resultArrayNames = new ArrayList(); + + for (TLRPC.TL_contact contact : ContactsController.Instance.contacts) { + TLRPC.User user = MessagesController.Instance.users.get(contact.user_id); + if (user.first_name != null && user.first_name.toLowerCase().startsWith(q) || user.last_name != null && user.last_name.toLowerCase().startsWith(q)) { + if (user.id == UserConfig.clientUserId) { + continue; + } + resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q)); + resultArray.add(user); + } + } + + updateSearchResults(resultArray, resultArrayNames); + } + }); + } + + private void updateSearchResults(final ArrayList users, final ArrayList names) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + searchResult = users; + searchResultNames = names; + notifyDataSetChanged(); + } + }); + } + + @Override + public boolean areAllItemsEnabled() { + return true; + } + + @Override + public boolean isEnabled(int i) { + return true; + } + + @Override + public int getCount() { + if (searchResult == null) { + return 0; + } + return searchResult.size(); + } + + @Override + public TLRPC.User getItem(int i) { + if (searchResult != null) { + if (i >= 0 && i < searchResult.size()) { + return searchResult.get(i); + } + } + return null; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + if (view == null) { + view = new ChatOrUserCell(mContext); + ((ChatOrUserCell)view).usePadding = false; + } + + ((ChatOrUserCell) view).useSeparator = i != searchResult.size() - 1; + + Object obj = searchResult.get(i); + TLRPC.User user = MessagesController.Instance.users.get(((TLRPC.User)obj).id); + + if (user != null) { + ((ChatOrUserCell)view).setData(user, null, null, searchResultNames.get(i), null); + + if (ignoreUsers != null) { + if (ignoreUsers.containsKey(user.id)) { + ((ChatOrUserCell)view).drawAlpha = 0.5f; + } else { + ((ChatOrUserCell)view).drawAlpha = 1.0f; + } + } + } + return view; + } + + @Override + public int getItemViewType(int i) { + return 0; + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public boolean isEmpty() { + return searchResult == null || searchResult.size() == 0; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationActivity.java index 9c25274ee..873466412 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationActivity.java @@ -51,6 +51,9 @@ public class ApplicationActivity extends ActionBarActivity implements Notificati private String photoPath = null; private String videoPath = null; private String sendingText = null; + private String documentPath = null; + private String[] imagesPathArray = null; + private String[] documentsPathArray = null; private int currentConnectionState; private View statusView; private View backStatusButton; @@ -117,6 +120,9 @@ public class ApplicationActivity extends ActionBarActivity implements Notificati photoPath = (String)NotificationCenter.Instance.getFromMemCache(533); videoPath = (String)NotificationCenter.Instance.getFromMemCache(534); sendingText = (String)NotificationCenter.Instance.getFromMemCache(535); + documentPath = (String)NotificationCenter.Instance.getFromMemCache(536); + imagesPathArray = (String[])NotificationCenter.Instance.getFromMemCache(537); + documentsPathArray = (String[])NotificationCenter.Instance.getFromMemCache(538); if (push_user_id != 0) { if (push_user_id == UserConfig.clientUserId) { @@ -153,7 +159,7 @@ public class ApplicationActivity extends ActionBarActivity implements Notificati getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment, "chat" + Math.random()).commitAllowingStateLoss(); } } - if (videoPath != null || photoPath != null || sendingText != null) { + if (videoPath != null || photoPath != null || sendingText != null || documentPath != null || documentsPathArray != null || imagesPathArray != null) { MessagesActivity fragment = new MessagesActivity(); fragment.selectAlertString = R.string.ForwardMessagesTo; fragment.animationType = 1; @@ -217,7 +223,10 @@ public class ApplicationActivity extends ActionBarActivity implements Notificati photoPath = (String)NotificationCenter.Instance.getFromMemCache(533); videoPath = (String)NotificationCenter.Instance.getFromMemCache(534); sendingText = (String)NotificationCenter.Instance.getFromMemCache(535); - if (videoPath != null || photoPath != null || sendingText != null) { + documentPath = (String)NotificationCenter.Instance.getFromMemCache(536); + imagesPathArray = (String[])NotificationCenter.Instance.getFromMemCache(537); + documentsPathArray = (String[])NotificationCenter.Instance.getFromMemCache(538); + if (videoPath != null || photoPath != null || sendingText != null || documentPath != null || imagesPathArray != null || documentsPathArray != null) { MessagesActivity fragment = new MessagesActivity(); fragment.selectAlertString = R.string.ForwardMessagesTo; fragment.animationType = 1; @@ -310,10 +319,23 @@ public class ApplicationActivity extends ActionBarActivity implements Notificati fragment.processSendingVideo(videoPath); } else if (sendingText != null) { fragment.processSendingText(sendingText); + } else if (documentPath != null) { + fragment.processSendingDocument(documentPath); + } else if (imagesPathArray != null) { + for (String path : imagesPathArray) { + fragment.processSendingPhoto(path); + } + } else if (documentsPathArray != null) { + for (String path : documentsPathArray) { + fragment.processSendingDocument(path); + } } photoPath = null; videoPath = null; sendingText = null; + documentPath = null; + imagesPathArray = null; + documentsPathArray = null; } } @@ -387,6 +409,7 @@ public class ApplicationActivity extends ActionBarActivity implements Notificati @Override public void onConfigurationChanged(android.content.res.Configuration newConfig) { super.onConfigurationChanged(newConfig); + Utilities.checkDisplaySize(); fixLayout(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java index fde77a624..8525e09e8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java @@ -11,7 +11,6 @@ package org.telegram.ui; import android.app.Activity; import android.app.Application; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -26,7 +25,6 @@ import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.gcm.GoogleCloudMessaging; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.messenger.BackgroundService; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; @@ -45,7 +43,6 @@ public class ApplicationLoader extends Application { private GoogleCloudMessaging gcm; private AtomicInteger msgId = new AtomicInteger(); private String regid; - private String SENDER_ID = "760348033672"; public static final String EXTRA_MESSAGE = "message"; public static final String PROPERTY_REG_ID = "registration_id"; private static final String PROPERTY_APP_VERSION = "appVersion"; @@ -133,8 +130,6 @@ public class ApplicationLoader extends Application { lastPauseTime = System.currentTimeMillis(); FileLog.e("tmessages", "start application with time " + lastPauseTime); - - startService(new Intent(this, BackgroundService.class)); } @Override @@ -149,6 +144,7 @@ public class ApplicationLoader extends Application { } currentLocale = newLocale; } + Utilities.checkDisplaySize(); } public static void resetLastPauseTime() { @@ -210,7 +206,7 @@ public class ApplicationLoader extends Application { while (count < 1000) { try { count++; - regid = gcm.register(SENDER_ID); + regid = gcm.register(ConnectionsManager.GCM_SENDER_ID); sendRegistrationIdToBackend(true); storeRegistrationId(applicationContext, regid); return true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java index 4c17a2699..09914d8af 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java @@ -10,7 +10,6 @@ package org.telegram.ui.Cells; import android.content.Context; import android.graphics.drawable.Drawable; -import android.util.AttributeSet; import android.view.View; public class BaseCell extends View { @@ -18,14 +17,6 @@ public class BaseCell extends View { super(context); } - public BaseCell(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BaseCell(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - protected void setDrawableBounds(Drawable drawable, int x, int y) { setDrawableBounds(drawable, x, y, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java new file mode 100644 index 000000000..9b94da82a --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java @@ -0,0 +1,396 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Cells; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.View; + +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.TLRPC; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.objects.MessageObject; +import org.telegram.ui.Views.ImageReceiver; +import org.telegram.ui.Views.ProgressView; +import org.telegram.ui.Views.SeekBar; + +import java.io.File; +import java.lang.ref.WeakReference; + +public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener { + + private static Drawable[][] statesDrawable = new Drawable[8][2]; + private static TextPaint timePaint; + + private ImageReceiver avatarImage; + private SeekBar seekBar; + private ProgressView progressView; + private int seekBarX; + private int seekBarY; + + private int buttonState = 0; + private int buttonX; + private int buttonY; + private int buttonPressed = 0; + + private int avatarPressed = 0; + + private StaticLayout timeLayout; + private int timeX; + private String lastTimeString = null; + + private int TAG; + + public TLRPC.User audioUser; + private TLRPC.FileLocation currentPhoto; + private String currentNameString; + + public ChatAudioCell(Context context, boolean isChat) { + super(context, isChat); + TAG = MediaController.Instance.generateObserverTag(); + + avatarImage = new ImageReceiver(); + avatarImage.parentView = new WeakReference(this); + seekBar = new SeekBar(context); + seekBar.delegate = this; + progressView = new ProgressView(); + + if (timePaint == null) { + statesDrawable[0][0] = getResources().getDrawable(R.drawable.play1); + statesDrawable[0][1] = getResources().getDrawable(R.drawable.play1_pressed); + statesDrawable[1][0] = getResources().getDrawable(R.drawable.pause1); + statesDrawable[1][1] = getResources().getDrawable(R.drawable.pause1_pressed); + statesDrawable[2][0] = getResources().getDrawable(R.drawable.audioload1); + statesDrawable[2][1] = getResources().getDrawable(R.drawable.audioload1_pressed); + statesDrawable[3][0] = getResources().getDrawable(R.drawable.audiocancel1); + statesDrawable[3][1] = getResources().getDrawable(R.drawable.audiocancel1_pressed); + + statesDrawable[4][0] = getResources().getDrawable(R.drawable.play2); + statesDrawable[4][1] = getResources().getDrawable(R.drawable.play2_pressed); + statesDrawable[5][0] = getResources().getDrawable(R.drawable.pause2); + statesDrawable[5][1] = getResources().getDrawable(R.drawable.pause2_pressed); + statesDrawable[6][0] = getResources().getDrawable(R.drawable.audioload2); + statesDrawable[6][1] = getResources().getDrawable(R.drawable.audioload2_pressed); + statesDrawable[7][0] = getResources().getDrawable(R.drawable.audiocancel2); + statesDrawable[7][1] = getResources().getDrawable(R.drawable.audiocancel2_pressed); + + timePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + timePaint.setTextSize(Utilities.dp(12)); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + boolean result = seekBar.onTouch(event.getAction(), event.getX() - seekBarX, event.getY() - seekBarY); + if (result) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + getParent().requestDisallowInterceptTouchEvent(true); + } + invalidate(); + } else { + int side = Utilities.dp(36); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side) { + buttonPressed = 1; + invalidate(); + result = true; + } else if (x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH) { + avatarPressed = 1; + result = true; + } + } else if (buttonPressed == 1) { + if (event.getAction() == MotionEvent.ACTION_UP) { + buttonPressed = 0; + playSoundEffect(SoundEffectConstants.CLICK); + didPressedButton(); + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + buttonPressed = 0; + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side)) { + buttonPressed = 0; + invalidate(); + } + } + } else if (avatarPressed == 1) { + if (event.getAction() == MotionEvent.ACTION_UP) { + avatarPressed = 0; + playSoundEffect(SoundEffectConstants.CLICK); + if (delegate != null) { + delegate.didPressedUserAvatar(this, audioUser); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + avatarPressed = 0; + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH)) { + avatarPressed = 0; + } + } + } + if (!result) { + result = super.onTouchEvent(event); + } + } + + return result; + } + + private void didPressedButton() { + if (buttonState == 0) { + boolean result = MediaController.Instance.playAudio(currentMessageObject); + if (result) { + buttonState = 1; + invalidate(); + } + } else if (buttonState == 1) { + boolean result = MediaController.Instance.pauseAudio(currentMessageObject); + if (result) { + buttonState = 0; + invalidate(); + } + } else if (buttonState == 2) { + FileLoader.Instance.loadFile(null, null, null, currentMessageObject.messageOwner.media.audio); + buttonState = 3; + invalidate(); + } else if (buttonState == 3) { + FileLoader.Instance.cancelLoadFile(null, null, null, currentMessageObject.messageOwner.media.audio); + buttonState = 2; + invalidate(); + } + } + + public void updateProgress() { + if (currentMessageObject == null) { + return; + } + + if (!seekBar.isDragging()) { + seekBar.setProgress(currentMessageObject.audioProgress); + } + + int duration = 0; + if (!MediaController.Instance.isPlayingAudio(currentMessageObject)) { + duration = currentMessageObject.messageOwner.media.audio.duration; + } else { + duration = currentMessageObject.audioProgressSec; + } + String timeString = String.format("%02d:%02d", duration / 60, duration % 60); + if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) { + int timeWidth = (int)Math.ceil(timePaint.measureText(timeString)); + timeLayout = new StaticLayout(timeString, timePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } + invalidate(); + } + + public void updateButtonState() { + String fileName = currentMessageObject.getFileName(); + File cacheFile = new File(Utilities.getCacheDir(), fileName); + if (cacheFile.exists()) { + MediaController.Instance.removeLoadingFileObserver(this); + boolean playing = MediaController.Instance.isPlayingAudio(currentMessageObject); + if (!playing || playing && MediaController.Instance.isAudioPaused()) { + buttonState = 0; + } else { + buttonState = 1; + } + progressView.setProgress(0); + } else { + MediaController.Instance.addLoadingFileObserver(currentMessageObject.getFileName(), this); + if (!FileLoader.Instance.isLoadingFile(fileName)) { + buttonState = 2; + progressView.setProgress(0); + } else { + buttonState = 3; + Float progress = FileLoader.Instance.fileProgresses.get(fileName); + if (progress != null) { + progressView.setProgress(progress); + } else { + progressView.setProgress(0); + } + } + } + updateProgress(); + } + + @Override + public void onFailedDownload(String fileName) { + updateButtonState(); + } + + @Override + public void onSuccessDownload(String fileName) { + updateButtonState(); + } + + @Override + public void onProgressDownload(String fileName, float progress) { + progressView.setProgress(progress); + invalidate(); + } + + @Override + public int getObserverTag() { + return TAG; + } + + @Override + public void onSeekBarDrag(float progress) { + if (currentMessageObject == null) { + return; + } + currentMessageObject.audioProgress = progress; + MediaController.Instance.seekToProgress(currentMessageObject, progress); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec); + setMeasuredDimension(width, Utilities.dp(68)); + if (chat) { + backgroundWidth = Math.min(width - Utilities.dp(102), Utilities.dp(300)); + } else { + backgroundWidth = Math.min(width - Utilities.dp(50), Utilities.dp(300)); + } + } + + @SuppressLint("DrawAllocation") + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (currentMessageObject.messageOwner.out) { + avatarImage.imageX = layoutWidth - backgroundWidth + Utilities.dp(9); + seekBarX = layoutWidth - backgroundWidth + Utilities.dp(94); + buttonX = layoutWidth - backgroundWidth + Utilities.dp(63); + timeX = layoutWidth - backgroundWidth + Utilities.dp(67); + } else { + if (chat) { + avatarImage.imageX = Utilities.dp(69); + seekBarX = Utilities.dp(155); + buttonX = Utilities.dp(124); + timeX = Utilities.dp(128); + } else { + avatarImage.imageX = Utilities.dp(16); + seekBarX = Utilities.dp(103); + buttonX = Utilities.dp(72); + timeX = Utilities.dp(76); + } + } + avatarImage.imageY = Utilities.dp(9); + avatarImage.imageW = Utilities.dp(50); + avatarImage.imageH = Utilities.dp(50); + + seekBar.width = backgroundWidth - Utilities.dp(112); + seekBar.height = Utilities.dp(30); + progressView.width = backgroundWidth - Utilities.dp(136); + progressView.height = Utilities.dp(30); + seekBarY = Utilities.dp(13); + buttonY = Utilities.dp(10); + + updateProgress(); + } + + @Override + protected boolean isUserDataChanged() { + TLRPC.User newUser = MessagesController.Instance.users.get(currentMessageObject.messageOwner.media.audio.user_id); + TLRPC.FileLocation newPhoto = null; + + if (avatarImage != null && newUser != null && newUser.photo != null) { + newPhoto = newUser.photo.photo_small; + } + + return currentPhoto == null && newPhoto != null || currentPhoto != null && newPhoto == null || currentPhoto != null && newPhoto != null && (currentPhoto.local_id != newPhoto.local_id || currentPhoto.volume_id != newPhoto.volume_id) || super.isUserDataChanged(); + } + + @Override + public void setMessageObject(MessageObject messageObject) { + if (currentMessageObject != messageObject || isUserDataChanged()) { + int uid = messageObject.messageOwner.media.audio.user_id; + audioUser = MessagesController.Instance.users.get(uid); + if (audioUser != null) { + if (audioUser.photo != null) { + currentPhoto = audioUser.photo.photo_small; + } + avatarImage.setImage(currentPhoto, "50_50", getResources().getDrawable(Utilities.getUserAvatarForId(uid))); + } else { + avatarImage.setImage((TLRPC.FileLocation)null, "50_50", getResources().getDrawable(Utilities.getUserAvatarForId(uid))); + } + + if (messageObject.messageOwner.out) { + seekBar.type = 0; + progressView.type = 0; + } else { + seekBar.type = 1; + progressView.type = 1; + } + + super.setMessageObject(messageObject); + } + updateButtonState(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (currentMessageObject == null) { + return; + } + + avatarImage.draw(canvas, avatarImage.imageX, avatarImage.imageY, Utilities.dp(50), Utilities.dp(50)); + + canvas.save(); + if (buttonState == 0 || buttonState == 1) { + canvas.translate(seekBarX, seekBarY); + seekBar.draw(canvas); + } else { + canvas.translate(seekBarX + Utilities.dp(12), seekBarY); + progressView.draw(canvas); + } + canvas.restore(); + + int state = buttonState; + if (!currentMessageObject.messageOwner.out) { + state += 4; + timePaint.setColor(0xffa1aab3); + } else { + timePaint.setColor(0xff70b15c); + } + Drawable buttonDrawable = statesDrawable[state][buttonPressed]; + int side = Utilities.dp(36); + int x = (side - buttonDrawable.getIntrinsicWidth()) / 2; + int y = (side - buttonDrawable.getIntrinsicHeight()) / 2; + setDrawableBounds(buttonDrawable, x + buttonX, y + buttonY); + buttonDrawable.draw(canvas); + + canvas.save(); + canvas.translate(timeX, Utilities.dp(45)); + timeLayout.draw(canvas); + canvas.restore(); + } + + @Override + protected void finalize() throws Throwable { + MediaController.Instance.removeLoadingFileObserver(this); + super.finalize(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java new file mode 100644 index 000000000..0211e210b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java @@ -0,0 +1,461 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Cells; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.text.Html; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.View; + +import org.telegram.messenger.TLRPC; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.objects.MessageObject; +import org.telegram.ui.ApplicationLoader; +import org.telegram.ui.Views.ImageReceiver; + +import java.lang.ref.WeakReference; + +public class ChatBaseCell extends BaseCell { + + public static interface ChatBaseCellDelegate { + public abstract void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user); + } + + protected boolean chat; + protected boolean isPressed = false; + protected boolean forwardName = false; + private boolean isCheckPressed = true; + private boolean wasLayout = false; + protected MessageObject currentMessageObject; + + private static Drawable backgroundDrawableIn; + private static Drawable backgroundDrawableInSelected; + private static Drawable backgroundDrawableOut; + private static Drawable backgroundDrawableOutSelected; + private static Drawable checkDrawable; + private static Drawable halfCheckDrawable; + private static Drawable clockDrawable; + private static Drawable errorDrawable; + private static TextPaint timePaintIn; + private static TextPaint timePaintOut; + private static TextPaint namePaint; + private static TextPaint forwardNamePaint; + + protected int backgroundWidth = 100; + + protected int layoutWidth; + protected int layoutHeight; + + private ImageReceiver avatarImage; + private boolean avatarPressed = false; + private boolean forwardNamePressed = false; + + private StaticLayout nameLayout; + protected int nameWidth; + protected boolean drawName = false; + + private StaticLayout forwardedNameLayout; + protected int forwardedNameWidth; + protected boolean drawForwardedName = false; + private int forwardNameX; + private int forwardNameY; + + private StaticLayout timeLayout; + protected int timeWidth; + protected int timeX; + private TextPaint currentTimePaint; + private String currentTimeString; + + private TLRPC.User currentUser; + private TLRPC.FileLocation currentPhoto; + private String currentNameString; + + private TLRPC.User currentForwardUser; + private String currentForwardNameString; + + public ChatBaseCellDelegate delegate; + + protected int namesOffset = 0; + + public ChatBaseCell(Context context, boolean isChat) { + super(context); + init(); + chat = isChat; + if (chat) { + avatarImage = new ImageReceiver(); + avatarImage.parentView = new WeakReference(this); + } + } + + private void init() { + if (backgroundDrawableIn == null) { + backgroundDrawableIn = getResources().getDrawable(R.drawable.msg_in); + backgroundDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_selected); + backgroundDrawableOut = getResources().getDrawable(R.drawable.msg_out); + backgroundDrawableOutSelected = getResources().getDrawable(R.drawable.msg_out_selected); + checkDrawable = getResources().getDrawable(R.drawable.msg_check); + halfCheckDrawable = getResources().getDrawable(R.drawable.msg_halfcheck); + clockDrawable = getResources().getDrawable(R.drawable.msg_clock); + errorDrawable = getResources().getDrawable(R.drawable.msg_warning); + + timePaintIn = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + timePaintIn.setTextSize(Utilities.dp(12)); + timePaintIn.setColor(0xffa1aab3); + + timePaintOut = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + timePaintOut.setTextSize(Utilities.dp(12)); + timePaintOut.setColor(0xff70b15c); + + namePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + namePaint.setTextSize(Utilities.dp(15)); + + forwardNamePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + forwardNamePaint.setTextSize(Utilities.dp(14)); + } + } + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed); + invalidate(); + } + + public void setCheckPressed(boolean value, boolean pressed) { + isCheckPressed = value; + isPressed = pressed; + invalidate(); + } + + protected boolean isUserDataChanged() { + if (currentMessageObject == null || currentUser == null) { + return false; + } + TLRPC.User newUser = MessagesController.Instance.users.get(currentMessageObject.messageOwner.from_id); + TLRPC.FileLocation newPhoto = null; + + if (avatarImage != null && newUser != null && newUser.photo != null) { + newPhoto = newUser.photo.photo_small; + } + + if (currentPhoto == null && newPhoto != null || currentPhoto != null && newPhoto == null || currentPhoto != null && newPhoto != null && (currentPhoto.local_id != newPhoto.local_id || currentPhoto.volume_id != newPhoto.volume_id)) { + return true; + } + + String newNameString = null; + if (drawName && chat && newUser != null && !currentMessageObject.messageOwner.out) { + newNameString = Utilities.formatName(newUser.first_name, newUser.last_name); + } + + if (currentNameString == null && newNameString != null || currentNameString != null && newNameString == null || currentNameString != null && newNameString != null && !currentNameString.equals(newNameString)) { + return true; + } + + newUser = MessagesController.Instance.users.get(currentMessageObject.messageOwner.fwd_from_id); + newNameString = null; + if (drawForwardedName && currentMessageObject.messageOwner instanceof TLRPC.TL_messageForwarded) { + newNameString = Utilities.formatName(newUser.first_name, newUser.last_name); + } + return currentForwardNameString == null && newNameString != null || currentForwardNameString != null && newNameString == null || currentForwardNameString != null && newNameString != null && !currentForwardNameString.equals(newNameString); + } + + public void setMessageObject(MessageObject messageObject) { + currentMessageObject = messageObject; + isPressed = false; + isCheckPressed = true; + wasLayout = false; + + if (currentMessageObject.messageOwner.id < 0 && currentMessageObject.messageOwner.send_state != MessagesController.MESSAGE_SEND_STATE_SEND_ERROR && currentMessageObject.messageOwner.send_state != MessagesController.MESSAGE_SEND_STATE_SENT) { + if (MessagesController.Instance.sendingMessages.get(currentMessageObject.messageOwner.id) == null) { + currentMessageObject.messageOwner.send_state = MessagesController.MESSAGE_SEND_STATE_SEND_ERROR; + } + } + + currentUser = MessagesController.Instance.users.get(messageObject.messageOwner.from_id); + if (avatarImage != null) { + if (currentUser != null) { + if (currentUser.photo != null) { + currentPhoto = currentUser.photo.photo_small; + } + avatarImage.setImage(currentPhoto, "50_50", getResources().getDrawable(Utilities.getUserAvatarForId(currentUser.id))); + } else { + avatarImage.setImage((TLRPC.FileLocation)null, "50_50", null); + } + } + + if (currentMessageObject.messageOwner.out) { + currentTimePaint = timePaintOut; + } else { + currentTimePaint = timePaintIn; + } + + currentTimeString = Utilities.formatterDay.format((long) (currentMessageObject.messageOwner.date) * 1000); + timeWidth = (int)Math.ceil(currentTimePaint.measureText(currentTimeString)); + + namesOffset = 0; + + if (drawName && chat && currentUser != null && !currentMessageObject.messageOwner.out) { + currentNameString = Utilities.formatName(currentUser.first_name, currentUser.last_name); + nameWidth = getMaxNameWidth(); + + CharSequence nameStringFinal = TextUtils.ellipsize(currentNameString.replace("\n", " "), namePaint, nameWidth - Utilities.dp(12), TextUtils.TruncateAt.END); + nameLayout = new StaticLayout(nameStringFinal, namePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (nameLayout.getLineCount() > 0) { + nameWidth = (int)Math.ceil(nameLayout.getLineWidth(0)); + namesOffset += Utilities.dp(18); + } else { + nameWidth = 0; + } + } else { + currentNameString = null; + nameLayout = null; + nameWidth = 0; + } + + if (drawForwardedName && messageObject.messageOwner instanceof TLRPC.TL_messageForwarded) { + currentForwardUser = MessagesController.Instance.users.get(messageObject.messageOwner.fwd_from_id); + if (currentForwardUser != null) { + currentForwardNameString = Utilities.formatName(currentForwardUser.first_name, currentForwardUser.last_name); + + forwardedNameWidth = getMaxNameWidth(); + + CharSequence str = TextUtils.ellipsize(currentForwardNameString.replace("\n", " "), forwardNamePaint, forwardedNameWidth - Utilities.dp(40), TextUtils.TruncateAt.END); + str = Html.fromHtml(String.format("%s
    %s %s", ApplicationLoader.applicationContext.getString(R.string.ForwardedMessage), ApplicationLoader.applicationContext.getString(R.string.From), str)); + forwardedNameLayout = new StaticLayout(str, forwardNamePaint, forwardedNameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (forwardedNameLayout.getLineCount() > 1) { + forwardedNameWidth = Math.max((int) Math.ceil(forwardedNameLayout.getLineWidth(0)), (int) Math.ceil(forwardedNameLayout.getLineWidth(1))); + namesOffset += Utilities.dp(36); + } else { + forwardedNameWidth = 0; + } + } else { + currentForwardNameString = null; + forwardedNameLayout = null; + forwardedNameWidth = 0; + } + } else { + currentForwardNameString = null; + forwardedNameLayout = null; + forwardedNameWidth = 0; + } + + requestLayout(); + } + + public final MessageObject getMessageObject() { + return currentMessageObject; + } + + protected int getMaxNameWidth() { + return backgroundWidth - Utilities.dp(8); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean result = false; + float x = event.getX(); + float y = event.getY(); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (avatarImage != null && x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH) { + avatarPressed = true; + result = true; + } else if (drawForwardedName && forwardedNameLayout != null) { + if (x >= forwardNameX && x <= forwardNameX + forwardedNameWidth && y >= forwardNameY && y <= forwardNameY + Utilities.dp(32)) { + forwardNamePressed = true; + result = true; + } + } + } else if (avatarPressed) { + if (event.getAction() == MotionEvent.ACTION_UP) { + avatarPressed = false; + playSoundEffect(SoundEffectConstants.CLICK); + if (delegate != null) { + delegate.didPressedUserAvatar(this, currentUser); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + avatarPressed = false; + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (avatarImage != null && !(x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH)) { + avatarPressed = false; + } + } + } else if (forwardNamePressed) { + if (event.getAction() == MotionEvent.ACTION_UP) { + forwardNamePressed = false; + playSoundEffect(SoundEffectConstants.CLICK); + if (delegate != null) { + delegate.didPressedUserAvatar(this, currentForwardUser); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + forwardNamePressed = false; + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= forwardNameX && x <= forwardNameX + forwardedNameWidth && y >= forwardNameY && y <= forwardNameY + Utilities.dp(32))) { + forwardNamePressed = false; + } + } + } + return result; + } + + @SuppressLint("DrawAllocation") + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (currentMessageObject == null) { + super.onLayout(changed, left, top, right, bottom); + return; + } + + if (changed || !wasLayout) { + layoutWidth = getMeasuredWidth(); + layoutHeight = getMeasuredHeight(); + + timeLayout = new StaticLayout(currentTimeString, currentTimePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (!currentMessageObject.messageOwner.out) { + timeX = backgroundWidth - Utilities.dp(9) - timeWidth + (chat ? Utilities.dp(52) : 0); + } else { + timeX = layoutWidth - timeWidth - Utilities.dpf(38.5f); + } + + if (avatarImage != null) { + avatarImage.imageX = Utilities.dp(6); + avatarImage.imageY = layoutHeight - Utilities.dp(45); + avatarImage.imageW = Utilities.dp(42); + avatarImage.imageH = Utilities.dp(42); + } + + wasLayout = true; + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (currentMessageObject == null) { + return; + } + + if (!wasLayout) { + requestFocus(); + return; + } + + if (avatarImage != null) { + avatarImage.draw(canvas, Utilities.dp(6), layoutHeight - Utilities.dp(45), Utilities.dp(42), Utilities.dp(42)); + } + + Drawable currentBackgroundDrawable = null; + if (currentMessageObject.messageOwner.out) { + if (isPressed() && isCheckPressed || !isCheckPressed && isPressed) { + currentBackgroundDrawable = backgroundDrawableOutSelected; + } else { + currentBackgroundDrawable = backgroundDrawableOut; + } + setDrawableBounds(currentBackgroundDrawable, layoutWidth - backgroundWidth, Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); + } else { + if (isPressed() && isCheckPressed || !isCheckPressed && isPressed) { + currentBackgroundDrawable = backgroundDrawableInSelected; + } else { + currentBackgroundDrawable = backgroundDrawableIn; + } + if (chat) { + setDrawableBounds(currentBackgroundDrawable, Utilities.dp(52), Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); + } else { + setDrawableBounds(currentBackgroundDrawable, 0, Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); + } + } + currentBackgroundDrawable.draw(canvas); + + if (drawName && nameLayout != null) { + canvas.save(); + canvas.translate(currentBackgroundDrawable.getBounds().left + Utilities.dp(19), Utilities.dp(10)); + namePaint.setColor(Utilities.getColorForId(currentUser.id)); + nameLayout.draw(canvas); + canvas.restore(); + } + + if (drawForwardedName && forwardedNameLayout != null) { + canvas.save(); + if (currentMessageObject.messageOwner.out) { + forwardNamePaint.setColor(0xff4a923c); + forwardNameX = currentBackgroundDrawable.getBounds().left + Utilities.dp(10); + forwardNameY = Utilities.dp(10 + (drawName ? 18 : 0)); + } else { + forwardNamePaint.setColor(0xff006fc8); + forwardNameX = currentBackgroundDrawable.getBounds().left + Utilities.dp(19); + forwardNameY = Utilities.dp(10 + (drawName ? 18 : 0)); + } + canvas.translate(forwardNameX, forwardNameY); + forwardedNameLayout.draw(canvas); + canvas.restore(); + } + + canvas.save(); + canvas.translate(timeX, layoutHeight - Utilities.dpf(6.5f) - timeLayout.getHeight()); + timeLayout.draw(canvas); + canvas.restore(); + + if (currentMessageObject.messageOwner.out) { + boolean drawCheck1 = false; + boolean drawCheck2 = false; + boolean drawClock = false; + boolean drawError = false; + + if (currentMessageObject.messageOwner.send_state == MessagesController.MESSAGE_SEND_STATE_SENDING) { + drawCheck1 = false; + drawCheck2 = false; + drawClock = true; + drawError = false; + } else if (currentMessageObject.messageOwner.send_state == MessagesController.MESSAGE_SEND_STATE_SEND_ERROR) { + drawCheck1 = false; + drawCheck2 = false; + drawClock = false; + drawError = true; + } else if (currentMessageObject.messageOwner.send_state == MessagesController.MESSAGE_SEND_STATE_SENT) { + if (!currentMessageObject.messageOwner.unread) { + drawCheck1 = true; + drawCheck2 = true; + } else { + drawCheck1 = false; + drawCheck2 = true; + } + drawClock = false; + drawError = false; + } + + if (drawClock) { + setDrawableBounds(clockDrawable, layoutWidth - Utilities.dpf(18.5f) - clockDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - clockDrawable.getIntrinsicHeight()); + clockDrawable.draw(canvas); + } + if (drawCheck2) { + if (drawCheck1) { + setDrawableBounds(checkDrawable, layoutWidth - Utilities.dpf(22.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - checkDrawable.getIntrinsicHeight()); + } else { + setDrawableBounds(checkDrawable, layoutWidth - Utilities.dpf(18.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - checkDrawable.getIntrinsicHeight()); + } + checkDrawable.draw(canvas); + } + if (drawCheck1) { + setDrawableBounds(halfCheckDrawable, layoutWidth - Utilities.dp(18) - halfCheckDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - halfCheckDrawable.getIntrinsicHeight()); + halfCheckDrawable.draw(canvas); + } + if (drawError) { + setDrawableBounds(errorDrawable, layoutWidth - Utilities.dp(18) - errorDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(6.5f) - errorDrawable.getIntrinsicHeight()); + errorDrawable.draw(canvas); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index ebcd0c611..cda68d560 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -9,18 +9,199 @@ package org.telegram.ui.Cells; import android.content.Context; -import android.util.AttributeSet; +import android.graphics.Canvas; +import android.text.Spannable; +import android.text.style.ClickableSpan; +import android.util.Log; +import android.view.MotionEvent; -public class ChatMessageCell extends BaseCell { - public ChatMessageCell(Context context) { - super(context); +import org.telegram.messenger.Utilities; +import org.telegram.objects.MessageObject; + +public class ChatMessageCell extends ChatBaseCell { + + private int textX, textY; + private int totalHeight = 0; + private ClickableSpan pressedLink; + private int visibleY = 0; + private int visibleHeight = 0; + + private int lastVisibleBlockNum = 0; + private int firstVisibleBlockNum = 0; + private int totalVisibleBlocksCount = 0; + + public ChatMessageCell(Context context, boolean isChat) { + super(context, isChat); + drawForwardedName = true; } - public ChatMessageCell(Context context, AttributeSet attrs) { - super(context, attrs); + @Override + public boolean onTouchEvent(MotionEvent event) { + if (currentMessageObject != null && currentMessageObject.messageText instanceof Spannable && !isPressed) { + if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) { + int x = (int)event.getX(); + int y = (int)event.getY(); + if (x >= textX && y >= textY && x <= textX + currentMessageObject.textWidth && y <= textY + currentMessageObject.textHeight) { + y -= textY; + int blockNum = Math.max(0, y / currentMessageObject.blockHeight); + if (blockNum < currentMessageObject.textLayoutBlocks.size()) { + MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(blockNum); + x -= textX - (int)Math.ceil(block.textXOffset); + y -= block.textYOffset; + final int line = block.textLayout.getLineForVertical(y); + final int off = block.textLayout.getOffsetForHorizontal(line, x) + block.charactersOffset; + + final float left = block.textLayout.getLineLeft(line); + if (left <= x && left + block.textLayout.getLineWidth(line) >= x) { + Spannable buffer = (Spannable)currentMessageObject.messageText; + ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); + + if (link.length != 0) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + pressedLink = link[0]; + return true; + } else { + if (link[0] == pressedLink) { + pressedLink.onClick(this); + return true; + } + } + } else { + pressedLink = null; + } + } else { + pressedLink = null; + } + } else { + pressedLink = null; + } + } else { + pressedLink = null; + } + } + } else { + pressedLink = null; + } + return super.onTouchEvent(event); } - public ChatMessageCell(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); + public void setVisiblePart(int position, int height) { + visibleY = position; + visibleHeight = height; + if (visibleY < 0) { + Log.e("tmessages", "vis = " + visibleY); + } + + int newFirst = -1, newLast = -1, newCount = 0; + + for (int a = Math.max(0, (visibleY - textY) / currentMessageObject.blockHeight); a < currentMessageObject.textLayoutBlocks.size(); a++) { + MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(a); + float y = textY + block.textYOffset; + if (intersect(y, y + currentMessageObject.blockHeight, visibleY, visibleY + visibleHeight)) { + if (newFirst == -1) { + newFirst = a; + } + newLast = a; + newCount++; + } else if (y > visibleY) { + break; + } + } + + if (lastVisibleBlockNum != newLast || firstVisibleBlockNum != newFirst || totalVisibleBlocksCount != newCount) { + lastVisibleBlockNum = newLast; + firstVisibleBlockNum = newFirst; + totalVisibleBlocksCount = newCount; + invalidate(); + } + } + + private boolean intersect(float left1, float right1, float left2, float right2) { + if (left1 <= left2) { + return right1 >= left2; + } + return left1 <= right2; + } + + @Override + public void setMessageObject(MessageObject messageObject) { + if (currentMessageObject != messageObject || isUserDataChanged()) { + pressedLink = null; + int maxWidth; + if (chat) { + maxWidth = Utilities.displaySize.x - Utilities.dp(122); + drawName = true; + } else { + maxWidth = Utilities.displaySize.x - Utilities.dp(80); + } + + backgroundWidth = maxWidth; + + super.setMessageObject(messageObject); + + backgroundWidth = messageObject.textWidth; + totalHeight = messageObject.textHeight + Utilities.dpf(19.5f) + namesOffset; + + int maxChildWidth = Math.max(backgroundWidth, nameWidth); + maxChildWidth = Math.max(maxChildWidth, forwardedNameWidth); + + int timeMore = timeWidth + Utilities.dp(6); + if (messageObject.messageOwner.out) { + timeMore += Utilities.dpf(20.5f); + } + + if (maxWidth - messageObject.lastLineWidth < timeMore) { + totalHeight += Utilities.dp(14); + backgroundWidth = Math.max(maxChildWidth, messageObject.lastLineWidth) + Utilities.dp(29); + } else { + int diff = maxChildWidth - messageObject.lastLineWidth; + if (diff >= 0 && diff <= timeMore) { + backgroundWidth = maxChildWidth + timeMore - diff + Utilities.dp(29); + } else { + backgroundWidth = Math.max(maxChildWidth, messageObject.lastLineWidth + timeMore) + Utilities.dp(29); + } + } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), totalHeight); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (changed) { + if (currentMessageObject.messageOwner.out) { + textX = layoutWidth - backgroundWidth + Utilities.dp(10); + textY = Utilities.dp(10) + namesOffset; + } else { + textX = Utilities.dp(19) + (chat ? Utilities.dp(52) : 0); + textY = Utilities.dp(10) + namesOffset; + } + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (currentMessageObject == null || currentMessageObject.textLayoutBlocks == null || currentMessageObject.textLayoutBlocks.isEmpty()) { + return; + } + + for (int a = Math.max(0, (visibleY - textY) / currentMessageObject.blockHeight); a < currentMessageObject.textLayoutBlocks.size(); a++) { + MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(a); + float y = textY + block.textYOffset; + if (intersect(y, y + currentMessageObject.blockHeight, visibleY, visibleY + visibleHeight)) { + canvas.save(); + canvas.translate(textX - (int)Math.ceil(block.textXOffset), textY + block.textYOffset); + block.textLayout.draw(canvas); + canvas.restore(); + } else { + break; + } + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java index bd7e19da4..256a5f96c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java @@ -17,11 +17,10 @@ import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; -import android.util.AttributeSet; import android.view.View; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.MessagesController; @@ -65,16 +64,6 @@ public class ChatOrUserCell extends BaseCell { init(); } - public ChatOrUserCell(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public ChatOrUserCell(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - private void init() { if (namePaint == null) { namePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); @@ -174,9 +163,6 @@ public class ChatOrUserCell extends BaseCell { int newStatus = 0; if (user.status != null) { newStatus = user.status.expires; - if (lastStatus == 0) { - lastStatus = user.status.was_online; - } } if (newStatus != lastStatus) { continueUpdate = true; @@ -202,9 +188,6 @@ public class ChatOrUserCell extends BaseCell { if (user != null) { if (user.status != null) { lastStatus = user.status.expires; - if (lastStatus == 0) { - lastStatus = user.status.was_online; - } } else { lastStatus = 0; } @@ -326,7 +309,7 @@ public class ChatOrUserCell extends BaseCell { if (chat != null) { nameString2 = chat.title; } else if (user != null) { - if (user.id != 333000 && ContactsController.Instance.contactsDict.get(user.id) == null) { + if (user.id / 1000 != 333 && ContactsController.Instance.contactsDict.get(user.id) == null) { if (ContactsController.Instance.contactsDict.size() == 0 && ContactsController.Instance.loadingContacts) { nameString2 = Utilities.formatName(user.first_name, user.last_name); } else { @@ -382,18 +365,14 @@ public class ChatOrUserCell extends BaseCell { onlineString = getResources().getString(R.string.Offline); } else { int currentTime = ConnectionsManager.Instance.getCurrentTime(); - if (user.id == UserConfig.clientUserId || user.status.expires > currentTime || user.status.was_online > currentTime) { + if (user.id == UserConfig.clientUserId || user.status.expires > currentTime) { currentOnlinePaint = onlinePaint; onlineString = getResources().getString(R.string.Online); } else { - if (user.status.was_online <= 10000 && user.status.expires <= 10000) { + if (user.status.expires <= 10000) { onlineString = getResources().getString(R.string.Invisible); } else { - int value = user.status.was_online; - if (value == 0) { - value = user.status.expires; - } - onlineString = Utilities.formatDateOnline(value); + onlineString = Utilities.formatDateOnline(user.status.expires); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java index 23a1a1ffe..61b8c25d9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -16,11 +16,10 @@ import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; -import android.util.AttributeSet; import android.view.View; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ContactsController; import org.telegram.messenger.Emoji; import org.telegram.messenger.MessagesController; @@ -143,16 +142,6 @@ public class DialogCell extends BaseCell { init(); } - public DialogCell(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public DialogCell(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - public void setDialog(TLRPC.TL_dialog dialog) { currentDialog = dialog; update(0); @@ -525,7 +514,7 @@ public class DialogCell extends BaseCell { if (chat != null) { nameString = chat.title; } else if (user != null) { - if (user.id != 333000 && ContactsController.Instance.contactsDict.get(user.id) == null) { + if (user.id / 1000 != 333 && ContactsController.Instance.contactsDict.get(user.id) == null) { if (ContactsController.Instance.contactsDict.size() == 0 && (!ContactsController.Instance.contactsLoaded || ContactsController.Instance.loadingContacts)) { nameString = Utilities.formatName(user.first_name, user.last_name); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 131909e7b..438a33664 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -8,6 +8,7 @@ package org.telegram.ui; +import android.animation.Animator; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; @@ -22,7 +23,6 @@ import android.graphics.Rect; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.Bundle; @@ -33,12 +33,8 @@ import android.support.v7.app.ActionBarActivity; import android.support.v7.view.ActionMode; import android.text.Editable; import android.text.Html; -import android.text.Layout; -import android.text.SpannableStringBuilder; import android.text.TextWatcher; -import android.text.style.ClickableSpan; import android.text.style.ImageSpan; -import android.util.Log; import android.util.TypedValue; import android.view.Display; import android.view.KeyEvent; @@ -53,6 +49,7 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.WindowManager; +import android.view.animation.AccelerateDecelerateInterpolator; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.webkit.MimeTypeMap; @@ -64,14 +61,14 @@ import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListView; import android.widget.PopupWindow; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; import org.telegram.objects.MessageObject; @@ -84,12 +81,14 @@ import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; +import org.telegram.ui.Cells.ChatAudioCell; +import org.telegram.ui.Cells.ChatBaseCell; +import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Views.BackupImageView; import org.telegram.ui.Views.BaseFragment; import org.telegram.ui.Views.EmojiView; import org.telegram.ui.Views.LayoutListView; import org.telegram.ui.Views.MessageActionLayout; -import org.telegram.ui.Views.MessageLayout; import org.telegram.ui.Views.OnSwipeTouchListener; import org.telegram.ui.Views.SizeNotifierRelativeLayout; @@ -122,6 +121,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private boolean ignoreTextChange = false; private TextView emptyView; private View bottomOverlay; + private View recordPanel; + private TextView recordTimeText; private TextView bottomOverlayText; private ImageButton audioSendButton; private MessageObject selectedObject; @@ -143,7 +144,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private TextView topPanelText; private long dialog_id; AlertDialog visibleDialog = null; - private final Rect mLastTouch = new Rect(); private SizeNotifierRelativeLayout sizeNotifierRelativeLayout; private HashMap selectedMessagesIds = new HashMap(); private HashMap selectedMessagesCanCopyIds = new HashMap(); @@ -160,7 +160,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private long lastTypingTimeSend = 0; private int minDate = 0; private int progressTag = 0; - private int fontSize = 16; private boolean invalidateAfterAnimation = false; boolean first = true; private int unread_to_load = 0; @@ -169,7 +168,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private boolean unread_end_reached = true; private boolean loadingForward = false; private MessageObject unreadMessageObject = null; - //private boolean reloadAfterAnimation = false; + private boolean recordingAudio = false; + private String lastTimeString = null; private String currentPicturePath; @@ -182,10 +182,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private CharSequence lastPrintString; - private MediaRecorder audioRecorder = null; - private TLRPC.TL_audio recordingAudio = null; - private File recordingAudioFile = null; - ActionMode mActionMode = null; private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @Override @@ -302,11 +298,13 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa NotificationCenter.Instance.addObserver(this, FileLoader.FileDidFailedLoad); NotificationCenter.Instance.addObserver(this, FileLoader.FileDidLoaded); NotificationCenter.Instance.addObserver(this, FileLoader.FileLoadProgressChanged); + NotificationCenter.Instance.addObserver(this, MediaController.audioProgressDidChanged); + NotificationCenter.Instance.addObserver(this, MediaController.audioDidReset); + NotificationCenter.Instance.addObserver(this, MediaController.recordProgressChanged); NotificationCenter.Instance.addObserver(this, 997); loading = true; MessagesController.Instance.loadMessages(dialog_id, 0, 30, 0, true, 0, classGuid, true, false); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - fontSize = preferences.getInt("fons_size", 16); sendByEnter = preferences.getBoolean("send_by_enter", false); if (currentChat != null) { @@ -339,6 +337,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa NotificationCenter.Instance.removeObserver(this, FileLoader.FileDidLoaded); NotificationCenter.Instance.removeObserver(this, FileLoader.FileLoadProgressChanged); NotificationCenter.Instance.removeObserver(this, MessagesController.contactsDidLoaded); + NotificationCenter.Instance.removeObserver(this, MediaController.audioProgressDidChanged); + NotificationCenter.Instance.removeObserver(this, MediaController.audioDidReset); + NotificationCenter.Instance.removeObserver(this, MediaController.recordProgressChanged); NotificationCenter.Instance.removeObserver(this, 997); if (sizeNotifierRelativeLayout != null) { sizeNotifierRelativeLayout.delegate = null; @@ -353,6 +354,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } catch (Exception e) { FileLog.e("tmessages", e); } + MediaController.Instance.stopAudio(); } @Override @@ -388,6 +390,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa progressView = fragmentView.findViewById(R.id.progressLayout); pagedownButton = fragmentView.findViewById(R.id.pagedown_button); audioSendButton = (ImageButton)fragmentView.findViewById(R.id.chat_audio_send_button); + recordPanel = fragmentView.findViewById(R.id.record_panel); + recordTimeText = (TextView)fragmentView.findViewById(R.id.recording_time_text); View progressViewInner = progressView.findViewById(R.id.progressLayoutInner); updateContactStatus(); @@ -461,7 +465,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } emptyView.setPadding(Utilities.dp(7), Utilities.dp(1), Utilities.dp(7), Utilities.dp(1)); - if (currentUser != null && currentUser.id == 333000) { + if (currentUser != null && currentUser.id / 1000 == 333) { emptyView.setText(R.string.GotAQuestion); } @@ -475,6 +479,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } }); + final Rect scrollRect = new Rect(); + chatListView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView absListView, int i) { @@ -506,6 +512,14 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { showPagedownButton(false, false); } + for (int a = 0; a < visibleItemCount; a++) { + View view = absListView.getChildAt(a); + if (view instanceof ChatMessageCell) { + ChatMessageCell messageCell = (ChatMessageCell)view; + messageCell.getLocalVisibleRect(scrollRect); + messageCell.setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top); + } + } } }); @@ -582,11 +596,23 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - startRecording(); - } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { - stopRecording(); + recordingAudio = MediaController.Instance.startRecording(dialog_id); + updateAudioRecordIntefrace(); + } else if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { + if (recordingAudio) { + MediaController.Instance.stopRecording(true); + recordingAudio = false; + updateAudioRecordIntefrace(); + } + } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE && recordingAudio) { + if (motionEvent.getX() < -Utilities.dp(100)) { + MediaController.Instance.stopRecording(false); + recordingAudio = false; + updateAudioRecordIntefrace(); + } } - return false; + view.onTouchEvent(motionEvent); + return true; } }); @@ -594,7 +620,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa @Override public void onClick(View view) { if (unread_end_reached || first_unread_id == 0) { - chatListView.setSelectionFromTop(messages.size() - 1, -10000 - chatListView.getPaddingTop()); + chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } else { messages.clear(); messagesByDays.clear(); @@ -630,7 +656,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) { int currentTime = ConnectionsManager.Instance.getCurrentTime(); - if (currentUser != null && currentUser.status != null && currentUser.status.expires < currentTime && currentUser.status.was_online < currentTime) { + if (currentUser != null && currentUser.status != null && currentUser.status.expires < currentTime) { return; } lastTypingTimeSend = System.currentTimeMillis(); @@ -674,9 +700,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa processRowSelect(view); return; } - if (!spanClicked(chatListView, view, R.id.chat_message_text)) { - createMenu(view, true); - } + createMenu(view, true); } }); @@ -710,12 +734,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa avatarImageView.performClick(); } } - - @Override - public void onTouchUp(MotionEvent event) { - mLastTouch.right = (int) event.getX(); - mLastTouch.bottom = (int) event.getY(); - } }); emptyView.setOnTouchListener(new OnSwipeTouchListener() { @@ -748,15 +766,68 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } private void checkSendButton() { - sendButton.setVisibility(View.VISIBLE); - audioSendButton.setVisibility(View.INVISIBLE); -// if (messsageEditText.length() > 0) { -// sendButton.setVisibility(View.VISIBLE); -// audioSendButton.setVisibility(View.INVISIBLE); -// } else { -// sendButton.setVisibility(View.INVISIBLE); -// audioSendButton.setVisibility(View.VISIBLE); -// } +// sendButton.setVisibility(View.VISIBLE); +// audioSendButton.setVisibility(View.INVISIBLE); + if (messsageEditText.length() > 0) { + sendButton.setVisibility(View.VISIBLE); + audioSendButton.setVisibility(View.INVISIBLE); + } else { + sendButton.setVisibility(View.INVISIBLE); + audioSendButton.setVisibility(View.VISIBLE); + } + } + + private void updateAudioRecordIntefrace() { + if (recordingAudio) { + recordPanel.setVisibility(View.VISIBLE); + recordTimeText.setText("00:00"); + lastTimeString = null; + if(android.os.Build.VERSION.SDK_INT > 12) { + recordPanel.setX(Utilities.displaySize.x); + recordPanel.animate().setInterpolator(new AccelerateDecelerateInterpolator()).setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + } + + @Override + public void onAnimationEnd(Animator animator) { + recordPanel.setX(0); + } + + @Override + public void onAnimationCancel(Animator animator) { + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + }).setDuration(300).translationX(0).start(); + } + } else { + if(android.os.Build.VERSION.SDK_INT > 12) { + recordPanel.animate().setInterpolator(new AccelerateDecelerateInterpolator()).setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + + } + + @Override + public void onAnimationEnd(Animator animator) { + recordPanel.setVisibility(View.GONE); + } + + @Override + public void onAnimationCancel(Animator animator) { + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + }).setDuration(300).translationX(Utilities.displaySize.x).start(); + } else { + recordPanel.setVisibility(View.GONE); + } + } } private void sendMessage() { @@ -767,7 +838,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa chatListView.post(new Runnable() { @Override public void run() { - chatListView.setSelectionFromTop(messages.size() - 1, -10000 - chatListView.getPaddingTop()); + chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } }); } @@ -819,84 +890,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa paused = true; } - private void startRecording() { - if (audioRecorder != null) { - return; - } - - recordingAudio = new TLRPC.TL_audio(); - recordingAudio.dc_id = Integer.MIN_VALUE; - recordingAudio.id = UserConfig.lastLocalId; - recordingAudio.user_id = UserConfig.clientUserId; - UserConfig.lastLocalId--; - UserConfig.saveConfig(false); - - recordingAudioFile = new File(Utilities.getCacheDir(), MessageObject.getAttachFileName(recordingAudio)); - - audioRecorder = new MediaRecorder(); - audioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); - audioRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - audioRecorder.setOutputFile(recordingAudioFile.getAbsolutePath()); - if(android.os.Build.VERSION.SDK_INT >= 10) { - audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - } else { - audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); - } - audioRecorder.setAudioSamplingRate(24000); - audioRecorder.setAudioChannels(1); - audioRecorder.setAudioEncodingBitRate(16000); - - try { - audioRecorder.prepare(); - audioRecorder.start(); - } catch (Exception e) { - Log.e("tmessages", "prepare() failed"); - } - } - - private void stopRecording() { - try { - audioRecorder.stop(); - audioRecorder.release(); - audioRecorder = null; - - recordingAudio.date = ConnectionsManager.Instance.getCurrentTime(); - recordingAudio.size = (int)recordingAudioFile.length(); - recordingAudio.path = recordingAudioFile.getAbsolutePath(); - int duration = 0; - - MediaPlayer player = new MediaPlayer(); - try { - player.setDataSource(recordingAudio.path); - player.prepare(); - duration = player.getDuration(); - recordingAudio.duration = duration / 1000; - } catch (Exception e) { - FileLog.e("tmessages", e); - } finally { - try { - player.release(); - player = null; - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - - if (duration > 500) { - MessagesController.Instance.sendMessage(recordingAudio, dialog_id); - } else { - recordingAudio = null; - recordingAudioFile.delete(); - recordingAudioFile = null; - } - } catch (Exception e) { - FileLog.e("tmessages", e); - recordingAudio = null; - recordingAudioFile.delete(); - recordingAudioFile = null; - } - } - private void updateSecretStatus() { if (bottomOverlay == null) { return; @@ -956,7 +949,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa int currentTime = ConnectionsManager.Instance.getCurrentTime(); for (TLRPC.TL_chatParticipant participant : info.participants) { TLRPC.User user = MessagesController.Instance.users.get(participant.user_id); - if (user != null && user.status != null && (user.status.expires > currentTime || user.status.was_online > currentTime || user.id == UserConfig.clientUserId) && (user.status.expires > 10000 || user.status.was_online > 10000)) { + if (user != null && user.status != null && (user.status.expires > currentTime || user.id == UserConfig.clientUserId) && user.status.expires > 10000) { onlineCount++; } } @@ -1041,9 +1034,14 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (parentView == null) { return; } - ChatListRowHolderEx holder = (ChatListRowHolderEx)parentView.getTag(); + MessageObject message = null; + if (view instanceof ChatBaseCell) { + message = ((ChatBaseCell)view).getMessageObject(); + } else { + ChatListRowHolderEx holder = (ChatListRowHolderEx)parentView.getTag(); + message = holder.message; + } - MessageObject message = holder.message; if (getMessageType(message) < 2) { return; } @@ -1087,7 +1085,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa title.setCompoundDrawablePadding(0); } } else if (currentUser != null) { - if (currentUser.id != 333000 && ContactsController.Instance.contactsDict.get(currentUser.id) == null && (ContactsController.Instance.contactsDict.size() != 0 || !ContactsController.Instance.loadingContacts)) { + if (currentUser.id / 1000 != 333 && ContactsController.Instance.contactsDict.get(currentUser.id) == null && (ContactsController.Instance.contactsDict.size() != 0 || !ContactsController.Instance.loadingContacts)) { if (currentUser.phone != null && currentUser.phone.length() != 0) { actionBar.setTitle(PhoneFormat.Instance.format("+" + currentUser.phone)); } else { @@ -1129,17 +1127,13 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa actionBar.setSubtitle(getStringEntry(R.string.Offline)); } else { int currentTime = ConnectionsManager.Instance.getCurrentTime(); - if (currentUser.status.expires > currentTime || currentUser.status.was_online > currentTime) { + if (currentUser.status.expires > currentTime) { actionBar.setSubtitle(getStringEntry(R.string.Online)); } else { - if (currentUser.status.was_online <= 10000 && currentUser.status.expires <= 10000) { + if (currentUser.status.expires <= 10000) { actionBar.setSubtitle(getStringEntry(R.string.Invisible)); } else { - int value = currentUser.status.was_online; - if (value == 0) { - value = currentUser.status.expires; - } - actionBar.setSubtitle(Utilities.formatDateOnline(value)); + actionBar.setSubtitle(Utilities.formatDateOnline(currentUser.status.expires)); } } } @@ -1300,7 +1294,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } public void processSendingPhoto(String imageFilePath) { - if (imageFilePath == null) { + if (imageFilePath == null || imageFilePath.length() == 0) { return; } TLRPC.TL_photo photo = MessagesController.Instance.generatePhotoSizes(imageFilePath); @@ -1313,8 +1307,49 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } + public void processSendingDocument(String documentFilePath) { + if (documentFilePath == null || documentFilePath.length() == 0) { + return; + } + File f = new File(documentFilePath); + if (!f.exists() || f.length() == 0) { + return; + } + String name = f.getName(); + if (name == null) { + name = "noname"; + } + String ext = ""; + int idx = documentFilePath.lastIndexOf("."); + if (idx != -1) { + ext = documentFilePath.substring(idx); + } + TLRPC.TL_document document = new TLRPC.TL_document(); + document.thumb = new TLRPC.TL_photoSizeEmpty(); + document.thumb.type = "s"; + document.id = 0; + document.user_id = UserConfig.clientUserId; + document.date = ConnectionsManager.Instance.getCurrentTime(); + document.file_name = name; + document.size = (int)f.length(); + document.dc_id = 0; + document.path = documentFilePath; + if (ext.length() != 0) { + MimeTypeMap myMime = MimeTypeMap.getSingleton(); + String mimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (mimeType != null) { + document.mime_type = mimeType; + } else { + document.mime_type = "application/octet-stream"; + } + } else { + document.mime_type = "application/octet-stream"; + } + MessagesController.Instance.sendMessage(document, dialog_id); + } + public void processSendingVideo(final String videoPath) { - if (videoPath == null) { + if (videoPath == null || videoPath.length() == 0) { return; } Bitmap thumb = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND); @@ -1516,7 +1551,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa chatListView.post(new Runnable() { @Override public void run() { - chatListView.setSelectionFromTop(messages.size() - 1, -10000 - chatListView.getPaddingTop()); + chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } }); } @@ -1721,7 +1756,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa chatListView.post(new Runnable() { @Override public void run() { - chatListView.setSelectionFromTop(messages.size() - 1, -10000 - chatListView.getPaddingTop()); + chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } }); } @@ -1875,6 +1910,12 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } else if (id == FileLoader.FileUploadProgressChanged) { String location = (String)args[0]; + boolean enc = (Boolean)args[2]; + if (enc && currentEncryptedChat == null) { + return; + } else if (!enc && currentEncryptedChat != null) { + return; + } ProgressBar bar; if ((bar = progressBarMap.get(location)) != null) { Float progress = (Float)args[1]; @@ -1942,6 +1983,44 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa updateVisibleRows(); } } + } else if (id == MediaController.audioDidReset) { + Integer mid = (Integer)args[0]; + if (chatListView != null) { + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = chatListView.getChildAt(a); + if (view instanceof ChatAudioCell) { + ChatAudioCell cell = (ChatAudioCell)view; + if (cell.getMessageObject() != null && cell.getMessageObject().messageOwner.id == mid) { + cell.updateButtonState(); + break; + } + } + } + } + } else if (id == MediaController.audioProgressDidChanged) { + Integer mid = (Integer)args[0]; + if (chatListView != null) { + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = chatListView.getChildAt(a); + if (view instanceof ChatAudioCell) { + ChatAudioCell cell = (ChatAudioCell)view; + if (cell.getMessageObject() != null && cell.getMessageObject().messageOwner.id == mid) { + cell.updateProgress(); + break; + } + } + } + } + } else if (id == MediaController.recordProgressChanged) { + Long time = (Long)args[0] / 1000; + String str = String.format("%02d:%02d", time / 60, time % 60); + if (lastTimeString == null || !lastPrintString.equals(str)) { + if (recordTimeText != null) { + recordTimeText.setText(str); + } + } } } @@ -1953,7 +2032,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa topPanel.setVisibility(View.GONE); } else { if (currentEncryptedChat != null && !(currentEncryptedChat instanceof TLRPC.TL_encryptedChat) - || currentUser.id == 333000 + || currentUser.id / 1000 == 333 || (currentUser.phone != null && currentUser.phone.length() != 0 && ContactsController.Instance.contactsDict.get(currentUser.id) != null && (ContactsController.Instance.contactsDict.size() != 0 || !ContactsController.Instance.loadingContacts))) { @@ -2010,7 +2089,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa chatListView.post(new Runnable() { @Override public void run() { - chatListView.setSelectionFromTop(messages.size() - 1, -10000 - chatListView.getPaddingTop()); + chatListView.setSelectionFromTop(messages.size() - 1, -100000 - chatListView.getPaddingTop()); } }); } @@ -2352,17 +2431,21 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } private View getRowParentView(View v) { - while (!(v.getTag() instanceof ChatListRowHolderEx)) { - ViewParent parent = v.getParent(); - if (!(parent instanceof View)) { - return null; - } - v = (View)v.getParent(); - if (v == null) { - return null; + if (v instanceof ChatBaseCell) { + return v; + } else { + while (!(v.getTag() instanceof ChatListRowHolderEx)) { + ViewParent parent = v.getParent(); + if (!(parent instanceof View)) { + return null; + } + v = (View)v.getParent(); + if (v == null) { + return null; + } } + return v; } - return v; } public void createMenu(View v, boolean single) { @@ -2378,9 +2461,13 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (parentView == null) { return; } - ChatListRowHolderEx holder = (ChatListRowHolderEx)parentView.getTag(); - - MessageObject message = holder.message; + MessageObject message = null; + if (v instanceof ChatBaseCell) { + message = ((ChatBaseCell)v).getMessageObject(); + } else { + ChatListRowHolderEx holder = (ChatListRowHolderEx)parentView.getTag(); + message = holder.message; + } final int type = getMessageType(message); if (single || type < 2) { if (type >= 0) { @@ -2767,51 +2854,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } - private boolean spanClicked(ListView list, View view, int textViewId) { - final TextView widget = (TextView)view.findViewById(textViewId); - if (widget == null) { - return false; - } - try { - list.offsetRectIntoDescendantCoords(widget, mLastTouch); - int x = mLastTouch.right; - int y = mLastTouch.bottom; - - x -= widget.getTotalPaddingLeft(); - y -= widget.getTotalPaddingTop(); - x += widget.getScrollX(); - y += widget.getScrollY(); - - final Layout layout = widget.getLayout(); - if (layout == null) { - return false; - } - final int line = layout.getLineForVertical(y); - final int off = layout.getOffsetForHorizontal(line, x); - - final float left = layout.getLineLeft(line); - if (left > x || left + layout.getLineWidth(line) < x) { - return false; - } - - final Editable buffer = new SpannableStringBuilder(widget.getText()); - if (buffer == null) { - return false; - } - final ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); - - if (link.length == 0) { - return false; - } - - link[0].onClick(widget); - return true; - } catch (Exception e) { - FileLog.e("tmessages", e); - return false; - } - } - private void updateVisibleRows() { if (chatListView == null) { return; @@ -2838,6 +2880,24 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa view.setBackgroundColor(0); } updateRowBackground(holder, disableSelection, selected); + } else if (view instanceof ChatBaseCell) { + ChatBaseCell cell = (ChatBaseCell)view; + + boolean disableSelection = false; + boolean selected = false; + if (mActionMode != null) { + if (selectedMessagesIds.containsKey(cell.getMessageObject().messageOwner.id)) { + view.setBackgroundColor(0x6633b5e5); + selected = true; + } else { + view.setBackgroundColor(0); + } + disableSelection = true; + } else { + view.setBackgroundColor(0); + } + + cell.setCheckPressed(!disableSelection, disableSelection && selected); } } } @@ -2849,12 +2909,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_photo_states); } else if (messageType == 3 || messageType == 5 || messageType == 7) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_photo_states); - } else if (messageType == 0 || messageType == 8) { - holder.messageLayout.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); - } else if (messageType == 1 || messageType == 9) { - holder.messageLayout.setBackgroundResource(R.drawable.chat_incoming_text_states); - holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); } else if (messageType == 12) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); holder.chatBubbleView.setPadding(Utilities.dp(6), Utilities.dp(6), Utilities.dp(18), 0); @@ -2867,9 +2921,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else if (messageType == 17) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); - } else if (messageType == 18) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), Utilities.dp(6)); } } else { if (messageType == 2 || messageType == 4 || messageType == 6) { @@ -2884,20 +2935,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo); } - } else if (messageType == 0 || messageType == 8) { - if (selected) { - holder.messageLayout.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.messageLayout.setBackgroundResource(R.drawable.msg_out); - } - holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); - } else if (messageType == 1 || messageType == 9) { - if (selected) { - holder.messageLayout.setBackgroundResource(R.drawable.msg_in_selected); - } else { - holder.messageLayout.setBackgroundResource(R.drawable.msg_in); - } - holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); } else if (messageType == 12) { if (selected) { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); @@ -2926,13 +2963,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); } holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); - } else if (messageType == 18) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); - } - holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), Utilities.dp(6)); } } } @@ -3010,21 +3040,13 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (view == null) { LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (type == 0) { - view = li.inflate(R.layout.chat_outgoing_text_layout, viewGroup, false); + view = new ChatMessageCell(mContext, false); } else if (type == 1) { - if (currentChat != null) { - view = li.inflate(R.layout.chat_group_incoming_text_layout, viewGroup, false); - } else { - view = li.inflate(R.layout.chat_incoming_text_layout, viewGroup, false); - } + view = new ChatMessageCell(mContext, currentChat != null); } else if (type == 8) { - view = li.inflate(R.layout.chat_outgoing_forward_layout, viewGroup, false); + view = new ChatMessageCell(mContext, false); } else if (type == 9) { - if (currentChat != null) { - view = li.inflate(R.layout.chat_group_incoming_forward_layout, viewGroup, false); - } else { - view = li.inflate(R.layout.chat_incoming_forward_layout, viewGroup, false); - } + view = new ChatMessageCell(mContext, currentChat != null); } else if (type == 4) { view = li.inflate(R.layout.chat_outgoing_location_layout, viewGroup, false); } else if (type == 5) { @@ -3072,27 +3094,31 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa view = li.inflate(R.layout.chat_incoming_document_layout, viewGroup, false); } } else if (type == 18) { - view = li.inflate(R.layout.chat_outgoing_audio_layout, viewGroup, false); + view = new ChatAudioCell(mContext, false); } else if (type == 19) { - if (currentChat != null) { - view = li.inflate(R.layout.chat_group_incoming_document_layout, viewGroup, false); - } else { - view = li.inflate(R.layout.chat_incoming_document_layout, viewGroup, false); - } + view = new ChatAudioCell(mContext, currentChat != null); } } - ChatListRowHolderEx holder = (ChatListRowHolderEx)view.getTag(); - if (holder == null) { - holder = new ChatListRowHolderEx(view, type); - view.setTag(holder); + if (view instanceof ChatBaseCell) { + ((ChatBaseCell)view).delegate = new ChatBaseCell.ChatBaseCellDelegate() { + @Override + public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { + if (user != null && user.id != UserConfig.clientUserId) { + UserProfileActivity fragment = new UserProfileActivity(); + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + fragment.setArguments(args); + ((ApplicationActivity)parentActivity).presentFragment(fragment, "user_" + user.id, false); + } + } + }; } - holder.message = message; boolean selected = false; boolean disableSelection = false; if (mActionMode != null) { - if (selectedMessagesIds.containsKey(holder.message.messageOwner.id)) { + if (selectedMessagesIds.containsKey(message.messageOwner.id)) { view.setBackgroundColor(0x6633b5e5); selected = true; } else { @@ -3102,8 +3128,20 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { view.setBackgroundColor(0); } - updateRowBackground(holder, disableSelection, selected); - holder.update(); + + if (view instanceof ChatBaseCell) { + ((ChatBaseCell)view).setMessageObject(message); + ((ChatBaseCell)view).setCheckPressed(!disableSelection, disableSelection && selected); + } else { + ChatListRowHolderEx holder = (ChatListRowHolderEx)view.getTag(); + if (holder == null) { + holder = new ChatListRowHolderEx(view, type); + view.setTag(holder); + } + holder.message = message; + updateRowBackground(holder, disableSelection, selected); + holder.update(); + } return view; } @@ -3148,10 +3186,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa public BackupImageView avatarImageView; public TextView nameTextView; public TextView messageTextView; - public MessageLayout messageLayout; public MessageActionLayout messageLayoutAction; - public TextView forwardedUserText; - public TextView foewardedUserName; public TextView timeTextView; public BackupImageView photoImage; public ImageView halfCheckImage; @@ -3181,18 +3216,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa int type = message.type; - if (type == 0 || type == 1 || type == 8 || type == 9) { - int width; - if (currentChat != null && (type == 1 || type == 9)) { - width = displaySize.x - Utilities.dp(122); - } else { - width = displaySize.x - Utilities.dp(80); - } - messageLayout.maxWidth = width; - messageLayout.messageTextView.setText(message.messageText); - messageLayout.messageTextView.setMaxWidth(width); - } - if (timeTextView != null) { timeTextView.setText(Utilities.formatterDay.format((long) (message.messageOwner.date) * 1000)); } @@ -3211,12 +3234,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa nameTextView.setTextColor(Utilities.getColorForId(message.messageOwner.from_id)); } - if (type == 8 || type == 9) { - TLRPC.User fwdUser = MessagesController.Instance.users.get(message.messageOwner.fwd_from_id); - if (fwdUser != null) { - forwardedUserText.setText(Html.fromHtml(getStringEntry(R.string.From) + " " + Utilities.formatName(fwdUser.first_name, fwdUser.last_name) + "")); - } - } else if (type == 2 || type == 3 || type == 6 || type == 7) { + if (type == 2 || type == 3 || type == 6 || type == 7) { int width = (int)(Math.min(displaySize.x, displaySize.y) * 0.7f); int height = width + Utilities.dp(100); if (type == 6 || type == 7) { @@ -3422,7 +3440,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } - if (message.messageOwner.id < 0 && message.messageOwner.send_state != MessagesController.MESSAGE_SEND_STATE_SENT) { + if (message.messageOwner.id < 0 && message.messageOwner.send_state != MessagesController.MESSAGE_SEND_STATE_SEND_ERROR && message.messageOwner.send_state != MessagesController.MESSAGE_SEND_STATE_SENT) { if (MessagesController.Instance.sendingMessages.get(message.messageOwner.id) == null) { message.messageOwner.send_state = MessagesController.MESSAGE_SEND_STATE_SEND_ERROR; } @@ -3539,7 +3557,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { load = true; } - } else { + } + if (load && message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0 || !load && (message.messageOwner.attachPath == null || message.messageOwner.attachPath.length() == 0)) { File cacheFile = null; if (((message.type == 2 || message.type == 3) && photoFileName == null) || (cacheFile = new File(Utilities.getCacheDir(), fileName)).exists()) { if (actionAttachButton != null) { @@ -3556,6 +3575,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (photoProgressView != null) { photoProgressView.setVisibility(View.GONE); } + load = false; } else { load = true; } @@ -3633,10 +3653,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa public ChatListRowHolderEx(View view, int type) { avatarImageView = (BackupImageView)view.findViewById(R.id.chat_group_avatar_image); nameTextView = (TextView)view.findViewById(R.id.chat_user_group_name); - messageLayout = (MessageLayout)view.findViewById(R.id.message_layout); messageLayoutAction = (MessageActionLayout)view.findViewById(R.id.message_action_layout); - forwardedUserText = (TextView)view.findViewById(R.id.chat_text_forward_name); - foewardedUserName = (TextView)view.findViewById(R.id.chat_text_forward_text); timeTextView = (TextView)view.findViewById(R.id.chat_time_text); photoImage = (BackupImageView)view.findViewById(R.id.chat_photo_image); halfCheckImage = (ImageView)view.findViewById(R.id.chat_row_halfcheck); @@ -3655,7 +3672,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa chatBubbleView = view.findViewById(R.id.chat_bubble_layout); photoProgressView = view.findViewById(R.id.photo_progress); if (messageTextView != null) { - messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, MessagesController.Instance.fontSize); } if (actionProgress != null) { @@ -3738,6 +3755,23 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa }); } + if (contactAvatar != null) { + contactAvatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (message.type == 18 || message.type == 19) { + if (message.messageOwner.media.audio.user_id != UserConfig.clientUserId && message.messageOwner.media.audio.user_id != 0) { + UserProfileActivity fragment = new UserProfileActivity(); + Bundle args = new Bundle(); + args.putInt("user_id", message.messageOwner.media.audio.user_id); + fragment.setArguments(args); + ((ApplicationActivity)parentActivity).presentFragment(fragment, "user_" + message.messageOwner.media.audio.user_id, false); + } + } + } + }); + } + if (actionAttachButton != null) { actionAttachButton.setOnClickListener(new View.OnClickListener() { @Override @@ -3827,22 +3861,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } }); } - - if (forwardedUserText != null) { - forwardedUserText.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - TLRPC.User fwdUser = MessagesController.Instance.users.get(message.messageOwner.fwd_from_id); - if (fwdUser != null && fwdUser.id != UserConfig.clientUserId) { - UserProfileActivity fragment = new UserProfileActivity(); - Bundle args = new Bundle(); - args.putInt("user_id", fwdUser.id); - fragment.setArguments(args); - ((ApplicationActivity)parentActivity).presentFragment(fragment, "user_" + fwdUser.id, false); - } - } - }); - } } private void alertUserOpenError() { @@ -3901,7 +3919,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa String fileName = message.getFileName(); if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { f = new File(message.messageOwner.attachPath); - } else { + } + if (f == null || f != null && !f.exists()) { f = new File(Utilities.getCacheDir(), fileName); } if (f != null && f.exists()) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java index 7932cae35..bfd34b48f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java @@ -36,7 +36,7 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; @@ -279,7 +279,7 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen public void didReceivedNotification(int id, Object... args) { if (id == MessagesController.updateInterfaces) { int mask = (Integer)args[0]; - if ((mask & MessagesController.UPDATE_MASK_CHAT_AVATAR) != 0 || (mask & MessagesController.UPDATE_MASK_CHAT_NAME) != 0 || (mask & MessagesController.UPDATE_MASK_CHAT_MEMBERS) != 0) { + if ((mask & MessagesController.UPDATE_MASK_CHAT_AVATAR) != 0 || (mask & MessagesController.UPDATE_MASK_CHAT_NAME) != 0 || (mask & MessagesController.UPDATE_MASK_CHAT_MEMBERS) != 0 || (mask & MessagesController.UPDATE_MASK_STATUS) != 0) { updateOnlineCount(); } if ((mask & MessagesController.UPDATE_MASK_AVATAR) != 0 || (mask & MessagesController.UPDATE_MASK_NAME) != 0 || (mask & MessagesController.UPDATE_MASK_STATUS) != 0) { @@ -382,7 +382,7 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen int i = 0; for (TLRPC.TL_chatParticipant participant : info.participants) { TLRPC.User user = MessagesController.Instance.users.get(participant.user_id); - if (user != null && user.status != null && (user.status.expires > currentTime || user.status.was_online > currentTime || user.id == UserConfig.clientUserId) && (user.status.expires > 10000 || user.status.was_online > 10000)) { + if (user != null && user.status != null && (user.status.expires > currentTime || user.id == UserConfig.clientUserId) && user.status.expires > 10000) { onlineCount++; } sortedUsers.add(i); @@ -401,9 +401,6 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen status1 = ConnectionsManager.Instance.getCurrentTime() + 50000; } else { status1 = user1.status.expires; - if (status1 == 0) { - status1 = user1.status.was_online; - } } } if (user2 != null && user2.status != null) { @@ -411,9 +408,6 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen status2 = ConnectionsManager.Instance.getCurrentTime() + 50000; } else { status2 = user2.status.expires; - if (status2 == 0) { - status2 = user2.status.was_online; - } } } return status1.compareTo(status2); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileChangeNameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileChangeNameActivity.java index abc024dd5..a2ca8c541 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileChangeNameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileChangeNameActivity.java @@ -22,7 +22,7 @@ import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java index 64e628dc6..83641ba75 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java @@ -24,7 +24,7 @@ import android.widget.EditText; import android.widget.TextView; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.MessagesController; @@ -131,17 +131,13 @@ public class ContactAddActivity extends BaseFragment implements NotificationCent onlineText.setText(getStringEntry(R.string.Offline)); } else { int currentTime = ConnectionsManager.Instance.getCurrentTime(); - if (user.status.expires > currentTime || user.status.was_online > currentTime) { + if (user.status.expires > currentTime) { onlineText.setText(getStringEntry(R.string.Online)); } else { - if (user.status.was_online <= 10000 && user.status.expires <= 10000) { + if (user.status.expires <= 10000) { onlineText.setText(getStringEntry(R.string.Invisible)); } else { - int value = user.status.was_online; - if (value == 0) { - value = user.status.expires; - } - onlineText.setText(Utilities.formatDateOnline(value)); + onlineText.setText(Utilities.formatDateOnline(user.status.expires)); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java index 765cca3c7..d5a69c3f2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java @@ -10,7 +10,6 @@ package org.telegram.ui; import android.app.Activity; import android.app.AlertDialog; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -20,7 +19,6 @@ import android.support.v4.internal.view.SupportMenuItem; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBar; import android.support.v7.widget.SearchView; -import android.text.Html; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -28,12 +26,11 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; @@ -43,6 +40,8 @@ import org.telegram.messenger.R; import org.telegram.messenger.RPCRequest; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; +import org.telegram.ui.Adapters.ContactsActivityAdapter; +import org.telegram.ui.Adapters.ContactsActivitySearchAdapter; import org.telegram.ui.Cells.ChatOrUserCell; import org.telegram.ui.Views.BaseFragment; import org.telegram.ui.Views.OnSwipeTouchListener; @@ -54,13 +53,11 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; -import java.util.Timer; -import java.util.TimerTask; public class ContactsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { private SectionedBaseAdapter listViewAdapter; private PinnedHeaderListView listView; - private BaseAdapter searchListViewAdapter; + private ContactsActivitySearchAdapter searchListViewAdapter; private boolean searchWas; private boolean searching; private boolean onlyUsers; @@ -75,11 +72,8 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter private HashMap ignoreUsers; private SupportMenuItem searchItem; - private Timer searchDialogsTimer; private String inviteText; private boolean updatingInviteText = false; - public ArrayList searchResult; - public ArrayList searchResultNames; public ContactsActivityDelegate delegate; public static interface ContactsActivityDelegate { @@ -144,22 +138,26 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (fragmentView == null) { + searching = false; + searchWas = false; + fragmentView = inflater.inflate(R.layout.contacts_layout, container, false); epmtyTextView = (TextView)fragmentView.findViewById(R.id.searchEmptyView); - searchListViewAdapter = new SearchAdapter(parentActivity); + searchListViewAdapter = new ContactsActivitySearchAdapter(parentActivity, ignoreUsers); listView = (PinnedHeaderListView)fragmentView.findViewById(R.id.listView); listView.setEmptyView(epmtyTextView); listView.setVerticalScrollBarEnabled(false); - listView.setAdapter(listViewAdapter = new ListAdapter(parentActivity)); + listViewAdapter = new ContactsActivityAdapter(parentActivity, onlyUsers, usersAsSections, ignoreUsers); + listView.setAdapter(listViewAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { if (searching && searchWas) { - TLRPC.User user = searchResult.get(i); - if (user.id == UserConfig.clientUserId) { + TLRPC.User user = searchListViewAdapter.getItem(i); + if (user == null || user.id == UserConfig.clientUserId) { return; } if (returnAsResult) { @@ -379,75 +377,6 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter ((ApplicationActivity)parentActivity).updateActionBar(); } - public void searchDialogs(final String query) { - if (query == null) { - searchResult = null; - searchResultNames = null; - } else { - try { - if (searchDialogsTimer != null) { - searchDialogsTimer.cancel(); - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - searchDialogsTimer = new Timer(); - searchDialogsTimer.schedule(new TimerTask() { - @Override - public void run() { - try { - searchDialogsTimer.cancel(); - searchDialogsTimer = null; - } catch (Exception e) { - FileLog.e("tmessages", e); - } - processSearch(query); - } - }, 100, 300); - } - } - - private void processSearch(final String query) { - Utilities.globalQueue.postRunnable(new Runnable() { - @Override - public void run() { - - String q = query.trim().toLowerCase(); - if (q.length() == 0) { - updateSearchResults(new ArrayList(), new ArrayList()); - return; - } - long time = System.currentTimeMillis(); - ArrayList resultArray = new ArrayList(); - ArrayList resultArrayNames = new ArrayList(); - - for (TLRPC.TL_contact contact : ContactsController.Instance.contacts) { - TLRPC.User user = MessagesController.Instance.users.get(contact.user_id); - if (user.first_name != null && user.first_name.toLowerCase().startsWith(q) || user.last_name != null && user.last_name.toLowerCase().startsWith(q)) { - if (user.id == UserConfig.clientUserId) { - continue; - } - resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q)); - resultArray.add(user); - } - } - - updateSearchResults(resultArray, resultArrayNames); - } - }); - } - - private void updateSearchResults(final ArrayList users, final ArrayList names) { - Utilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - searchResult = users; - searchResultNames = names; - searchListViewAdapter.notifyDataSetChanged(); - } - }); - } - @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); @@ -497,7 +426,10 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter @Override public boolean onQueryTextChange(String s) { - searchDialogs(s); + if (searchListViewAdapter == null) { + return true; + } + searchListViewAdapter.searchDialogs(s); if (s.length() != 0) { searchWas = true; if (listView != null) { @@ -531,7 +463,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter @Override public boolean onMenuItemActionCollapse(MenuItem menuItem) { searchView.setQuery("", false); - searchDialogs(null); + searchListViewAdapter.searchDialogs(null); searching = false; searchWas = false; ViewGroup group = (ViewGroup) listView.getParent(); @@ -578,331 +510,46 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter } private void updateInviteText() { - if (updatingInviteText) { - return; - } - updatingInviteText = true; - TLRPC.TL_help_getInviteText req = new TLRPC.TL_help_getInviteText(); - req.lang_code = Locale.getDefault().getCountry(); - if (req.lang_code == null || req.lang_code.length() == 0) { - req.lang_code = "en"; - } - ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { - @Override - public void run(TLObject response, TLRPC.TL_error error) { - if (error != null) { - return; - } - final TLRPC.TL_help_inviteText res = (TLRPC.TL_help_inviteText)response; - if (res.message.length() == 0) { - return; - } - Utilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - updatingInviteText = false; - SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - SharedPreferences.Editor editor = preferences.edit(); - editor.putString("invitetext", res.message); - editor.putInt("invitetexttime", (int) (System.currentTimeMillis() / 1000)); - editor.commit(); - } - }); + if (!updatingInviteText) { + updatingInviteText = true; + TLRPC.TL_help_getInviteText req = new TLRPC.TL_help_getInviteText(); + req.lang_code = Locale.getDefault().getCountry(); + if (req.lang_code == null || req.lang_code.length() == 0) { + req.lang_code = "en"; } - }, null, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); + ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (error == null) { + final TLRPC.TL_help_inviteText res = (TLRPC.TL_help_inviteText)response; + if (res.message.length() != 0) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + updatingInviteText = false; + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("invitetext", res.message); + editor.putInt("invitetexttime", (int) (System.currentTimeMillis() / 1000)); + editor.commit(); + } + }); + } + } + } + }, null, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); + } } private void updateVisibleRows(int mask) { - if (listView == null) { - return; - } - int count = listView.getChildCount(); - for (int a = 0; a < count; a++) { - View child = listView.getChildAt(a); - if (child instanceof ChatOrUserCell) { - ((ChatOrUserCell) child).update(mask); - } - } - } - - private class SearchAdapter extends BaseAdapter { - private Context mContext; - - public SearchAdapter(Context context) { - mContext = context; - } - - @Override - public boolean areAllItemsEnabled() { - return true; - } - - @Override - public boolean isEnabled(int i) { - return true; - } - - @Override - public int getCount() { - if (searchResult == null) { - return 0; - } - return searchResult.size(); - } - - @Override - public Object getItem(int i) { - return null; - } - - @Override - public long getItemId(int i) { - return i; - } - - @Override - public boolean hasStableIds() { - return false; - } - - @Override - public View getView(int i, View view, ViewGroup viewGroup) { - if (view == null) { - view = new ChatOrUserCell(mContext); - ((ChatOrUserCell)view).usePadding = false; - } - - ((ChatOrUserCell) view).useSeparator = i != searchResult.size() - 1; - - Object obj = searchResult.get(i); - TLRPC.User user = MessagesController.Instance.users.get(((TLRPC.User)obj).id); - - if (user != null) { - ((ChatOrUserCell)view).setData(user, null, null, searchResultNames.get(i), null); - - if (ignoreUsers != null) { - if (ignoreUsers.containsKey(user.id)) { - ((ChatOrUserCell)view).drawAlpha = 0.5f; - } else { - ((ChatOrUserCell)view).drawAlpha = 1.0f; - } + if (listView != null) { + int count = listView.getChildCount(); + for (int a = 0; a < count; a++) { + View child = listView.getChildAt(a); + if (child instanceof ChatOrUserCell) { + ((ChatOrUserCell) child).update(mask); } } - return view; - } - - @Override - public int getItemViewType(int i) { - return 0; - } - - @Override - public int getViewTypeCount() { - return 1; - } - - @Override - public boolean isEmpty() { - return searchResult == null || searchResult.size() == 0; - } - } - - private class ListAdapter extends SectionedBaseAdapter { - private Context mContext; - - public ListAdapter(Context context) { - mContext = context; - } - - @Override - public Object getItem(int section, int position) { - return null; - } - - @Override - public long getItemId(int section, int position) { - return 0; - } - - @Override - public int getSectionCount() { - int count = 0; - if (usersAsSections) { - count += ContactsController.Instance.sortedUsersSectionsArray.size(); - } else { - count++; - } - if (!onlyUsers) { - count += ContactsController.Instance.sortedContactsSectionsArray.size(); - } - return count; - } - - @Override - public int getCountForSection(int section) { - if (usersAsSections) { - if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { - ArrayList arr = ContactsController.Instance.usersSectionsDict.get(ContactsController.Instance.sortedUsersSectionsArray.get(section)); - return arr.size(); - } - } else { - if (section == 0) { - return ContactsController.Instance.contacts.size() + 1; - } - } - ArrayList arr = ContactsController.Instance.contactsSectionsDict.get(ContactsController.Instance.sortedContactsSectionsArray.get(section - 1)); - return arr.size(); - } - - @Override - public View getItemView(int section, int position, View convertView, ViewGroup parent) { - - TLRPC.User user = null; - int count = 0; - if (usersAsSections) { - if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { - ArrayList arr = ContactsController.Instance.usersSectionsDict.get(ContactsController.Instance.sortedUsersSectionsArray.get(section)); - user = MessagesController.Instance.users.get(arr.get(position).user_id); - count = arr.size(); - } - } else { - if (section == 0) { - if (position == 0) { - if (convertView == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = li.inflate(R.layout.contacts_invite_row_layout, parent, false); - } - View divider = convertView.findViewById(R.id.settings_row_divider); - if (ContactsController.Instance.contacts.isEmpty()) { - divider.setVisibility(View.INVISIBLE); - } else { - divider.setVisibility(View.VISIBLE); - } - return convertView; - } - user = MessagesController.Instance.users.get(ContactsController.Instance.contacts.get(position - 1).user_id); - count = ContactsController.Instance.contacts.size(); - } - } - if (user != null) { - if (convertView == null) { - convertView = new ChatOrUserCell(mContext); - ((ChatOrUserCell)convertView).useBoldFont = true; - ((ChatOrUserCell)convertView).usePadding = false; - } - - ((ChatOrUserCell)convertView).setData(user, null, null, null, null); - - if (ignoreUsers != null) { - if (ignoreUsers.containsKey(user.id)) { - ((ChatOrUserCell)convertView).drawAlpha = 0.5f; - } else { - ((ChatOrUserCell)convertView).drawAlpha = 1.0f; - } - } - - ((ChatOrUserCell) convertView).useSeparator = position != count - 1; - - return convertView; - } - - TextView textView; - if (convertView == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = li.inflate(R.layout.settings_row_button_layout, parent, false); - textView = (TextView)convertView.findViewById(R.id.settings_row_text); - } else { - textView = (TextView)convertView.findViewById(R.id.settings_row_text); - } - - View divider = convertView.findViewById(R.id.settings_row_divider); - ArrayList arr = ContactsController.Instance.contactsSectionsDict.get(ContactsController.Instance.sortedContactsSectionsArray.get(section - 1)); - ContactsController.Contact contact = arr.get(position); - if (divider != null) { - if (position == arr.size() - 1) { - divider.setVisibility(View.INVISIBLE); - } else { - divider.setVisibility(View.VISIBLE); - } - } - if (contact.first_name != null && contact.last_name != null) { - textView.setText(Html.fromHtml(contact.first_name + " " + contact.last_name + "")); - } else if (contact.first_name != null && contact.last_name == null) { - textView.setText(Html.fromHtml("" + contact.first_name + "")); - } else { - textView.setText(Html.fromHtml("" + contact.last_name + "")); - } - return convertView; - } - - @Override - public int getItemViewType(int section, int position) { - if (usersAsSections) { - if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { - return 0; - } - } else if (section == 0) { - if (position == 0) { - return 2; - } - return 0; - } - return 1; - } - - @Override - public int getItemViewTypeCount() { - return 3; - } - - @Override - public int getSectionHeaderViewType(int section) { - if (usersAsSections) { - if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { - return 1; - } - } else if (section == 0) { - return 0; - } - return 1; - } - - @Override - public int getSectionHeaderViewTypeCount() { - return 2; - } - - @Override - public View getSectionHeaderView(int section, View convertView, ViewGroup parent) { - if (usersAsSections) { - if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { - if (convertView == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = li.inflate(R.layout.settings_section_layout, parent, false); - convertView.setBackgroundColor(0xffffffff); - } - TextView textView = (TextView)convertView.findViewById(R.id.settings_section_text); - textView.setText(ContactsController.Instance.sortedUsersSectionsArray.get(section)); - return convertView; - } - } else { - if (section == 0) { - if (convertView == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = li.inflate(R.layout.empty_layout, parent, false); - } - return convertView; - } - } - - if (convertView == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = li.inflate(R.layout.settings_section_layout, parent, false); - convertView.setBackgroundColor(0xffffffff); - } - TextView textView = (TextView)convertView.findViewById(R.id.settings_section_text); - textView.setText(ContactsController.Instance.sortedContactsSectionsArray.get(section - 1)); - return convertView; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java index 9a8b78fdf..b3edd663e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java @@ -69,6 +69,9 @@ public class CountrySelectActivity extends ActionBarActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + searching = false; + searchWas = false; + try { BufferedReader reader = new BufferedReader(new InputStreamReader(getResources().getAssets().open("countries.txt"))); String line; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java index 4877e3da1..e1598d60a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java @@ -79,10 +79,14 @@ public class DocumentSelectActivity extends BaseFragment { public void onReceive(Context arg0, Intent intent) { Runnable r = new Runnable() { public void run() { - if (currentDir == null){ - listRoots(); - } else { - listFiles(currentDir); + try { + if (currentDir == null){ + listRoots(); + } else { + listFiles(currentDir); + } + } catch (Exception e) { + FileLog.e("tmessages", e); } } }; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java index 9c1f4a3a8..f16cc2db8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java @@ -35,7 +35,7 @@ import org.telegram.objects.PhotoObject; import org.telegram.ui.Views.AbstractGalleryActivity; import org.telegram.ui.Views.GalleryViewPager; import org.telegram.ui.Views.PZSImageView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.objects.MessageObject; import org.telegram.messenger.FileLoader; import org.telegram.messenger.MessagesController; @@ -479,7 +479,7 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif } private TLRPC.FileLocation getCurrentFile() { - if (mViewPager == null) { + if (mViewPager == null || localPagerAdapter == null) { return null; } int item = mViewPager.getCurrentItem(); @@ -497,7 +497,16 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif } } } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) { - TLRPC.PhotoSize sizeFull = PhotoObject.getClosestPhotoSizeWithSize(message.messageOwner.media.photo.sizes, 800, 800); + int width = (int)(Math.min(displaySize.x, displaySize.y) * 0.7f); + int height = width + Utilities.dp(100); + if (width > 800) { + width = 800; + } + if (height > 800) { + height = 800; + } + + TLRPC.PhotoSize sizeFull = PhotoObject.getClosestPhotoSizeWithSize(message.messageOwner.media.photo.sizes, width, height); if (sizeFull != null) { return sizeFull.location; } @@ -538,7 +547,16 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif } else { ArrayList sizes = obj.messageOwner.action.photo.sizes; if (sizes.size() > 0) { - TLRPC.PhotoSize sizeFull = PhotoObject.getClosestPhotoSizeWithSize(sizes, 800, 800); + int width = (int)(Math.min(displaySize.x, displaySize.y) * 0.7f); + int height = width + Utilities.dp(100); + if (width > 800) { + width = 800; + } + if (height > 800) { + height = 800; + } + + TLRPC.PhotoSize sizeFull = PhotoObject.getClosestPhotoSizeWithSize(sizes, width, height); if (sizeFull != null) { currentFileName = sizeFull.location.volume_id + "_" + sizeFull.location.local_id + ".jpg"; } @@ -634,10 +652,14 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - mViewPager.beginFakeDrag(); - if (mViewPager.isFakeDragging()) { - mViewPager.fakeDragBy(1); - mViewPager.endFakeDrag(); + try { + mViewPager.beginFakeDrag(); + if (mViewPager.isFakeDragging()) { + mViewPager.fakeDragBy(1); + mViewPager.endFakeDrag(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); } mViewPager.getViewTreeObserver().removeOnPreDrawListener(this); return false; @@ -660,7 +682,9 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif switch (itemId) { case android.R.id.home: cancelRunning = true; - mViewPager.setAdapter(null); + if (mViewPager != null) { + mViewPager.setAdapter(null); + } localPagerAdapter = null; finish(); System.gc(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java index afa191db7..27ce632f9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java @@ -35,7 +35,7 @@ import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.Emoji; @@ -106,6 +106,10 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (fragmentView == null) { + + searching = false; + searchWas = false; + fragmentView = inflater.inflate(R.layout.group_create_layout, container, false); epmtyTextView = (TextView)fragmentView.findViewById(R.id.searchEmptyView); @@ -546,18 +550,14 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen holder.messageTextView.setTextColor(0xff808080); } else { int currentTime = ConnectionsManager.Instance.getCurrentTime(); - if (user.status.expires > currentTime || user.status.was_online > currentTime) { + if (user.status.expires > currentTime) { holder.messageTextView.setTextColor(0xff357aa8); holder.messageTextView.setText(getStringEntry(R.string.Online)); } else { - if (user.status.was_online <= 10000 && user.status.expires <= 10000) { + if (user.status.expires <= 10000) { holder.messageTextView.setText(getStringEntry(R.string.Invisible)); } else { - int value = user.status.was_online; - if (value == 0) { - value = user.status.expires; - } - holder.messageTextView.setText(Utilities.formatDateOnline(value)); + holder.messageTextView.setText(Utilities.formatDateOnline(user.status.expires)); } holder.messageTextView.setTextColor(0xff808080); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java index 24d72c46b..3b088bfaa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -25,7 +25,7 @@ import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/IdenticonActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/IdenticonActivity.java index fe4d6c9ce..0d9c703ca 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/IdenticonActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/IdenticonActivity.java @@ -22,7 +22,7 @@ import android.view.WindowManager; import android.widget.LinearLayout; import android.widget.TextView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 65a32b3a3..b972cc5b2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -15,8 +15,9 @@ import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.widget.Toast; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; @@ -25,6 +26,8 @@ import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.Views.PausableActivity; +import java.util.ArrayList; + public class LaunchActivity extends PausableActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -46,63 +49,77 @@ public class LaunchActivity extends PausableActivity { Intent intent = getIntent(); if (intent != null && intent.getAction() != null) { if (Intent.ACTION_SEND.equals(intent.getAction())) { - if (intent.getType() != null) { - if (intent.getType().startsWith("image/")) { - Parcelable parcelable = intent.getParcelableExtra(Intent.EXTRA_STREAM); - if (parcelable == null) { - return; - } - String path = null; - if (parcelable instanceof Uri) { - path = Utilities.getPath((Uri)parcelable); - } else { - path = intent.getParcelableExtra(Intent.EXTRA_STREAM).toString(); - if (path.startsWith("content:")) { - Cursor cursor = getContentResolver().query(Uri.parse(path), new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null); - if (cursor != null) { - cursor.moveToFirst(); - path = cursor.getString(0); - cursor.close(); - } - } - } - if (path != null) { - if (path.startsWith("file:")) { - path = path.replace("file://", ""); - } - NotificationCenter.Instance.addToMemCache(533, path); - } - } else if (intent.getType().startsWith("video/")) { - Parcelable parcelable = intent.getParcelableExtra(Intent.EXTRA_STREAM); - if (parcelable == null) { - return; - } - String path = null; - if (parcelable instanceof Uri) { - path = Utilities.getPath((Uri)parcelable); - } else { - path = parcelable.toString(); - if (path.startsWith("content:")) { - Cursor cursor = getContentResolver().query(Uri.parse(path), new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null); - if (cursor != null) { - cursor.moveToFirst(); - path = cursor.getString(0); - cursor.close(); - } - } - } - if (path != null) { - if (path.startsWith("file:")) { - path = path.replace("file://", ""); - } - NotificationCenter.Instance.addToMemCache(534, path); - } - } else if (intent.getType().equals("text/plain")) { - String text = intent.getStringExtra(Intent.EXTRA_TEXT); - if (text != null && text.length() != 0) { - NotificationCenter.Instance.addToMemCache(535, text); - } + boolean error = false; + String type = intent.getType(); + if (type != null && type.equals("text/plain")) { + String text = intent.getStringExtra(Intent.EXTRA_TEXT); + if (text != null && text.length() != 0) { + NotificationCenter.Instance.addToMemCache(535, text); + } else { + error = true; } + } else { + Parcelable parcelable = intent.getParcelableExtra(Intent.EXTRA_STREAM); + if (parcelable == null) { + return; + } + String path = null; + if (!(parcelable instanceof Uri)) { + parcelable = Uri.parse(parcelable.toString()); + } + path = Utilities.getPath((Uri)parcelable); + if (path != null) { + if (path.startsWith("file:")) { + path = path.replace("file://", ""); + } + if (type != null && type.startsWith("image/")) { + NotificationCenter.Instance.addToMemCache(533, path); + } else if (type != null && type.startsWith("video/")) { + NotificationCenter.Instance.addToMemCache(534, path); + } else { + NotificationCenter.Instance.addToMemCache(536, path); + } + } else { + error = true; + } + if (error) { + Toast.makeText(this, "Unsupported content", Toast.LENGTH_SHORT).show(); + } + } + } else if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) { + boolean error = false; + try { + ArrayList uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + String type = intent.getType(); + if (uris != null) { + String[] uris2 = new String[uris.size()]; + for (int i = 0; i < uris2.length; i++) { + Parcelable parcelable = uris.get(i); + if (!(parcelable instanceof Uri)) { + parcelable = Uri.parse(parcelable.toString()); + } + String path = Utilities.getPath((Uri)parcelable); + if (path != null) { + if (path.startsWith("file:")) { + path = path.replace("file://", ""); + } + uris2[i] = path; + } + } + if (type != null && type.startsWith("image/")) { + NotificationCenter.Instance.addToMemCache(537, uris2); + } else { + NotificationCenter.Instance.addToMemCache(538, uris2); + } + } else { + error = true; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + error = true; + } + if (error) { + Toast.makeText(this, "Unsupported content", Toast.LENGTH_SHORT).show(); } } else if (Intent.ACTION_VIEW.equals(intent.getAction())) { try { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java index b19633af8..62a26942e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -28,7 +28,7 @@ import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.objects.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java index c7b0cfa37..457368b2d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java @@ -26,8 +26,8 @@ import android.widget.EditText; import android.widget.TextView; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; @@ -327,6 +327,7 @@ public class LoginActivityPhoneView extends SlideView implements AdapterView.OnI if (error == null) { final TLRPC.TL_auth_sentCode res = (TLRPC.TL_auth_sentCode)response; params.putString("phoneHash", res.phone_code_hash); + params.putInt("calltime", res.send_call_timeout * 1000); if (res.phone_registered) { params.putString("registered", "true"); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityRegisterView.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityRegisterView.java index 3d5baf207..85f423723 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityRegisterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityRegisterView.java @@ -19,8 +19,8 @@ import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.MessagesController; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivitySmsView.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivitySmsView.java index b992b5298..40a21b0d1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivitySmsView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivitySmsView.java @@ -21,8 +21,8 @@ import android.widget.EditText; import android.widget.TextView; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; @@ -53,6 +53,7 @@ public class LoginActivitySmsView extends SlideView implements NotificationCente private int time = 60000; private double lastCurrentTime; private boolean waitingForSms = false; + private int callTime = 60000; public LoginActivitySmsView(Context context) { super(context); @@ -113,6 +114,7 @@ public class LoginActivitySmsView extends SlideView implements NotificationCente requestPhone = params.getString("phoneFormated"); phoneHash = params.getString("phoneHash"); registered = params.getString("registered"); + callTime = params.getInt("calltime"); String number = PhoneFormat.Instance.format(phone); confirmTextView.setText(Html.fromHtml(String.format(ApplicationLoader.applicationContext.getResources().getString(R.string.SentSmsCode) + " %s", number))); @@ -120,7 +122,7 @@ public class LoginActivitySmsView extends SlideView implements NotificationCente Utilities.showKeyboard(codeField); codeField.requestFocus(); - time = 60000; + time = callTime; try { synchronized(timerSync) { if (timeTimer != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java index 53a812f29..c967361ac 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java @@ -27,7 +27,7 @@ import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.TextView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.Utilities; import org.telegram.objects.MessageObject; import org.telegram.messenger.MessagesController; @@ -288,24 +288,27 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - WindowManager manager = (WindowManager)parentActivity.getSystemService(Activity.WINDOW_SERVICE); - int rotation = manager.getDefaultDisplay().getRotation(); + if (parentActivity != null) { + WindowManager manager = (WindowManager)parentActivity.getSystemService(Activity.WINDOW_SERVICE); + int rotation = manager.getDefaultDisplay().getRotation(); - if (rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90) { - orientation = 1; - listView.setNumColumns(6); - itemWidth = getResources().getDisplayMetrics().widthPixels / 6 - Utilities.dp(2) * 5; - listView.setColumnWidth(itemWidth); - } else { - orientation = 0; - listView.setNumColumns(4); - itemWidth = getResources().getDisplayMetrics().widthPixels / 4 - Utilities.dp(2) * 3; - listView.setColumnWidth(itemWidth); + if (rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90) { + orientation = 1; + listView.setNumColumns(6); + itemWidth = getResources().getDisplayMetrics().widthPixels / 6 - Utilities.dp(2) * 5; + listView.setColumnWidth(itemWidth); + } else { + orientation = 0; + listView.setNumColumns(4); + itemWidth = getResources().getDisplayMetrics().widthPixels / 4 - Utilities.dp(2) * 3; + listView.setColumnWidth(itemWidth); + } + listView.setPadding(listView.getPaddingLeft(), Utilities.dp(4), listView.getPaddingRight(), listView.getPaddingBottom()); + listAdapter.notifyDataSetChanged(); + } + if (listView != null) { + listView.getViewTreeObserver().removeOnPreDrawListener(this); } - listView.setPadding(listView.getPaddingLeft(), Utilities.dp(4), listView.getPaddingRight(), listView.getPaddingBottom()); - listAdapter.notifyDataSetChanged(); - - listView.getViewTreeObserver().removeOnPreDrawListener(this); return false; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java index 60a6c34a5..98fa3b7a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java @@ -30,8 +30,8 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; @@ -121,6 +121,9 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (fragmentView == null) { + searching = false; + searchWas = false; + fragmentView = inflater.inflate(R.layout.messages_list, container, false); messagesListViewAdapter = new MessagesAdapter(parentActivity); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index 5365378bb..b4ea595ef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -10,6 +10,7 @@ package org.telegram.ui; import android.app.Activity; import android.app.AlertDialog; +import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -19,6 +20,7 @@ import android.net.Uri; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; +import android.util.Base64; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -32,8 +34,10 @@ import android.widget.TextView; import android.widget.Toast; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.SerializedData; +import org.telegram.messenger.TLClassStore; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; @@ -70,6 +74,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter int askQuestionRow; int logoutRow; int sendLogsRow; + int clearLogsRow; int rowCount; int messagesSectionRow; int sendByEnterRow; @@ -159,6 +164,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter supportSectionRow = rowCount++; if (ConnectionsManager.DEBUG_VERSION) { sendLogsRow = rowCount++; + clearLogsRow = rowCount++; } askQuestionRow = rowCount++; logoutRow = rowCount++; @@ -198,6 +204,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("fons_size", 12 + which); + MessagesController.Instance.fontSize = 12 + which; editor.commit(); if (listView != null) { listView.invalidateViews(); @@ -222,13 +229,92 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } else if (i == backgroundRow) { ((ApplicationActivity)parentActivity).presentFragment(new SettingsWallpapersActivity(), "settings_wallpapers", false); } else if (i == askQuestionRow) { - ChatActivity fragment = new ChatActivity(); - Bundle bundle = new Bundle(); - bundle.putInt("user_id", 333000); - fragment.setArguments(bundle); - ((ApplicationActivity)parentActivity).presentFragment(fragment, "chat" + Math.random(), false); + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + int uid = preferences.getInt("support_id", 0); + TLRPC.User supportUser = null; + if (uid != 0) { + supportUser = MessagesController.Instance.users.get(uid); + if (supportUser == null) { + String userString = preferences.getString("support_user", null); + if (userString != null) { + try { + byte[] datacentersBytes = Base64.decode(userString, Base64.DEFAULT); + if (datacentersBytes != null) { + SerializedData data = new SerializedData(datacentersBytes); + supportUser = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + + } + } catch (Exception e) { + FileLog.e("tmessages", e); + supportUser = null; + } + } + } + } + if (supportUser == null) { + if (parentActivity == null) { + return; + } + final ProgressDialog progressDialog = new ProgressDialog(parentActivity); + progressDialog.setMessage(parentActivity.getString(R.string.Loading)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + progressDialog.show(); + TLRPC.TL_help_getSupport req = new TLRPC.TL_help_getSupport(); + ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (error == null) { + + final TLRPC.TL_help_support res = (TLRPC.TL_help_support)response; + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt("support_id", res.user.id); + SerializedData data = new SerializedData(); + res.user.serializeToStream(data); + editor.putString("support_user", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); + editor.commit(); + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + MessagesController.Instance.users.put(res.user.id, res.user); + ChatActivity fragment = new ChatActivity(); + Bundle bundle = new Bundle(); + bundle.putInt("user_id", res.user.id); + fragment.setArguments(bundle); + ((ApplicationActivity)parentActivity).presentFragment(fragment, "chat" + Math.random(), false); + } + }); + } else { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + } + }, null, true, RPCRequest.RPCRequestClassGeneric); + } else { + MessagesController.Instance.users.putIfAbsent(supportUser.id, supportUser); + ChatActivity fragment = new ChatActivity(); + Bundle bundle = new Bundle(); + bundle.putInt("user_id", supportUser.id); + fragment.setArguments(bundle); + ((ApplicationActivity)parentActivity).presentFragment(fragment, "chat" + Math.random(), false); + } } else if (i == sendLogsRow) { sendLogs(); + } else if (i == clearLogsRow) { + FileLog.cleanupLogs(); } else if (i == sendByEnterRow) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); boolean send = preferences.getBoolean("send_by_enter", false); @@ -314,11 +400,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == 232) { - FileLog.cleanupLogs(); - } else { - avatarUpdater.onActivityResult(requestCode, resultCode, data); - } + avatarUpdater.onActivityResult(requestCode, resultCode, data); } @Override @@ -377,7 +459,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter i.putExtra(Intent.EXTRA_EMAIL, new String[]{"drklo.2kb@gmail.com"}); i.putExtra(Intent.EXTRA_SUBJECT, "last logs"); i.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); - startActivityForResult(Intent.createChooser(i, "Select email application."), 232); + startActivity(Intent.createChooser(i, "Select email application.")); } catch (Exception e) { e.printStackTrace(); } @@ -427,7 +509,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter public boolean isEnabled(int i) { return i == textSizeRow || i == enableAnimationsRow || i == blockedRow || i == notificationRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == sendByEnterRow || i == terminateSessionsRow || i == photoDownloadPrivateRow || - i == photoDownloadChatRow; + i == photoDownloadChatRow || i == clearLogsRow; } @Override @@ -619,6 +701,9 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } else if (i == sendLogsRow) { textView.setText("Send Logs"); divider.setVisibility(View.VISIBLE); + } else if (i == clearLogsRow) { + textView.setText("Clear Logs"); + divider.setVisibility(View.VISIBLE); } else if (i == askQuestionRow) { textView.setText(getStringEntry(R.string.AskAQuestion)); divider.setVisibility(View.INVISIBLE); @@ -738,7 +823,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter return 5; } else if (i == enableAnimationsRow || i == sendByEnterRow || i == photoDownloadChatRow || i == photoDownloadPrivateRow) { return 3; - } else if (i == numberRow || i == notificationRow || i == blockedRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == terminateSessionsRow) { + } else if (i == numberRow || i == notificationRow || i == blockedRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == terminateSessionsRow || i == clearLogsRow) { return 2; } else if (i == logoutRow) { return 4; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java index 5febab5b8..9fb79034e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java @@ -25,8 +25,8 @@ import android.widget.ListView; import android.widget.TextView; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeNameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeNameActivity.java index 07d736a47..e7ebf258a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeNameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsChangeNameActivity.java @@ -16,15 +16,14 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java index 18d1dba47..f4de45f30 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java @@ -30,8 +30,8 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java index be4678a69..61b119517 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java @@ -29,8 +29,8 @@ import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ProgressBar; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; @@ -356,6 +356,9 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica long reqId = ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { @Override public void run(final TLObject response, TLRPC.TL_error error) { + if (error != null) { + return; + } Utilities.RunOnUIThread(new Runnable() { @Override public void run() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java index f81cc5ab1..faf8e424d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java @@ -37,8 +37,8 @@ import android.widget.ListView; import android.widget.TextView; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.TL.TLObject; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLObject; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; @@ -632,17 +632,13 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen onlineText.setText(getStringEntry(R.string.Offline)); } else { int currentTime = ConnectionsManager.Instance.getCurrentTime(); - if (user.status.expires > currentTime || user.status.was_online > currentTime) { + if (user.status.expires > currentTime) { onlineText.setText(getStringEntry(R.string.Online)); } else { - if (user.status.was_online <= 10000 && user.status.expires <= 10000) { + if (user.status.expires <= 10000) { onlineText.setText(getStringEntry(R.string.Invisible)); } else { - int value = user.status.was_online; - if (value == 0) { - value = user.status.expires; - } - onlineText.setText(Utilities.formatDateOnline(value)); + onlineText.setText(Utilities.formatDateOnline(user.status.expires)); } } } @@ -722,6 +718,9 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { + if (parentActivity == null) { + return; + } TLRPC.User user = MessagesController.Instance.users.get(user_id); if (user == null || user instanceof TLRPC.TL_userEmpty) { return; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java index 3f576e371..c11f8c81e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java @@ -15,7 +15,7 @@ import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.NotificationCenter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java index 397dcf57a..ce5f8328f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java @@ -16,7 +16,7 @@ import android.graphics.drawable.NinePatchDrawable; import android.os.Build; import android.widget.ImageView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.Utilities; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java index 65759ac27..66ba390e7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java @@ -15,7 +15,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.view.View; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.Utilities; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/MessageLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/MessageLayout.java deleted file mode 100644 index c2764d099..000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/MessageLayout.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 1.3.2. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013. - */ - -package org.telegram.ui.Views; - -import android.view.View; -import android.widget.TextView; - -import org.telegram.messenger.R; -import org.telegram.messenger.Utilities; - -public class MessageLayout extends FrameLayoutFixed { - public TextView timeTextView; - public View timeLayout; - public TightTextView messageTextView; - public int maxWidth; - - public MessageLayout(android.content.Context context) { - super(context); - } - - public MessageLayout(android.content.Context context, android.util.AttributeSet attrs) { - super(context, attrs); - } - - public MessageLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int timeWidth = timeLayout != null ? timeLayout.getMeasuredWidth() : timeTextView.getMeasuredWidth(); - int totalWidth = getMeasuredWidth(); - - int maxChildWidth = getChildAt(0).getMeasuredWidth(); - int count = getChildCount(); - for (int a = 1; a < count - 1; a++) { - maxChildWidth = Math.max(maxChildWidth, getChildAt(a).getMeasuredWidth()); - } - int timeMore = timeWidth + Utilities.dp(6); - int fields = totalWidth - Math.max(maxChildWidth, timeWidth); - int height = getMeasuredHeight(); - - if (maxWidth - messageTextView.lastLineWidth < timeMore) { - setMeasuredDimension(Math.max(maxChildWidth, messageTextView.lastLineWidth) + fields, height + Utilities.dp(14)); - } else { - int diff = maxChildWidth - messageTextView.lastLineWidth; - if (diff >= 0 && diff <= timeMore) { - setMeasuredDimension(maxChildWidth + timeMore - diff + fields, height); - } else { - setMeasuredDimension(Math.max(maxChildWidth, messageTextView.lastLineWidth + timeMore) + fields, height); - } - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - timeTextView = (TextView)findViewById(R.id.chat_time_text); - messageTextView = (TightTextView)findViewById(R.id.chat_message_text); - timeLayout = findViewById(R.id.chat_time_layout); - } -} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/NotificationView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/NotificationView.java index 17c64028a..89ebd0984 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/NotificationView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/NotificationView.java @@ -23,7 +23,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import org.telegram.TL.TLRPC; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/ProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/ProgressView.java new file mode 100644 index 000000000..da0e62184 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/ProgressView.java @@ -0,0 +1,63 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Views; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import org.telegram.messenger.Utilities; + +public class ProgressView { + private static Paint innerPaint1; + private static Paint outerPaint1; + private static Paint innerPaint2; + private static Paint outerPaint2; + + public int type; + public int thumbX = 0; + public int width; + public int height; + + public ProgressView() { + if (innerPaint1 == null) { + innerPaint1 = new Paint(); + outerPaint1 = new Paint(); + innerPaint2 = new Paint(); + outerPaint2 = new Paint(); + + innerPaint1.setColor(0xffb4e396); + outerPaint1.setColor(0xff6ac453); + innerPaint2.setColor(0xffd9e2eb); + outerPaint2.setColor(0xff86c5f8); + } + } + + public void setProgress(float progress) { + thumbX = (int)Math.ceil(width * progress); + if (thumbX < 0) { + thumbX = 0; + } else if (thumbX > width) { + thumbX = width; + } + } + + public void draw(Canvas canvas) { + Paint inner = null; + Paint outer = null; + if (type == 0) { + inner = innerPaint1; + outer = outerPaint1; + } else if (type == 1) { + inner = innerPaint2; + outer = outerPaint2; + } + canvas.drawRect(0, height / 2 - Utilities.dp(1), width, height / 2 + Utilities.dp(1), inner); + canvas.drawRect(0, height / 2 - Utilities.dp(1), thumbX, height / 2 + Utilities.dp(1), outer); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/SectionedBaseAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/SectionedBaseAdapter.java index d764719aa..6e2a00440 100755 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/SectionedBaseAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/SectionedBaseAdapter.java @@ -13,7 +13,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -public abstract class SectionedBaseAdapter extends BaseAdapter implements PinnedHeaderListView.PinnedSectionedHeaderAdapter { +import org.telegram.ui.Adapters.BaseFragmentAdapter; + +public abstract class SectionedBaseAdapter extends BaseFragmentAdapter implements PinnedHeaderListView.PinnedSectionedHeaderAdapter { /** * Holds the calculated values of @{link getPositionInSectionForPosition} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java index 0d55da6ac..268e6173d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java @@ -12,51 +12,41 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; -import android.util.AttributeSet; import android.view.MotionEvent; -import android.view.View; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; -public class SeekBar extends View { - Drawable thumbDrawable1; - Drawable thumbDrawablePressed1; - Drawable thumbDrawable2; - Drawable thumbDrawablePressed2; - static Paint innerPaint1 = new Paint(); - static Paint outerPaint1 = new Paint(); - static Paint innerPaint2 = new Paint(); - static Paint outerPaint2 = new Paint(); +public class SeekBar { + + public abstract interface SeekBarDelegate { + public void onSeekBarDrag(float progress); + } + + private static Drawable thumbDrawable1; + private static Drawable thumbDrawablePressed1; + private static Drawable thumbDrawable2; + private static Drawable thumbDrawablePressed2; + private static Paint innerPaint1 = new Paint(); + private static Paint outerPaint1 = new Paint(); + private static Paint innerPaint2 = new Paint(); + private static Paint outerPaint2 = new Paint(); + private static int thumbWidth; + private static int thumbHeight; public int type; public int thumbX = 0; public int thumbDX = 0; private boolean pressed = false; - private boolean dragging = false; - private int thumbWidth; - private int thumbHeight; + public int width; + public int height; + public SeekBarDelegate delegate; public SeekBar(Context context) { - super(context); - init(); - } - - public SeekBar(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public SeekBar(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - private void init() { if (thumbDrawable1 == null) { - thumbDrawable1 = getResources().getDrawable(R.drawable.player1); - thumbDrawablePressed1 = getResources().getDrawable(R.drawable.player1_pressed); - thumbDrawable2 = getResources().getDrawable(R.drawable.player2); - thumbDrawablePressed2 = getResources().getDrawable(R.drawable.player2_pressed); + thumbDrawable1 = context.getResources().getDrawable(R.drawable.player1); + thumbDrawablePressed1 = context.getResources().getDrawable(R.drawable.player1_pressed); + thumbDrawable2 = context.getResources().getDrawable(R.drawable.player2); + thumbDrawablePressed2 = context.getResources().getDrawable(R.drawable.player2_pressed); innerPaint1.setColor(0xffb4e396); outerPaint1.setColor(0xff6ac453); innerPaint2.setColor(0xffd9e2eb); @@ -64,46 +54,52 @@ public class SeekBar extends View { thumbWidth = thumbDrawable1.getIntrinsicWidth(); thumbHeight = thumbDrawable1.getIntrinsicHeight(); } - - setOnTouchListener(new OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - float x = motionEvent.getX(); - float y = motionEvent.getY(); - if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - int additionWidth = (getMeasuredHeight() - thumbWidth) / 2; - if (thumbX - additionWidth <= x && x <= thumbX + thumbWidth + additionWidth && y >= 0 && y <= getMeasuredHeight()) { - pressed = true; - thumbDX = (int)(x - thumbX); - invalidate(); - getParent().requestDisallowInterceptTouchEvent(true); - return true; - } - } else if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { - if (pressed) { - pressed = false; - invalidate(); - return true; - } - } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) { - if (pressed) { - thumbX = (int)(x - thumbDX); - if (thumbX < 0) { - thumbX = 0; - } else if (thumbX > getMeasuredWidth() - thumbWidth) { - thumbX = getMeasuredWidth() - thumbWidth; - } - invalidate(); - return true; - } - } - return false; - } - }); } - @Override - protected void onDraw(Canvas canvas) { + public boolean onTouch(int action, float x, float y) { + if (action == MotionEvent.ACTION_DOWN) { + int additionWidth = (height - thumbWidth) / 2; + if (thumbX - additionWidth <= x && x <= thumbX + thumbWidth + additionWidth && y >= 0 && y <= height) { + pressed = true; + thumbDX = (int)(x - thumbX); + return true; + } + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (pressed) { + if (action == MotionEvent.ACTION_UP && delegate != null) { + delegate.onSeekBarDrag((float)thumbX / (float)(width - thumbWidth)); + } + pressed = false; + return true; + } + } else if (action == MotionEvent.ACTION_MOVE) { + if (pressed) { + thumbX = (int)(x - thumbDX); + if (thumbX < 0) { + thumbX = 0; + } else if (thumbX > width - thumbWidth) { + thumbX = width - thumbWidth; + } + return true; + } + } + return false; + } + + public void setProgress(float progress) { + thumbX = (int)Math.ceil((width - thumbWidth) * progress); + if (thumbX < 0) { + thumbX = 0; + } else if (thumbX > width - thumbWidth) { + thumbX = width - thumbWidth; + } + } + + public boolean isDragging() { + return pressed; + } + + public void draw(Canvas canvas) { Drawable thumb = null; Paint inner = null; Paint outer = null; @@ -124,8 +120,6 @@ public class SeekBar extends View { inner = innerPaint2; outer = outerPaint2; } - int height = getMeasuredHeight(); - int width = getMeasuredWidth(); int y = (height - thumbHeight) / 2; canvas.drawRect(thumbWidth / 2, height / 2 - Utilities.dp(1), width - thumbWidth / 2, height / 2 + Utilities.dp(1), inner); canvas.drawRect(thumbWidth / 2, height / 2 - Utilities.dp(1), thumbWidth / 2 + thumbX, height / 2 + Utilities.dp(1), outer); diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png new file mode 100755 index 000000000..7f5b68969 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png new file mode 100755 index 000000000..8963b7120 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png new file mode 100755 index 000000000..aad25e8ed Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png new file mode 100755 index 000000000..9bdc31ef1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audiocancel2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png new file mode 100755 index 000000000..1eecb03db Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audioload1.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png new file mode 100755 index 000000000..854fe27a3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audioload1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png new file mode 100755 index 000000000..9dc1787a7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audioload2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png new file mode 100755 index 000000000..feab040a6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/audioload2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel1.png new file mode 100755 index 000000000..4800fcb57 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel1.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel1_pressed.png new file mode 100755 index 000000000..4800fcb57 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel2.png new file mode 100755 index 000000000..eb5f9ae5c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel2.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel2_pressed.png new file mode 100755 index 000000000..c4aa51e28 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audiocancel2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audioload1.png b/TMessagesProj/src/main/res/drawable-ldpi/audioload1.png new file mode 100755 index 000000000..5eb0d5b89 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audioload1.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-ldpi/audioload1_pressed.png new file mode 100755 index 000000000..c62e90c54 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audioload1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audioload2.png b/TMessagesProj/src/main/res/drawable-ldpi/audioload2.png new file mode 100755 index 000000000..ad3f00e0c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audioload2.png differ diff --git a/TMessagesProj/src/main/res/drawable-ldpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-ldpi/audioload2_pressed.png new file mode 100755 index 000000000..a285a1588 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-ldpi/audioload2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png new file mode 100755 index 000000000..3ec699799 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png new file mode 100755 index 000000000..81b12fca1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png new file mode 100755 index 000000000..f626dd957 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png new file mode 100755 index 000000000..2129a26cf Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audiocancel2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png new file mode 100755 index 000000000..e06d062f2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audioload1.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png new file mode 100755 index 000000000..96774c41a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audioload1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png new file mode 100755 index 000000000..cad0562f4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audioload2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png new file mode 100755 index 000000000..b1c3c6c0c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/audioload2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_send.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_send.png index 9d5a7c907..64dd4c8e3 100755 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/ic_send.png and b/TMessagesProj/src/main/res/drawable-mdpi/ic_send.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png new file mode 100755 index 000000000..9a589b40c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png new file mode 100755 index 000000000..405882168 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png new file mode 100755 index 000000000..8af795283 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png new file mode 100755 index 000000000..b5ca6b6e4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audiocancel2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png new file mode 100755 index 000000000..48db9f061 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png new file mode 100755 index 000000000..48db9f061 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audioload1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png new file mode 100755 index 000000000..02dbe77b2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png new file mode 100755 index 000000000..434cc9d22 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/audioload2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png new file mode 100755 index 000000000..c7f705a60 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png new file mode 100755 index 000000000..ba16d325d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png new file mode 100755 index 000000000..ff6cb6814 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png new file mode 100755 index 000000000..cc9f56b11 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audiocancel2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png new file mode 100755 index 000000000..15b9c0217 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png new file mode 100755 index 000000000..3465e6e61 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload1_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png new file mode 100755 index 000000000..4699e159d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png new file mode 100755 index 000000000..c206870fd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/audioload2_pressed.png differ diff --git a/TMessagesProj/src/main/res/drawable/pause1_states.xml b/TMessagesProj/src/main/res/drawable/pause1_states.xml deleted file mode 100644 index 79b4a63da..000000000 --- a/TMessagesProj/src/main/res/drawable/pause1_states.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/pause2_states.xml b/TMessagesProj/src/main/res/drawable/pause2_states.xml deleted file mode 100644 index 741d3cb33..000000000 --- a/TMessagesProj/src/main/res/drawable/pause2_states.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/play1_states.xml b/TMessagesProj/src/main/res/drawable/play1_states.xml deleted file mode 100644 index cdb37c5f3..000000000 --- a/TMessagesProj/src/main/res/drawable/play1_states.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/play2_states.xml b/TMessagesProj/src/main/res/drawable/play2_states.xml deleted file mode 100644 index c53194ad6..000000000 --- a/TMessagesProj/src/main/res/drawable/play2_states.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_group_incoming_forward_layout.xml b/TMessagesProj/src/main/res/layout/chat_group_incoming_forward_layout.xml deleted file mode 100644 index cea511a64..000000000 --- a/TMessagesProj/src/main/res/layout/chat_group_incoming_forward_layout.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_group_incoming_text_layout.xml b/TMessagesProj/src/main/res/layout/chat_group_incoming_text_layout.xml deleted file mode 100644 index 08e39cd57..000000000 --- a/TMessagesProj/src/main/res/layout/chat_group_incoming_text_layout.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_incoming_forward_layout.xml b/TMessagesProj/src/main/res/layout/chat_incoming_forward_layout.xml deleted file mode 100644 index 7ef566b04..000000000 --- a/TMessagesProj/src/main/res/layout/chat_incoming_forward_layout.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_incoming_text_layout.xml b/TMessagesProj/src/main/res/layout/chat_incoming_text_layout.xml deleted file mode 100644 index 8e38644b9..000000000 --- a/TMessagesProj/src/main/res/layout/chat_incoming_text_layout.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_layout.xml b/TMessagesProj/src/main/res/layout/chat_layout.xml index 6f7cc65f6..8d61efec3 100644 --- a/TMessagesProj/src/main/res/layout/chat_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_layout.xml @@ -143,19 +143,6 @@ android:src="@drawable/send_button_states" android:background="@android:color/transparent"/> - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_outgoing_forward_layout.xml b/TMessagesProj/src/main/res/layout/chat_outgoing_forward_layout.xml deleted file mode 100644 index b673b56e1..000000000 --- a/TMessagesProj/src/main/res/layout/chat_outgoing_forward_layout.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_outgoing_text_layout.xml b/TMessagesProj/src/main/res/layout/chat_outgoing_text_layout.xml deleted file mode 100644 index 4861b6d63..000000000 --- a/TMessagesProj/src/main/res/layout/chat_outgoing_text_layout.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 6163a6968..a75c8e843 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -37,7 +37,7 @@ أمس لا توجد نتائج ...لا توجد محادثات بعد - إبدأ المراسلة بالضغط على \nأيقونة القلم في أعلى يمين الشاشة\nأو اذهب لقسم جهات الاتصال. + إبدأ المراسلة بالضغط على \nأيقونة النقاط في أعلى يمين الشاشة\nأو اذهب لقسم جهات الاتصال. في إنتظار الشبكة... جاري الاتصال... جاري التحديث... @@ -105,6 +105,7 @@ لقد تم إخراجك من هذه المجموعة لقد قمت بمغادرة المجموعة حذف المجموعة + قم بالسحب للإلغاء تم طلب محادثة سرية @@ -245,7 +246,7 @@ المجموعات المحادثات الأحداث - عندما يشترك صديق في تيليجرام + اشترك صديق في تيليجرام لا توجد وسائط بعد diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml new file mode 100644 index 000000000..2c9ec0b9f --- /dev/null +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -0,0 +1,354 @@ + + + + + + Telegram + + + Uw telefoon + Bevestig uw landcode\nen voer uw telefoonnummer in. + Kies een land + Verkeerde landcode + + + Uw code + We hebben een sms met een activatiecode naar uw telefoon verstuurd + We bellen u over + We bellen u… + Code + Verkeerd nummer? + + + Uw naam + Stel uw voor- en achternaam in + + Voornaam (verplicht) + Achternaam (optioneel) + Registratie annuleren + + + Gesprekken + Zoeken + Nieuwe berichten + Instellingen + Contacten + Nieuwe groep + gisteren + Geen resultaten + Nog geen gesprekken… + Start met een gesprek\ndoor op de Opstellen-knop rechtsboven te drukken\nof ga naar de contactenlijst. + Wachten op het netwerk… + Verbinden… + Updaten… + Nieuw privégesprek + Wacht op %s om online te komen… + Privégesprek geannuleerd + Encryptiesleutels uitwisselen… + %s neemt nu deel aan het privégesprek. + U neemt nu deel aan het privégesprek. + Verwijder gespreksgeschiedenis + Verwijder en sluit dit gesprek + Verborgen naam + Kies gesprek + + + Kies bestand + Vrij: %1$s van %2$s + Onbekende fout + Toegangsfout + Nog geen bestanden… + De bestandsgrootte mag niet groter zijn dan %1$s + Geen opslagmedium gevonden + USB-overdracht actief + Interne opslag + Externe opslag + Systeemroot + SD-kaart + + + onzichtbaar + aan het typen… + Bijlage + is aan het typen… + zijn aan het typen… + en nog %d mensen + Heeft u een vraag\nover Telegram? + Neem foto + Gallerij + Locatie + Video + Document + Nog geen berichten hier… + Bekijk foto + Bekijk locatie + Speel video + Doorgestuurd bericht + Van + Niets recents + leden + Bericht + Type bericht + Download + Geselecteerd: + DEEL MIJN CONTACTINFORMATIE + VOEG TOE AAN CONTACTEN + %s heeft u uitgenodigd voor een privégesprek. + U heeft %s uitgenodigd voor een privégesprek. + Privégesprekken: + Gebruik end-to-end versleuteling + Laat geen sporen achter op onze server + Gebruik een automatische zelfdestructie-timer + Doorsturen niet toestaan + %1$d nieuw bericht + %1$d nieuwe berichten + U bent uit deze groep gezet + U hebt deze groep verlaten + Verwijder deze groep + SCHUIF OM TE ANNULEREN + + + Privégesprek aangevraagd + Privégesprek geaccepteerd + %1$s heeft de zelfdestructie-timer ingesteld op %2$s + U heeft de zelfdestructie-timer ingesteld op %1$s + %1$s heeft de zelfdestructie-timer uitgeschakeld + U heeft de zelfdestructie-timer uitgeschakeld + 2 seconden + 5 seconden + 1 minuut + 1 uur + 1 dag + 1 week + U heeft een nieuw bericht + %1$s: %2$s + %1$s heeft u een bericht gestuurd + %1$s heeft u een foto gestuurd + %1$s heeft u een video gestuurd + %1$s heeft een contact met u gedeeld + %1$s heeft u een kaart gestuurd + %1$s heeft u een document gestuurd + %1$s heeft u een geluidsbestand gestuurd + %1$s @ %2$s: %3$s + %1$s heeft een bericht gestuurd naar de groep %2$s + %1$s heeft een foto gestuurd naar de groep %2$s + %1$s heeft een video gestuurd naar de groep %2$s + %1$s heeft een contact gedeeld met de groep %2$s + %1$s heeft een kaart gestuurd naar de groep %2$s + %1$s heeft een document gestuurd naar de groep %2$s + %1$s heeft een audiobestand gestuurd naar de groep %2$s + %1$s heeft u uitgenodigd voor de groep %2$s + %1$s heeft de naam van de groep %2$s aangepast + %1$s heeft de foto van de groep %2$s aangepast + %1$s nodigt %3$s uit voor de groep %2$s + %1$s zette %3$s uit de groep %2$s + %1$s heeft u uit de groep %2$s gezet + %1$s heeft de groep %2$s verlaten + %1$s heeft nu Telegram! + %1$s,\nWe hebben een nieuwe aanmelding op uw account gedetecteerd van een nieuw apparaat op 2$s\n\nApparaat: %3$s\nLocatie: %4$s\n\nAls u dit niet was, kunt u naar Instellingen - Alle andere sessies beëindigen.\n\nHet Telegram Team + %1$s heeft zijn profielfoto aangepast + + + Contactpersoon selecteren + Nog geen contactpersonen + Hey, zullen we overstappen op Telegram: http://telegram.org/dl2 + vandaag om + gisteren om + om + online + offline + laatst gezien: + laatst gezien: + Vrienden uitnodigen + + + Bericht sturen naar… + Groepsnaam invullen + LID + Groepsnaam + LEDEN + ALLE CONTACTPERSONEN + + + GROEPSNAAM INVULLEN + Gedeelde media + Groepsinformatie + GEDEELDE MEDIA + INSTELLINGEN + Lid toevoegen + Verwijderen en groep verlaten + Meldingen + Uit de groep zetten + + + Delen + Toevoegen + Blokkeren + Bewerken + Verwijderen + THUIS + MOBIEL + WERK + OVERIG + HOOFD + Contactgegevens + TELEFOON + Privégesprek starten + Er is een fout opgetreden. + Kan geen privégesprek starten met %1$s.\n\n%2$s gebruikt een oudere versie van Telegram en moet eerst een update installeren. + Privégesprek + Encryptiesleutel + Zelfdestructie-timer + Uit + 2s + 5s + 1m + 1u + 1d + 1w + Deze afbeelding is een weergave van de encryptiesleutel van het privégesprek met ]]>%1$s]]>.
    ]]>Als deze afbeelding er hetzelfde uitziet als op de telefoon van]]>%2$s]]>, dan is uw gesprek 200%% veilig.
    ]]>Lees meer op telegram.org
    + + + Alle meldingsinstellingen naar standaardinstellingen herstellen + Tekstgrootte berichten + Stel een vraag + Animaties inschakelen + Deblokkeren + Druk langdurig op een gebruiker om deze te deblokkeren. + Nog geen geblokkeerde gebruikers + UW TELEFOONNUMMER + BERICHTMELDINGEN + Melding + Bericht weergeven + GROEPSMELDINGEN + Geluid + IN-APP MELDINGEN + In-App geluiden + In-App trillen + Trillen + In-App weergave + HERSTELLEN + Alle meldingen herstellen + Maak alle aangepaste instellingen voor meldingen ongedaan voor alle contacten en groepen + Meldingen en geluiden + Geblokkeerde gebruikers + Inkomende foto\’s bewaren + Uitloggen + UW VOOR- EN ACHTERNAAM + Geen geluid + Standaard + ONDERSTEUNING + Gespreksachtergrond + BERICHTEN + Versturen met Enter + Alle andere sessies beëindigen + AUTOMATISCH FOTO\’S DOWNLOADEN + Groepen + Privégesprekken + GEBEURTENISSEN + Contact aangemeld bij Telegram + + + Nog geen media gedeeld + Downloaden annuleren + + + Mijn locatie + Kaart + Luchtfoto + Hybride + m verderop + km verderop + Locatie verzenden + Locatie delen + + + Alle media tonen + Naar galerij opslaan + van + Galerij + + + Volgende + Vorige + Klaar + Open + Annuleren + Toevoegen + Bewerken + Versturen + Bellen + Kopiëren + Verwijderen + Doorsturen + Opnieuw proberen + Van camera + Van galerij + Foto verwijderen + Foto openen + Instellen + OK + + + un1 heeft un2 verwijderd + un1 heeft de groep verlaten + un1 heeft un2 toegevoegd + un1 heeft de groepsfoto verwijderd + un1 heeft de groepsfoto veranderd + un1 heeft de groepsnaam veranderd naar un2 + un1 heeft de groep gemaakt + U hebt un2 verwijderd + U hebt de groep verlaten + U hebt un2 toegevoegd + U heeft de groepsafbeelding verwijderd + U heeft de groepsafbeelding veranderd + U heeft de groepsnaam veranderd in un2 + U heeft de groep gemaakt + un1 heeft u verwijderd + un1 heeft u toegevoegd + Dit bericht wordt niet ondersteund op uw versie van Telegram. + Foto + Video + Locatie + Contactpersoon + Document + Geluid + U + + + Ongeldig telefoonnummer + Code verlopen, log opnieuw in + Te veel pogingen, probeer het later nogmaals + Ongeldige code + Ongeldige voornaam + Ongeldige achternaam + Laden… + U heeft geen videospeler, installeer een videospeler om verder te gaan + U heeft geen enkele applicatie die MIME type \‘%1$s\’ kan verwerken, installeer hier een applicatie voor om verder te gaan + Deze gebruiker heeft nog geen Telegram, uitnodiging sturen? + Weet u het zeker? + Contactpersoon toevoegen? + %1$s aan de groep toevoegen? + Berichten doorsturen naar %1$s? + Deze chat verwijderen? + + + Telegram + Snel + Gratis + Veilig + Krachtig + In de cloud + Privé + Welkom in het tijdperk van snel en veilig chatten + Telegram]]> bezorgt berichten sneller]]>dan elke andere app + Telegram]]> is altijd gratis. Geen advertenties.]]>Geen abonnementskosten + Telegram]]> beveiligt uw berichten]]>tegen hackers + Telegram]]> heeft geen beperkingen op de grootte]]>van uw media en gesprekken + Telegram]]> biedt toegang tot uw berichten]]>vanaf meerdere apparaten + Telegram]]> berichten zijn sterk versleuteld]]>en kunnen zichzelf vernietigen + Start met chatten + + + CACHE_TAG +
    \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 352603dfd..7fe96902b 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -1,6 +1,6 @@ - + Telegram @@ -95,7 +95,7 @@ AÑADIR A CONTACTOS %s te ha invitado a un chat secreto. Has invitado a %s a un chat secreto. - Los Chats Secretos: + Los chats secretos: Usan cifrado de móvil-a-móvil No dejan rastro en nuestros servidores Tienen autodestrucción de mensajes @@ -105,6 +105,7 @@ Has sido expulsado de este grupo Has abandonado este grupo Eliminar este grupo + DESLIZA PARA CANCELAR Chat secreto solicitado @@ -150,7 +151,7 @@ Seleccionar contacto No hay contactos aún - Hey, cambiémonos a Telegram: http://telegram.org/dl2 + Oye, cambiémonos a Telegram: http://telegram.org/dl2 hoy a las ayer a las a las @@ -291,7 +292,7 @@ un1 expulsó a un2 un1 dejó el grupo - un1 agregó a un2 + un1 añadió a un2 un1 eliminó la foto del grupo un1 cambió la foto del grupo un1 cambió el nombre del grupo a un2 diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 3b850b794..612f1feee 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -105,6 +105,7 @@ You were kicked from this group You left this group Delete this Group + SLIDE TO CANCEL Secret chat requested