Create a new affinity called FLEXNUM that works like NUMERIC except that it

never tries to convert integer to real or real to integer.  The affinity is
only used internally - it is not possible to create a table column with this
affinity.  This affinity is used on subqueries and views that are built off
of a compound SELECT and where the datatype is controlled by a CAST expression.
dbsqlfuzz c9ee6f9a0a8b8fefb02cf69de2a8b67ca39525c8

FossilOrigin-Name: 44135d6ea84f7ba6b36549954b38a8bc048d5ffea5a9779e35950afa4eb2dfb2
This commit is contained in:
drh 2022-12-15 20:03:08 +00:00
parent 118b53bd21
commit 00d6b2755f
8 changed files with 75 additions and 42 deletions

View File

@ -1,5 +1,5 @@
C Fix\san\sincompatibility\sbetween\sthe\sTcl\sinterface\sand\sthe\s"memdb"\svfs\sby\sallowing\smemdb\sto\saccept\sfilenames\sthat\sbegin\swith\s'\\'\scharacters.
D 2022-12-15T18:56:12.441
C Create\sa\snew\saffinity\scalled\sFLEXNUM\sthat\sworks\slike\sNUMERIC\sexcept\sthat\sit\nnever\stries\sto\sconvert\sinteger\sto\sreal\sor\sreal\sto\sinteger.\s\sThe\saffinity\sis\nonly\sused\sinternally\s-\sit\sis\snot\spossible\sto\screate\sa\stable\scolumn\swith\sthis\naffinity.\s\sThis\saffinity\sis\sused\son\ssubqueries\sand\sviews\sthat\sare\sbuilt\soff\nof\sa\scompound\sSELECT\sand\swhere\sthe\sdatatype\sis\scontrolled\sby\sa\sCAST\sexpression.\ndbsqlfuzz\sc9ee6f9a0a8b8fefb02cf69de2a8b67ca39525c8
D 2022-12-15T20:03:08.225
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -587,7 +587,7 @@ F src/btmutex.c 6ffb0a22c19e2f9110be0964d0731d2ef1c67b5f7fabfbaeb7b9dabc4b7740ca
F src/btree.c 2f794c217e52fdf4322bf37ee7778331b4d93aed2c00b5d67f914c0239a9edcc
F src/btree.h 49da925329574798be3cbb745a49d069a9e67c99900d8a0d04b1e934d60394ea
F src/btreeInt.h 88ad499c92b489afedbfefc3f067c4d15023ec021afe622db240dc9d2277cfa5
F src/build.c 51b46b657d914877dc64d0f66e2d82021b6ff622f43287cebc105efee0165600
F src/build.c ea069a5655797f174403ee6f32c532e69ddcf4031bc0e65cca4863cb28cba603
F src/callback.c 4cd7225b26a97f7de5fee5ae10464bed5a78f2adefe19534cc2095b3a8ca484a
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 20507cc0b0a6c19cd882fcd0eaeda32ae6a4229fb4b024cfdf3183043d9b703d
@ -595,7 +595,7 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5
F src/dbpage.c f1a87f4ebcf22284e0aaf0697862f4ccfc120dcd6db3d8dfa3b049b2580c01d8
F src/dbstat.c a56a7ad1163a9888d46cd5820be2e65354fb1aa04ed6909f7c3e5831e0ee2c29
F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e
F src/expr.c 63e0b77a5444c75908fb1d2db9b15248c3b31b4fd5ea38f1a45a2b4ac2f14cf8
F src/expr.c 6dc067ab8244627d62d0d81bd9d9cc78173325cf2fe3f494f0a65a418ce2b9f8
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002
F src/func.c 7e86074afc4dc702691a29b7801f6dcc191db092b52e8bbe69dcd2f7be52194d
@ -645,12 +645,12 @@ F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c a6c97f07b148ebf2e47c973b3ed292b43950da5bccec4a98f38ab7db6d6a0283
F src/select.c c4f845e116a425c71b8115b7ffc221237cb59569de3253134cbd570b7e04cf0f
F src/shell.c.in 47d491325ea7e01d9d4b7f6166a83fbfc6729b3d8a546d857d627a58a5d46d73
F src/sqlite.h.in e752f82b9d71f1d42b259b1900e4b1caf0965e844d756cd5cc91cc2cf45ed925
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
F src/sqliteInt.h 58dac6ab1352193715dc7de71cf900cafce6c3064a7331dc8b2029a6165a2b92
F src/sqliteInt.h 4bcf6f13b8195de4fd020cd58ebb605a27752b3d82d0d5071d4435a446f00487
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@ -717,7 +717,7 @@ F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c 313f3154e2b85a447326f5dd15de8d31a4df6ab0c3579bd58f426ff634ec9050
F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd
F src/vdbe.c 705df5b079978ea98ae18db8ffcefbdf529f7465e9c8fa19b022530556fc68e1
F src/vdbe.c c1a65b1bb9e2fdb8831cfb4a7ff4ef3088647dd5f096bcedd5231c03181459f4
F src/vdbe.h 73b904a6b3bb27f308c6cc287a5751ebc7f1f89456be0ed068a12b92844c6e8c
F src/vdbeInt.h 8651e4c4e04d1860d0bdcf330cb8294e3778a9d4222be30ce4c490d9220af783
F src/vdbeapi.c df3f73a4d0a487f2068e3c84776cd6e3fba5ae80ff612659dcfda4307686420b
@ -863,7 +863,7 @@ F test/capi3c.test 54e2dc0c8fd7c34ad1590d1be6864397da2438c95a9f5aee2f8fbc60c112e
F test/capi3d.test 8b778794af891b0dca3d900bd345fbc8ebd2aa2aae425a9dccdd10d5233dfbde
F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
F test/carray01.test d55d57bf66b1af1c7ac55fae66ff4910884a8f5d21a90a18797ce386212a2634
F test/cast.test 46a5963a216c2b14220557b8636b968d6de9d3292d79616becbf7109ca00e1ad
F test/cast.test e3a7e452f37efec0df0a89e55aa2f04861ba6613deb16075101414668bf4bb24
F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
F test/changes.test 9dd8e597d84072122fc8a4fcdea837f4a54a461e6e536053ea984303e8ca937b
F test/changes2.test d222c0cbf5ab0ac4d7c180594e486c1bf20b2098d33e56ce33b8e12eba6823b9
@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P eb27feecea508f3491a09699f2339951facd2345d479cfd3020183dc2af703b2
R d9029a8d7af89f7bcaaa52e77671d993
U dan
Z 711e972efe20ba3f8cde77248740eec0
P bd537f2057a4800bd30e7dd57405c3e57df649471104c80bd32573a89568029e
R 37c0f0e978d6f512d45ea1c89699be07
U drh
Z f6d3f39ad91532557d8a7968a4b1fd8b
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
bd537f2057a4800bd30e7dd57405c3e57df649471104c80bd32573a89568029e
44135d6ea84f7ba6b36549954b38a8bc048d5ffea5a9779e35950afa4eb2dfb2

