SQLite

Check-in [709841f8]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:When doing a text-affinity comparison between two values where one or both have both a text and a numeric type, make sure the numeric type does not confuse the answer. This is a deeper fix to the problem observed by forum pose 3776b48e71. The problem bisects to [25f2246be404f38b] on 2014-08-24, prior to version 3.8.7.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 709841f88c77276f09701bf38e25503c64b3a0afbe2fbf878136db12f31cbe21
User & Date: drh 2024-01-20 15:13:13
Context
2024-03-06
20:54
When doing a text-affinity comparison between two values where one or both have both a text and a numeric type, make sure the numeric type does not confuse the answer. This is a deeper fix to the problem observed by forum post 3776b48e71. The problem bisects to [25f2246be404f38b] on 2014-08-24, prior to version 3.8.7. (check-in: 6d385ccd user: mistachkin tags: branch-3.45)
2024-01-20
16:29
Rig sqlite3_serialize() so that it will initialize a previously uninitialized database prior to serializing it, so that it does not have a zero-byte size and does not return NULL (except for OOM). Forum thread 498777780e16880a. (check-in: e638d5e4 user: drh tags: trunk)
16:18
Allow "_" characters to appear following any digit in an integer or real SQL literal. (check-in: 401650aa user: dan tags: digit-separators)
15:13
When doing a text-affinity comparison between two values where one or both have both a text and a numeric type, make sure the numeric type does not confuse the answer. This is a deeper fix to the problem observed by forum pose 3776b48e71. The problem bisects to [25f2246be404f38b] on 2014-08-24, prior to version 3.8.7. (check-in: 709841f8 user: drh tags: trunk)
13:18
Ensure that the replace() SQL function always returns a TEXT value even when its first argument is numeric and its second argument is an empty string. Fix for the issue reported by forum post 3776b48e71. (check-in: 01868ebc user: drh tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/misc/noop.c.

33
34
35
36
37
38
39


















40
41
42
43
44
45
46
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  assert( argc==1 );
  sqlite3_result_value(context, argv[0]);
}



















#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_noop_init(
  sqlite3 *db, 
  char **pzErrMsg, 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  assert( argc==1 );
  sqlite3_result_value(context, argv[0]);
}

/*
** Implementation of the multitype_text() function.
**
** The function returns its argument.  The result will always have a
** TEXT value.  But if the original input is numeric, it will also
** have that numeric value.
*/
static void multitypeTextFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  assert( argc==1 );
  (void)argc;
  (void)sqlite3_value_text(argv[0]);
  sqlite3_result_value(context, argv[0]);
}

#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_noop_init(
  sqlite3 *db, 
  char **pzErrMsg, 
60
61
62
63
64
65
66




67
68
  rc = sqlite3_create_function(db, "noop_do", 1,
                     SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_DIRECTONLY,
                     0, noopfunc, 0, 0);
  if( rc ) return rc;
  rc = sqlite3_create_function(db, "noop_nd", 1,
                     SQLITE_UTF8,
                     0, noopfunc, 0, 0);




  return rc;
}







>
>
>
>


78
79
80
81
82
83
84
85
86
87
88
89
90
  rc = sqlite3_create_function(db, "noop_do", 1,
                     SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_DIRECTONLY,
                     0, noopfunc, 0, 0);
  if( rc ) return rc;
  rc = sqlite3_create_function(db, "noop_nd", 1,
                     SQLITE_UTF8,
                     0, noopfunc, 0, 0);
  if( rc ) return rc;
  rc = sqlite3_create_function(db, "multitype_text", 1,
                     SQLITE_UTF8,
                     0, multitypeTextFunc, 0, 0);
  return rc;
}

Changes to src/test1.c.

986
987
988
989
990
991
992

































993
994
995
996
997
998
999
  int argc,  
  sqlite3_value **argv
){
  sqlite3_int64 v = sqlite3_value_int64(argv[0]);
  sqlite3_result_int64(context, v);
  sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, context);
}


































/*
** SQL function:  strtod(X)
**
** Use the C-library strtod() function to convert string X into a double.
** Used for comparing the accuracy of SQLite's internal text-to-float conversion
** routines against the C-library.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
  int argc,  
  sqlite3_value **argv
){
  sqlite3_int64 v = sqlite3_value_int64(argv[0]);
  sqlite3_result_int64(context, v);
  sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, context);
}

/*
** These SQL functions attempt to return a value (their first argument)
** that has been modified to have multiple datatypes.  For example both
** TEXT and INTEGER.
*/
static void addTextTypeFunction(
  sqlite3_context *context, 
  int argc,  
  sqlite3_value **argv
){
  (void)sqlite3_value_text(argv[0]);
  (void)argc;
  sqlite3_result_value(context, argv[0]);
}
static void addIntTypeFunction(
  sqlite3_context *context, 
  int argc,  
  sqlite3_value **argv
){
  (void)sqlite3_value_int64(argv[0]);
  (void)argc;
  sqlite3_result_value(context, argv[0]);
}
static void addRealTypeFunction(
  sqlite3_context *context, 
  int argc,  
  sqlite3_value **argv
){
  (void)sqlite3_value_double(argv[0]);
  (void)argc;
  sqlite3_result_value(context, argv[0]);
}

