SQLite4
Check-in [8ede88c1df]
Not logged in

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

Overview
Comment:Fold in Peter Reid's fixes and enhancements to the sqlite4_num object.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8ede88c1df96790b39ab47b79bc83b7cbbb21898
User & Date: drh 2013-02-14 15:32:53
Context
2013-02-15
19:20
Fix a crash bug found by TH4. check-in: 6453240a0f user: drh tags: trunk
2013-02-14
15:32
Fold in Peter Reid's fixes and enhancements to the sqlite4_num object. check-in: 8ede88c1df user: drh tags: trunk
01:51
Fix sqlite4_num_to_text when formatting an integer with a negative exponent. It was leaving an uninitialized byte in the destination string. Leaf check-in: 3cac6cdb86 user: peterreid tags: num_work
2013-02-13
12:28
Change the API for binding strings and blobs so that the destructor now carries an extra argument which can be used as the sqlite4_env pointer, thus allowing functions like sqlite4_free() to be used as a string or blob destructor. check-in: 56335097b1 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/math.c.

211
212
213
214
215
216
217














218
219
220
221
222
223
224
...
237
238
239
240
241
242
243

244
245
246
247
248
249
250
...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376



377
378

379
380
381
382
383
384
385


















386
387
388
389
390
391
392
...
465
466
467
468
469
470
471
472
473



474
475
476
477
478
479
480
  r.sign = A.sign ^ B.sign;
  r.approx = A.approx | B.approx;
  if( r.approx==0 && A.m%B.m!=0 ) r.approx = 1;
  r.m = A.m/B.m;
  r.e = A.e - B.e;
  return r;
}















/*
** Compare numbers A and B.  Return:
**
**    1     if A<B
**    2     if A==B
**    3     if A>B
................................................................................
    return A.sign ? 1 : 3;
  }
  if( B.e>SQLITE4_MX_EXP ){
    if( B.m==0 ) return 0;
    return B.sign ? 3 : 1;
  }
  if( A.sign!=B.sign ){

    return A.sign ? 1 : 3;
  }
  adjustExponent(&A, &B);
  if( A.sign ){
    sqlite4_num t = A;
    A = B;
    B = t;
................................................................................
    i = incr;
  }else if( zIn[0]=='+' ){
    i = incr;
  }else{
    i = 0;
  }
  if( nIn<=0 ) goto not_a_valid_number;
  if( nIn>=incr*2
   && ((c=zIn[i])=='i' || c=='I')
   && ((c=zIn[i+incr])=='n' || c=='N')
   && ((c=zIn[i+incr*2])=='f' || c=='F')
  ){
    r.e = SQLITE4_MX_EXP+1;
    r.m = nIn<=i+incr*3 || zIn[i+incr*3]==0;
    return r;
  }
  while( i<nIn && (c = zIn[i])!=0 ){
    i += incr;
    if( c>='0' && c<='9' ){
      if( c==0 && nDigit==0 ){
        if( seenRadix && r.e > -(SQLITE4_MX_EXP+1000) ) r.e--;
        continue;
      }
      nDigit++;
      if( nDigit<=18 ){
        r.m = (r.m*10) + c - '0';
        if( seenRadix ) r.e--;
      }else{
        if( c!='0' ) r.approx = 1;
        if( !seenRadix ) r.e++;
      }
    }else if( c=='.' ){
      seenRadix = 1;
    }else{
      break;
    }
  }
  if( c=='e' || c=='E' ){
    int exp = 0;
    int expsign = 0;
    int nEDigit = 0;
    if( zIn[i]=='-' ){
      expsign = 1;
      i += incr;
    }else if( zIn[i]=='+' ){
      i += incr;
    }
    if( i>=nIn ) goto not_a_valid_number;
    while( i<nIn && (c = zIn[i])!=0  ){
      i += incr;
      if( c<'0' || c>'9' ) break;
      if( c=='0' && nEDigit==0 ) continue;
      nEDigit++;
      if( nEDigit>3 ) goto not_a_valid_number;
      exp = exp*10 + c - '0';
    }
    if( expsign ) exp = -exp;
    r.e += exp;



  }
  if( c!=0 ) goto not_a_valid_number;

  return r;
  
not_a_valid_number:
  r.e = SQLITE4_MX_EXP+1;
  r.m = 0;
  return r;  
}



















/*
** Convert an integer into text in the buffer supplied. The
** text is zero-terminated and right-justified in the buffer.
** A pointer to the first character of text is returned.
**
** The buffer needs to be at least 21 bytes in length.
................................................................................
    zNum += m;
    n -= m;
    removeTrailingZeros(zNum, &n);
    if( n>0 ){
      zOut[0] = '.';
      memcpy(zOut+1, zNum, n);
      nOut += n;
    }
    zOut[n+1] = 0;



    return nOut;
  }
  if( x.e<0 && x.e >= -n-5 ){
    /* Values less than 1 and with no more than 5 subsequent zeros prior
    ** to the first significant digit.  Ex:  0.0000012345 */
    int j = -(n + x.e);
    memcpy(zOut, "0.", 2);







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







 