View File

@ -2140,7 +2140,8 @@ static char *createTableStmt(sqlite3 *db, Table *p){
/* SQLITE_AFF_TEXT */ " TEXT",
/* SQLITE_AFF_NUMERIC */ " NUM",
/* SQLITE_AFF_INTEGER */ " INT",
/* SQLITE_AFF_REAL */ " REAL"
/* SQLITE_AFF_REAL */ " REAL",
/* SQLITE_AFF_FLEXNUM */ " NUM",
};
int len;
const char *zType;
@ -2156,10 +2157,12 @@ static char *createTableStmt(sqlite3 *db, Table *p){
testcase( pCol->affinity==SQLITE_AFF_NUMERIC );
testcase( pCol->affinity==SQLITE_AFF_INTEGER );
testcase( pCol->affinity==SQLITE_AFF_REAL );
testcase( pCol->affinity==SQLITE_AFF_FLEXNUM );
zType = azType[pCol->affinity - SQLITE_AFF_BLOB];
len = sqlite3Strlen30(zType);
assert( pCol->affinity==SQLITE_AFF_BLOB
assert( pCol->affinity==SQLITE_AFF_BLOB
|| pCol->affinity==SQLITE_AFF_FLEXNUM
|| pCol->affinity==sqlite3AffinityType(zType, 0) );
memcpy(&zStmt[k], zType, len);
k += len;

View File

@ -4250,7 +4250,7 @@ expr_code_doover:
assert( pExpr->y.pTab!=0 );
aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
if( aff>SQLITE_AFF_BLOB ){
static const char zAff[] = "B\000C\000D\000E";
static const char zAff[] = "B\000C\000D\000E\000F";
assert( SQLITE_AFF_BLOB=='A' );
assert( SQLITE_AFF_TEXT=='B' );
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,

View File

@ -2288,14 +2288,6 @@ int sqlite3ColumnsFromExprList(
return SQLITE_OK;
}
/*
** This bit, when added to the "aff" parameter of
** sqlite3ColumnTypeOfSubquery() means that result set
** expressions of the form "CAST(expr AS NUMERIC)" should result in
** NONE affinity rather than NUMERIC affinity.
*/
#define SQLITE_AFF_FLAG1 0x10
/*
** pTab is a transient Table object that represents a subquery of some
** kind (maybe a parenthesized subquery in the FROM clause of a larger
@ -2306,17 +2298,12 @@ int sqlite3ColumnsFromExprList(
** * The datatype name, as it might appear in a CREATE TABLE statement
** * Which collating sequence to use for the column
** * The affinity of the column
**
** The SQLITE_AFF_FLAG1 bit added to parameter aff means that a
** result set column of the form "CAST(expr AS NUMERIC)" should use
** NONE affinity rather than NUMERIC affinity. See the
** 2022-12-10 "reopen" of ticket https://sqlite.org/src/tktview/57c47526c3.
*/
void sqlite3SubqueryColumnTypes(
Parse *pParse, /* Parsing contexts */
Table *pTab, /* Add column type information to this table */
Select *pSelect, /* SELECT used to determine types and collations */
char aff /* Default affinity. Maybe with SQLITE_AFF_FLAG1 too */
char aff /* Default affinity. */
){
sqlite3 *db = pParse->db;
Column *pCol;
@ -2328,6 +2315,7 @@ void sqlite3SubqueryColumnTypes(
assert( pSelect!=0 );
assert( (pSelect->selFlags & SF_Resolved)!=0 );
assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
if( db->mallocFailed ) return;
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
a = pSelect->pEList->a;
@ -2339,13 +2327,9 @@ void sqlite3SubqueryColumnTypes(
/* pCol->szEst = ... // Column size est for SELECT tables never used */
pCol->affinity = sqlite3ExprAffinity(p);
if( pCol->affinity<=SQLITE_AFF_NONE ){
assert( (SQLITE_AFF_FLAG1 & SQLITE_AFF_MASK)==0 );
pCol->affinity = aff & SQLITE_AFF_MASK;
}
if( aff & SQLITE_AFF_FLAG1 ){
if( pCol->affinity==SQLITE_AFF_NUMERIC && p->op==TK_CAST ){
pCol->affinity = SQLITE_AFF_NONE;
}
pCol->affinity = aff;
}else if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){
pCol->affinity = SQLITE_AFF_FLEXNUM;
}
if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){
int m = 0;
@ -2360,7 +2344,9 @@ void sqlite3SubqueryColumnTypes(
pCol->affinity = SQLITE_AFF_BLOB;
}
}
if( pCol->affinity==SQLITE_AFF_NUMERIC ){
if( pCol->affinity==SQLITE_AFF_NUMERIC
|| pCol->affinity==SQLITE_AFF_FLEXNUM
){
zType = "NUM";
}else{
zType = 0;
@ -6254,8 +6240,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
/* A sub-query in the FROM clause of a SELECT */
Select *pSel = pFrom->pSelect;
if( pSel ){
sqlite3SubqueryColumnTypes(pParse, pTab, pSel,
SQLITE_AFF_NONE|SQLITE_AFF_FLAG1);
sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE);
}
}
}

View File

@ -2239,6 +2239,7 @@ struct CollSeq {
#define SQLITE_AFF_NUMERIC 0x43 /* 'C' */
#define SQLITE_AFF_INTEGER 0x44 /* 'D' */
#define SQLITE_AFF_REAL 0x45 /* 'E' */
#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)

View File

@ -371,6 +371,10 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){
** always preferred, even if the affinity is REAL, because
** an integer representation is more space efficient on disk.
**
** SQLITE_AFF_FLEXNUM:
** If the value is text, then try to convert it into a number of
** some kind (integer or real) but do not make any other changes.
**
** SQLITE_AFF_TEXT:
** Convert pRec to a text representation.
**
@ -385,11 +389,11 @@ static void applyAffinity(
){
if( affinity>=SQLITE_AFF_NUMERIC ){
assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
|| affinity==SQLITE_AFF_NUMERIC );
|| affinity==SQLITE_AFF_NUMERIC || affinity==SQLITE_AFF_FLEXNUM );
if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/
if( (pRec->flags & MEM_Real)==0 ){
if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1);
}else{
}else if( affinity<=SQLITE_AFF_REAL ){
sqlite3VdbeIntegerAffinity(pRec);
}
}

View File

@ -515,4 +515,44 @@ do_execsql_test cast-9.13 {
SELECT x, typeof(x) FROM dual CROSS JOIN (SELECT CAST(4.5 AS NUMERIC) AS x);
} {4.5 real}
# 2022-12-15 dbsqlfuzz c9ee6f9a0a8b8fefb02cf69de2a8b67ca39525c8
#
# Added a new SQLITE_AFF_FLEXNUM that does not try to convert int to real or
# real to int.
#
do_execsql_test cast-10.1 {
VALUES(CAST(44 AS REAL)),(55);
} {44.0 55}
do_execsql_test cast-10.2 {
SELECT CAST(44 AS REAL) AS 'm' UNION ALL SELECT 55;
} {44.0 55}
do_execsql_test cast-10.3 {
SELECT * FROM (VALUES(CAST(44 AS REAL)),(55));
} {44.0 55}
do_execsql_test cast-10.4 {
SELECT * FROM (SELECT CAST(44 AS REAL) AS 'm' UNION ALL SELECT 55);
} {44.0 55}
do_execsql_test cast-10.5 {
SELECT * FROM dual CROSS JOIN (VALUES(CAST(44 AS REAL)),(55));
} {X 44.0 X 55}
do_execsql_test cast-10.6 {
SELECT * FROM dual CROSS JOIN (SELECT CAST(44 AS REAL) AS 'm'
UNION ALL SELECT 55);
} {X 44.0 X 55}
do_execsql_test cast-10.7 {
DROP VIEW v1;
CREATE VIEW v1 AS SELECT CAST(44 AS REAL) AS 'm' UNION ALL SELECT 55;
SELECT name, type FROM pragma_table_info('v1');
} {m NUM}
do_execsql_test cast-10.8 {
CREATE VIEW v2 AS VALUES(CAST(44 AS REAL)),(55);
SELECT type FROM pragma_table_info('v2');
} {NUM}
do_execsql_test cast-10.9 {
SELECT * FROM v1;
} {44.0 55}
do_execsql_test cast-10.10 {
SELECT * FROM v2;
} {44.0 55}
finish_test