/*
** SQL function:  strtod(X)
**
** Use the C-library strtod() function to convert string X into a double.
** Used for comparing the accuracy of SQLite's internal text-to-float conversion
** routines against the C-library.
1098
1099
1100
1101
1102
1103
1104
















1105
1106
1107
1108
1109
1110
1111
  /* The intreal() function converts its argument to an integer and returns
  ** it as a MEM_IntReal.
  */
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "intreal", 1, SQLITE_UTF8,
          0, intrealFunction, 0, 0);
  }

















  /* Functions strtod() and dtostr() work as in the shell.  These routines
  ** use the standard C library to convert between floating point and
  ** text.  This is used to compare SQLite's internal conversion routines
  ** against the standard library conversion routines.
  **
  ** Both routines copy/pasted from the shell.c.in implementation







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
  /* The intreal() function converts its argument to an integer and returns
  ** it as a MEM_IntReal.
  */
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "intreal", 1, SQLITE_UTF8,
          0, intrealFunction, 0, 0);
  }

  /* The add_text_type(), add_int_type(), and add_real_type() functions
  ** attempt to return a value that has multiple datatypes.
  */
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "add_text_type", 1, SQLITE_UTF8,
          0, addTextTypeFunction, 0, 0);
  }
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "add_int_type", 1, SQLITE_UTF8,
          0, addIntTypeFunction, 0, 0);
  }
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "add_real_type", 1, SQLITE_UTF8,
          0, addRealTypeFunction, 0, 0);
  }

  /* Functions strtod() and dtostr() work as in the shell.  These routines
  ** use the standard C library to convert between floating point and
  ** text.  This is used to compare SQLite's internal conversion routines
  ** against the standard library conversion routines.
  **
  ** Both routines copy/pasted from the shell.c.in implementation

Changes to src/vdbe.c.

2297
2298
2299
2300
2301
2302
2303


2304
2305
2306
2307
2308
2309
2310
2311
2312


2313
2314
2315
2316
2317
2318
2319
2320
          flags3 = pIn3->flags;
        }
        if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
          applyNumericAffinity(pIn3,0);
        }
      }
    }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){


      if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
        testcase( pIn1->flags & MEM_Int );
        testcase( pIn1->flags & MEM_Real );
        testcase( pIn1->flags & MEM_IntReal );
        sqlite3VdbeMemStringify(pIn1, encoding, 1);
        testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
        flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
        if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
      }


      if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
        testcase( pIn3->flags & MEM_Int );
        testcase( pIn3->flags & MEM_Real );
        testcase( pIn3->flags & MEM_IntReal );
        sqlite3VdbeMemStringify(pIn3, encoding, 1);
        testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) );
        flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask);
      }







>
>
|








>
>
|







2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
          flags3 = pIn3->flags;
        }
        if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
          applyNumericAffinity(pIn3,0);
        }
      }
    }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
      if( (flags1 & MEM_Str)!=0 ){
        pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
      }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
        testcase( pIn1->flags & MEM_Int );
        testcase( pIn1->flags & MEM_Real );
        testcase( pIn1->flags & MEM_IntReal );
        sqlite3VdbeMemStringify(pIn1, encoding, 1);
        testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
        flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
        if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
      }
      if( (flags3 & MEM_Str)!=0 ){
        pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
      }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
        testcase( pIn3->flags & MEM_Int );
        testcase( pIn3->flags & MEM_Real );
        testcase( pIn3->flags & MEM_IntReal );
        sqlite3VdbeMemStringify(pIn3, encoding, 1);
        testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) );
        flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask);
      }

Changes to test/types3.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus
# of this file is testing the interaction of SQLite manifest types
# with Tcl dual-representations.
#
# $Id: types3.test,v 1.8 2008/04/28 13:02:58 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# A variable with only a string representation comes in as TEXT
do_test types3-1.1 {
  set V {}







<
<







8
9
10
11
12
13
14


15
16
17
18
19
20
21
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus
# of this file is testing the interaction of SQLite manifest types
# with Tcl dual-representations.
#



set testdir [file dirname $argv0]
source $testdir/tester.tcl

# A variable with only a string representation comes in as TEXT
do_test types3-1.1 {
  set V {}
91
92
93
94
95
96
97
98




























99
  set V [db one {SELECT '1234567890123456.0'}]
  tcl_variable_type V
} {}
do_test types3-2.6 {
  set V [db one {SELECT NULL}]
  tcl_variable_type V
} {}





























finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  set V [db one {SELECT '1234567890123456.0'}]
  tcl_variable_type V
} {}
do_test types3-2.6 {
  set V [db one {SELECT NULL}]
  tcl_variable_type V
} {}

# See https://sqlite.org/forum/forumpost/3776b48e71
#
# On a text-affinity comparison of two values where one of
# the values has both MEM_Str and a numeric type like MEM_Int,
# make sure that only the MEM_Str representation is used.
#
sqlite3_create_function db
do_execsql_test types3-3.1 {
  DROP TABLE IF EXISTS t1;
  CREATE TABLE t1(x TEXT PRIMARY KEY);
  INSERT INTO t1 VALUES('1');
  SELECT * FROM t1 WHERE NOT x=upper(1);
} {}
do_execsql_test types3-3.2 {
  SELECT * FROM t1 WHERE NOT x=add_text_type(1);
} {}
do_execsql_test types3-3.3 {
  SELECT * FROM t1 WHERE NOT x=add_int_type('1');
} {}
do_execsql_test types3-3.4 {
  DELETE FROM t1;
  INSERT INTO t1 VALUES(1.25);
  SELECT * FROM t1 WHERE NOT x=add_real_type('1.25');
} {}
do_execsql_test types3-3.5 {
  SELECT * FROM t1 WHERE NOT x=add_text_type(1.25);
} {}

finish_test