>







 







|











|













<
<
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
|
<
>







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







 







<
|
>
>
>







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366




367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
...
497
498
499
500
501
502
503

504
505
506
507
508
509
510
511
512
513
514
  r.sign = A.sign ^ B.sign;
  r.approx = A.approx | B.approx;
  if( r.approx==0 && A.m%B.m!=0 ) r.approx = 1;
  r.m = A.m/B.m;
  r.e = A.e - B.e;
  return r;
}

/*
** Test if A is infinite.
*/
int sqlite4_num_isinf(sqlite4_num A){
  return A.e>SQLITE4_MX_EXP && A.m!=0;
}

/*
** Test if A is NaN.
*/
int sqlite4_num_isnan(sqlite4_num A){
  return A.e>SQLITE4_MX_EXP && A.m==0; 
}

/*
** Compare numbers A and B.  Return:
**
**    1     if A<B
**    2     if A==B
**    3     if A>B
................................................................................
    return A.sign ? 1 : 3;
  }
  if( B.e>SQLITE4_MX_EXP ){
    if( B.m==0 ) return 0;
    return B.sign ? 3 : 1;
  }
  if( A.sign!=B.sign ){
    if ( A.m==0 && B.m==0 ) return 2;
    return A.sign ? 1 : 3;
  }
  adjustExponent(&A, &B);
  if( A.sign ){
    sqlite4_num t = A;
    A = B;
    B = t;
................................................................................
    i = incr;
  }else if( zIn[0]=='+' ){
    i = incr;
  }else{
    i = 0;
  }
  if( nIn<=0 ) goto not_a_valid_number;
  if( nIn>=incr*3
   && ((c=zIn[i])=='i' || c=='I')
   && ((c=zIn[i+incr])=='n' || c=='N')
   && ((c=zIn[i+incr*2])=='f' || c=='F')
  ){
    r.e = SQLITE4_MX_EXP+1;
    r.m = nIn<=i+incr*3 || zIn[i+incr*3]==0;
    return r;
  }
  while( i<nIn && (c = zIn[i])!=0 ){
    i += incr;
    if( c>='0' && c<='9' ){
      if( c=='0' && nDigit==0 ){
        if( seenRadix && r.e > -(SQLITE4_MX_EXP+1000) ) r.e--;
        continue;
      }
      nDigit++;
      if( nDigit<=18 ){
        r.m = (r.m*10) + c - '0';
        if( seenRadix ) r.e--;
      }else{
        if( c!='0' ) r.approx = 1;
        if( !seenRadix ) r.e++;
      }
    }else if( c=='.' ){
      seenRadix = 1;




    }else if( c=='e' || c=='E' ){
      int exp = 0;
      int expsign = 0;
      int nEDigit = 0;
      if( zIn[i]=='-' ){
        expsign = 1;
        i += incr;
      }else if( zIn[i]=='+' ){
        i += incr;
      }
      if( i>=nIn ) goto not_a_valid_number;
      while( i<nIn && (c = zIn[i])!=0 ){
        i += incr;
        if( c<'0' || c>'9' ) goto not_a_valid_number;
        if( c=='0' && nEDigit==0 ) continue;
        nEDigit++;
        if( nEDigit>3 ) goto not_a_valid_number;
        exp = exp*10 + c - '0';
      }
      if( expsign ) exp = -exp;
      r.e += exp;
      break;
    }else{
      goto not_a_valid_number;
    }

  }
  return r;
  
not_a_valid_number:
  r.e = SQLITE4_MX_EXP+1;
  r.m = 0;
  return r;  
}

/*
** Convert an sqlite4_int64 to a number and return that number.
*/
sqlite4_num sqlite4_num_from_int64(sqlite4_int64 n){
  sqlite4_num r;
  r.approx = 0;
  r.e = 0;
  r.sign = n < 0;
  if( n>=0 ){
    r.m = n;
  }else if( n!=SMALLEST_INT64 ){
    r.m = -n;
  }else{
    r.m = 1+(u64)LARGEST_INT64;
  }
  return r;
}

/*
** Convert an integer into text in the buffer supplied. The
** text is zero-terminated and right-justified in the buffer.
** A pointer to the first character of text is returned.
**
** The buffer needs to be at least 21 bytes in length.
................................................................................
    zNum += m;
    n -= m;
    removeTrailingZeros(zNum, &n);
    if( n>0 ){
      zOut[0] = '.';
      memcpy(zOut+1, zNum, n);
      nOut += n;

      zOut[n+1] = 0;
    }else{
      zOut[0] = 0;
    }
    return nOut;
  }
  if( x.e<0 && x.e >= -n-5 ){
    /* Values less than 1 and with no more than 5 subsequent zeros prior
    ** to the first significant digit.  Ex:  0.0000012345 */
    int j = -(n + x.e);
    memcpy(zOut, "0.", 2);

Changes to src/sqlite.h.in.

4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
** Every number in SQLite is represented in memory by an instance of
** the following object.
*/
typedef struct sqlite4_num sqlite4_num;
struct sqlite4_num {
  unsigned char sign;     /* Sign of the overall value */
  unsigned char approx;   /* True if the value is approximate */
  unsigned short e;       /* The exponent. */
  sqlite4_uint64 m;       /* The significant */
};

/*
** CAPI4REF: Operations On SQLite Number Objects
*/
sqlite4_num sqlite4_num_add(sqlite4_num, sqlite4_num);







|







4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
** Every number in SQLite is represented in memory by an instance of
** the following object.
*/
typedef struct sqlite4_num sqlite4_num;
struct sqlite4_num {
  unsigned char sign;     /* Sign of the overall value */
  unsigned char approx;   /* True if the value is approximate */
  short e;                /* The exponent. */
  sqlite4_uint64 m;       /* The significant */
};

/*
** CAPI4REF: Operations On SQLite Number Objects
*/
sqlite4_num sqlite4_num_add(sqlite4_num, sqlite4_num);

Changes to src/sqliteInt.h.

460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
/*
** Constants for the largest and smallest possible 64-bit signed integers.
** These macros are designed to work correctly on both 32-bit and 64-bit
** compilers.
*/
#define LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
#define LARGEST_UINT64  (0xffffffff|(((i64)0xffffffff)<<32))

/* 
** Round up a number to the next larger multiple of 8.  This is used
** to force 8-byte alignment on 64-bit architectures.
*/
#define ROUND8(x)     (((x)+7)&~7)








|







460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
/*
** Constants for the largest and smallest possible 64-bit signed integers.
** These macros are designed to work correctly on both 32-bit and 64-bit
** compilers.
*/
#define LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
#define LARGEST_UINT64  (0xffffffff|(((u64)0xffffffff)<<32))

/* 
** Round up a number to the next larger multiple of 8.  This is used
** to force 8-byte alignment on 64-bit architectures.
*/
#define ROUND8(x)     (((x)+7)&~7)

Added test/num.test.























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 2001 September 15
#
# 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 implements regression tests for SQLite library.  The
# focus of this file is testing the sqlite_*_printf() interface.
#
# $Id: printf.test,v 1.31 2009/02/01 00:21:10 drh Exp $

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

do_test num-1.1.1 {
  sqlite4_num_compare 20 20 
} {equal}
do_test num-1.1.2 {
  sqlite4_num_compare 20 2e1
} {equal}
do_test num-1.1.3 {
  sqlite4_num_compare -00034 -3.4e1
} {equal}
do_test num-1.1.4 {
  sqlite4_num_compare -inf +inf
} {lesser}
do_test num-1.1.5 {
  sqlite4_num_compare -inf 0
} {lesser}
do_test num-1.1.6 {
  sqlite4_num_compare inf 4
} {greater}
do_test num-1.1.7 {
  sqlite4_num_compare nan 7
} {incomparable}
# Is +0 > -0?
#do_test num-equal-1.1.4 {
#  sqlite4_num_compare +0 -0
#} {equal}

do_test num-2.1.1 {
  sqlite4_num_to_text [sqlite4_num_from_text 37]
} {37}
do_test num-2.1.2 {
  sqlite4_num_to_text [sqlite4_num_from_text 37 2]
} {37}
do_test num-2.1.4 {
  sqlite4_num_compare [sqlite4_num_from_text 2.9e2X 5] 290
} {equal}
do_test num-2.1.5 {
  sqlite4_num_isnan [sqlite4_num_from_text inf 2]
} {true}
do_test num-2.1.6 {
  sqlite4_num_isinf [sqlite4_num_from_text inf 3]
} {true}

do_test num-3.1.1 {
  sqlite4_num_to_text [sqlite4_num_add 5 7]
} {12}

do_test num-4.1.1 {
  sqlite4_num_to_text [sqlite4_num_sub 9 3]
} {6}
do_test num-4.1.2 {
  sqlite4_num_to_text [sqlite4_num_sub 5 12]
} {-7}
do_test num-4.2.1 {
  sqlite4_num_compare [sqlite4_num_sub 1 1] [sqlite4_num_sub -1 -1]
} {equal}

do_test num-5.1.1 {
  sqlite4_num_to_text [sqlite4_num_mul 9 8]
} {72}

do_test num-6.1.1 {
  sqlite4_num_to_text [sqlite4_num_div 6 5]
} {1.2}
do_test num-6.1.2 {
  sqlite4_num_compare 2 [sqlite4_num_div 2 1]
} {equal}
do_test num-6.1.3 {
  sqlite4_num_to_text [sqlite4_num_div 2 1]
} {2}
do_test num-6.1.4 {
  sqlite4_num_to_text [sqlite4_num_div 22 10]
} {2.2}
finish_test

Changes to test/test_main.c.

4348
4349
4350
4351
4352
4353
4354







































































































































































































4355
4356
4357
4358
4359
4360
4361
....
4401
4402
4403
4404
4405
4406
4407
4408









4409
4410
4411
4412
4413
4414
4415
      Tcl_AppendResult(interp, " ", aOpt[i].zOptName);
    }
    return TCL_ERROR;
  }
  sqlite4_test_control(SQLITE4_TESTCTRL_OPTIMIZATIONS, db, mask);
  return TCL_OK;
}








































































































































































































/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest1_Init(Tcl_Interp *interp){
  extern int sqlite4_search_count;
  extern int sqlite4_found_count;
................................................................................
     { "sqlite_set_magic",              (Tcl_CmdProc*)sqlite_set_magic      },
     { "sqlite4_interrupt",             (Tcl_CmdProc*)test_interrupt        },
     { "sqlite_delete_function",        (Tcl_CmdProc*)delete_function       },
     { "sqlite_delete_collation",       (Tcl_CmdProc*)delete_collation      },
     { "sqlite4_get_autocommit",        (Tcl_CmdProc*)get_autocommit        },
     { "sqlite4_stack_used",            (Tcl_CmdProc*)test_stack_used       },
     { "printf",                        (Tcl_CmdProc*)test_printf           },
     { "sqlite4IoTrace",              (Tcl_CmdProc*)test_io_trace         },









  };
  static struct {
     char *zName;
     Tcl_ObjCmdProc *xProc;
     void *clientData;
  } aObjCmd[] = {
     { "sqlite4_connection_pointer",    get_sqlite_pointer, 0 },







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







 







|
>
>
>
>
>
>
>
>
>







4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
....
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
      Tcl_AppendResult(interp, " ", aOpt[i].zOptName);
    }
    return TCL_ERROR;
  }
  sqlite4_test_control(SQLITE4_TESTCTRL_OPTIMIZATIONS, db, mask);
  return TCL_OK;
}

#define NUM_FORMAT "sign:%d approx:%d e:%d m:%lld"

/* Append a return value representing a sqlite4_num.
*/
static void append_num_result( Tcl_Interp *interp, sqlite4_num A ){
  char buf[100];
  sprintf( buf, NUM_FORMAT, A.sign, A.approx, A.e, A.m );
  Tcl_AppendResult(interp, buf, 0);
}

/* Convert a string either representing a sqlite4_num (listing its fields as
** returned by append_num_result) or that can be parsed as one. Invalid
** strings become NaN.
*/
static sqlite4_num test_parse_num( char *arg ){
  sqlite4_num A;
  int sign, approx, e;
  if( sscanf( arg, NUM_FORMAT, &sign, &approx, &e, &A.m)==4 ){
    A.sign = sign;
    A.approx = approx;
    A.e = e;
    return A;
  } else {
    return sqlite4_num_from_text(arg, -1, 0);
  }
}

/* Convert return values of sqlite4_num to strings that will be readable in
** the tests.
*/
static char *describe_num_comparison( int code ){
  switch( code ){
    case 0: return "incomparable";
    case 1: return "lesser";
    case 2: return "equal";
    case 3: return "greater";
    default: return "error"; 
  }
}

/* Compare two numbers A and B. Returns "incomparable", "lesser", "equal",
** "greater", or "error".
*/
static int test_num_compare(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  sqlite4_num A, B;
  int cmp;
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " NUM NUM\"", 0);
    return TCL_ERROR;
  }
  
  A = test_parse_num( argv[1] );
  B = test_parse_num( argv[2] );
  cmp = sqlite4_num_compare(A, B);
  Tcl_AppendResult( interp, describe_num_comparison( cmp ), 0);
  return TCL_OK; 
}

/* Create a sqlite4_num from a string. The optional second argument specifies
** how many bytes may be read.
*/
static int test_num_from_text(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  sqlite4_num A;
  int len;
  if( argc!=2 && argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
      " STRING\" or \"", argv[0], " STRING INTEGER\"", 0);
    return TCL_ERROR;
  }

  if( argc==3 ){
    if ( Tcl_GetInt(interp, argv[2], &len) ) return TCL_ERROR; 
  }else{
    len = -1;
  }

  A = sqlite4_num_from_text( argv[1], len, 0 );
  append_num_result(interp, A);
  return TCL_OK;
}

static int test_num_to_text(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  char text[30];
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
      " NUM\"", 0);
    return TCL_ERROR;
  }
  sqlite4_num_to_text( test_parse_num( argv[1] ), text );
  Tcl_AppendResult( interp, text, 0 );
  return TCL_OK;
}

static int test_num_binary_op(
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv,            /* Text of each argument */
  sqlite4_num (*op) (sqlite4_num, sqlite4_num)
){
  sqlite4_num A, B, R;
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
      " NUM NUM\"", 0);
    return TCL_ERROR;
  }
  A = test_parse_num(argv[1]);
  B = test_parse_num(argv[2]);
  R = op(A, B);
  append_num_result(interp, R);
  return TCL_OK;
}

static int test_num_add(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  return test_num_binary_op( interp, argc, argv, sqlite4_num_add );
}

static int test_num_sub(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  return test_num_binary_op( interp, argc, argv, sqlite4_num_sub );
}

static int test_num_mul(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  return test_num_binary_op( interp, argc, argv, sqlite4_num_mul );
}

static int test_num_div(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  return test_num_binary_op( interp, argc, argv, sqlite4_num_div );
}

static int test_num_predicate(
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv,            /* Text of each argument */
  int (*pred) (sqlite4_num)
){
  sqlite4_num A;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
      " NUM\"", 0);
    return TCL_ERROR;
  }
  A = test_parse_num(argv[1]);
  Tcl_AppendResult(interp, pred(A) ? "true" : "false", 0);  
  return TCL_OK;
}

static int test_num_isinf(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  return test_num_predicate( interp, argc, argv, sqlite4_num_isinf );
}

static int test_num_isnan(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  return test_num_predicate( interp, argc, argv, sqlite4_num_isnan );
}

/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest1_Init(Tcl_Interp *interp){
  extern int sqlite4_search_count;
  extern int sqlite4_found_count;
................................................................................
     { "sqlite_set_magic",              (Tcl_CmdProc*)sqlite_set_magic      },
     { "sqlite4_interrupt",             (Tcl_CmdProc*)test_interrupt        },
     { "sqlite_delete_function",        (Tcl_CmdProc*)delete_function       },
     { "sqlite_delete_collation",       (Tcl_CmdProc*)delete_collation      },
     { "sqlite4_get_autocommit",        (Tcl_CmdProc*)get_autocommit        },
     { "sqlite4_stack_used",            (Tcl_CmdProc*)test_stack_used       },
     { "printf",                        (Tcl_CmdProc*)test_printf           },
     { "sqlite4IoTrace",                (Tcl_CmdProc*)test_io_trace         },
     { "sqlite4_num_compare",           (Tcl_CmdProc*)test_num_compare      }, 
     { "sqlite4_num_from_text",         (Tcl_CmdProc*)test_num_from_text    }, 
     { "sqlite4_num_to_text",           (Tcl_CmdProc*)test_num_to_text      },
     { "sqlite4_num_add",               (Tcl_CmdProc*)test_num_add          },
     { "sqlite4_num_sub",               (Tcl_CmdProc*)test_num_sub          },
     { "sqlite4_num_mul",               (Tcl_CmdProc*)test_num_mul          },
     { "sqlite4_num_div",               (Tcl_CmdProc*)test_num_div          },
     { "sqlite4_num_isinf",             (Tcl_CmdProc*)test_num_isinf        },
     { "sqlite4_num_isnan",             (Tcl_CmdProc*)test_num_isnan        },
  };
  static struct {
     char *zName;
     Tcl_ObjCmdProc *xProc;
     void *clientData;
  } aObjCmd[] = {
     { "sqlite4_connection_pointer",    get_sqlite_pointer, 0 },