System.Data.SQLite
Check-in [4c4566e720]
Not logged in

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

Overview
Comment:Add support for creating custom SQL functions using delegates.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 4c4566e720d7b7c702ff6ab499635ffc99f46c1f
User & Date: mistachkin 2015-08-17 19:30:04
Context
2015-08-18
03:04
Update the included FTS5 extension to the latest trunk code. check-in: e3cb9d9023 user: mistachkin tags: trunk
2015-08-17
19:30
Add support for creating custom SQL functions using delegates. check-in: 4c4566e720 user: mistachkin tags: trunk
19:27
Further improvements to comments. Closed-Leaf check-in: 73d28a2e59 user: mistachkin tags: delegateFunction
18:41
Update tests to account for Eagle core library changes. check-in: 4d039ece1c user: mistachkin tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Doc/Extra/Provider/version.html.

43
44
45
46
47
48
49

50
51
52
53
54
55
56
    <div id="mainSection">
    <div id="mainBody">
    <h1 class="heading">Version History</h1>
    <p><b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/releaselog/3_8_11_1.html">SQLite 3.8.11.1</a>.</li>
      <li>Add full support for Visual Studio 2015 and the .NET Framework 4.6.</li>

      <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Prevent encrypted connections from being used with the connection pool. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/89d3a159f1">[89d3a159f1]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Honor the second argument to Math.Round when using LINQ.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Honor the pre-existing flags for connections during the Open method. Fix for <a href="https://system.data.sqlite.org/index.html/info/964063da16">[964063da16]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for <a href="https://system.data.sqlite.org/index.html/info/9d353b0bd8">[9d353b0bd8]</a>.</li>
      <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/71bedaca19">[71bedaca19]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>







>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    <div id="mainSection">
    <div id="mainBody">
    <h1 class="heading">Version History</h1>
    <p><b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/releaselog/3_8_11_1.html">SQLite 3.8.11.1</a>.</li>
      <li>Add full support for Visual Studio 2015 and the .NET Framework 4.6.</li>
      <li>Add support for creating custom SQL functions using delegates.</li>
      <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Prevent encrypted connections from being used with the connection pool. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/89d3a159f1">[89d3a159f1]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Honor the second argument to Math.Round when using LINQ.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Honor the pre-existing flags for connections during the Open method. Fix for <a href="https://system.data.sqlite.org/index.html/info/964063da16">[964063da16]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for <a href="https://system.data.sqlite.org/index.html/info/9d353b0bd8">[9d353b0bd8]</a>.</li>
      <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/71bedaca19">[71bedaca19]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>

Changes to System.Data.SQLite/SQLiteConnection.cs.

1408
1409
1410
1411
1412
1413
1414








































1415
1416
1417
1418
1419
1420
1421

        if (_sql == null)
            throw new InvalidOperationException(
                "Database connection not valid for binding functions.");

        _sql.BindFunction(functionAttribute, function, _flags);
    }









































    ///////////////////////////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// Attempts to unbind the specified <see cref="SQLiteFunction" /> object
    /// instance to this connection.
    /// </summary>







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







1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461

        if (_sql == null)
            throw new InvalidOperationException(
                "Database connection not valid for binding functions.");

        _sql.BindFunction(functionAttribute, function, _flags);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// Attempts to bind the specified <see cref="SQLiteFunction" /> object
    /// instance to this connection.
    /// </summary>
    /// <param name="functionAttribute">
    /// The <see cref="SQLiteFunctionAttribute" /> object instance containing
    /// the metadata for the function to be bound.
    /// </param>
    /// <param name="callback1">
    /// A <see cref="Delegate" /> object instance that helps implement the
    /// function to be bound.  For scalar functions, this corresponds to the
    /// <see cref="SQLiteInvokeDelegate" /> type.  For aggregate functions,
    /// this corresponds to the <see cref="SQLiteStepDelegate" /> type.  For
    /// collation functions, this corresponds to the
    /// <see cref="SQLiteCompareDelegate" /> type.
    /// </param>
    /// <param name="callback2">
    /// A <see cref="Delegate" /> object instance that helps implement the
    /// function to be bound.  For aggregate functions, this corresponds to the
    /// <see cref="SQLiteFinalDelegate" /> type.  For other callback types, it
    /// is not used and must be null.
    /// </param>
    public void BindFunction(
        SQLiteFunctionAttribute functionAttribute,
        Delegate callback1,
        Delegate callback2
        )
    {
        CheckDisposed();

        if (_sql == null)
            throw new InvalidOperationException(
                "Database connection not valid for binding functions.");

        _sql.BindFunction(functionAttribute,
            new SQLiteDelegateFunction(callback1, callback2), _flags);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// Attempts to unbind the specified <see cref="SQLiteFunction" /> object
    /// instance to this connection.
    /// </summary>

Changes to System.Data.SQLite/SQLiteFunction.cs.

737
738
739
740
741
742
743

















































744



745














































746
747
748
749
750
751
752
...
770
771
772
773
774
775
776
777
778
779


780
781





782
783
784
785
786
787
788
...
986
987
988
989
990
991
992

993

















994


































































































































































































































































































































































































































































































































































995
996
997
998
999
1000
1001
        for (int y = 0; y < arAtt.Length; y++)
        {
            SQLiteFunctionAttribute at = arAtt[y] as SQLiteFunctionAttribute;

            if (at == null)
                continue;


















































            at.InstanceType = typ;



            _registeredFunctions.Add(at, null);














































        }
    }

    /// <summary>
    /// Called by the SQLiteBase derived classes, this method binds all registered (known) user-defined functions to a connection.
    /// It is done this way so that all user-defined functions will access the database using the same encoding scheme
    /// as the connection (UTF-8 or UTF-16).
................................................................................
                in _registeredFunctions)
        {
            SQLiteFunctionAttribute pr = pair.Key;

            if (pr == null)
                continue;

            SQLiteFunction f = (SQLiteFunction)Activator.CreateInstance(
                pr.InstanceType);



            BindFunction(sqlbase, pr, f, flags);
            lFunctions[pr] = f;





        }

        return lFunctions;
    }

    /// <summary>
    /// Called by the SQLiteBase derived classes, this method unbinds all registered (known)
................................................................................
        {
            return sqliteBase.CreateCollation(
                name, null, null, false) == SQLiteErrorCode.Ok;
        }
    }
  }
























































































































































































































































































































































































































































































































































































  /// <summary>
  /// Extends SQLiteFunction and allows an inherited class to obtain the collating sequence associated with a function call.
  /// </summary>
  /// <remarks>
  /// User-defined functions can call the GetCollationSequence() method in this class and use it to compare strings and char arrays.
  /// </remarks>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
<

>
>
|
|
>
>
>
>
>







 







>

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

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







737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
...
868
869
870
871
872
873
874
875

876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
....
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
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
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
        for (int y = 0; y < arAtt.Length; y++)
        {
            SQLiteFunctionAttribute at = arAtt[y] as SQLiteFunctionAttribute;

            if (at == null)
                continue;

            RegisterFunction(
                at.Name, at.Arguments, at.FuncType, typ,
                at.Callback1, at.Callback2);
        }
    }

    /// <summary>
    /// Alternative method of registering a function.  This method
    /// does not require the specified type to be annotated with
    /// <see cref="SQLiteFunctionAttribute" />.
    /// </summary>
    /// <param name="name">
    /// The name of the function to register.
    /// </param>
    /// <param name="argumentCount">
    /// The number of arguments accepted by the function.
    /// </param>
    /// <param name="functionType">
    /// The type of SQLite function being resitered (e.g. scalar,
    /// aggregate, or collating sequence).
    /// </param>
    /// <param name="instanceType">
    /// The <see cref="Type" /> that actually implements the function.
    /// This will only be used if the <paramref name="callback1" />
    /// and <paramref name="callback2" /> parameters are null.
    /// </param>
    /// <param name="callback1">
    /// The <see cref="Delegate" /> to be used for all calls into the
    /// <see cref="SQLiteFunction.Invoke" />,
    /// <see cref="SQLiteFunction.Step" />,
    /// and <see cref="SQLiteFunction.Compare" /> virtual methods.
    /// </param>
    /// <param name="callback2">
    /// The <see cref="Delegate" /> to be used for all calls into the
    /// <see cref="SQLiteFunction.Final" /> virtual method.  This
    /// parameter is only necessary for aggregate functions.
    /// </param>
    public static void RegisterFunction(
        string name,
        int argumentCount,
        FunctionType functionType,
        Type instanceType,
        Delegate callback1,
        Delegate callback2
        )
    {
        SQLiteFunctionAttribute at = new SQLiteFunctionAttribute(
            name, argumentCount, functionType);

        at.InstanceType = instanceType;
        at.Callback1 = callback1;
        at.Callback2 = callback2;

        _registeredFunctions.Add(at, null);
    }

    /// <summary>
    /// Creates a <see cref="SQLiteFunction" /> instance based on the specified
    /// <see cref="SQLiteFunctionAttribute" />.
    /// </summary>
    /// <param name="functionAttribute">
    /// The <see cref="SQLiteFunctionAttribute" /> containing the metadata about
    /// the function to create.
    /// </param>
    /// <param name="function">
    /// The created function -OR- null if the function could not be created.
    /// </param>
    /// <returns>
    /// Non-zero if the function was created; otherwise, zero.
    /// </returns>
    private static bool CreateFunction(
        SQLiteFunctionAttribute functionAttribute,
        out SQLiteFunction function
        )
    {
        if (functionAttribute == null)
        {
            function = null;
            return false;
        }
        else if ((functionAttribute.Callback1 != null) ||
                (functionAttribute.Callback2 != null))
        {
            function = new SQLiteDelegateFunction(
                functionAttribute.Callback1,
                functionAttribute.Callback2);

            return true;
        }
        else if (functionAttribute.InstanceType != null)
        {
            function = (SQLiteFunction)Activator.CreateInstance(
                functionAttribute.InstanceType);

            return true;
        }
        else
        {
            function = null;
            return false;
        }
    }

    /// <summary>
    /// Called by the SQLiteBase derived classes, this method binds all registered (known) user-defined functions to a connection.
    /// It is done this way so that all user-defined functions will access the database using the same encoding scheme
    /// as the connection (UTF-8 or UTF-16).
................................................................................
                in _registeredFunctions)
        {
            SQLiteFunctionAttribute pr = pair.Key;

            if (pr == null)
                continue;

            SQLiteFunction f;


            if (CreateFunction(pr, out f))
            {
                BindFunction(sqlbase, pr, f, flags);
                lFunctions[pr] = f;
            }
            else
            {
                lFunctions[pr] = null;
            }
        }

        return lFunctions;
    }

    /// <summary>
    /// Called by the SQLiteBase derived classes, this method unbinds all registered (known)
................................................................................
        {
            return sqliteBase.CreateCollation(
                name, null, null, false) == SQLiteErrorCode.Ok;
        }
    }
  }

  /////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// This <see cref="Delegate" /> type is used with the
  /// <see cref="SQLiteDelegateFunction.Invoke" /> method.
  /// </summary>
  /// <param name="param0">
  /// This is always the string literal "Invoke".
  /// </param>
  /// <param name="args">
  /// The arguments for the scalar function.
  /// </param>
  /// <returns>
  /// The result of the scalar function.
  /// </returns>
  public delegate object SQLiteInvokeDelegate(
    string param0,
    object[] args
  );

  /////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// This <see cref="Delegate" /> type is used with the
  /// <see cref="SQLiteDelegateFunction.Step" /> method.
  /// </summary>
  /// <param name="param0">
  /// This is always the string literal "Step".
  /// </param>
  /// <param name="args">
  /// The arguments for the aggregate function.
  /// </param>
  /// <param name="stepNumber">
  /// The step number (one based).  This is incrememted each time the
  /// <see cref="SQLiteDelegateFunction.Step" /> method is called.
  /// </param>
  /// <param name="contextData">
  /// A placeholder for implementers to store contextual data pertaining
  /// to the current context.
  /// </param>
  public delegate void SQLiteStepDelegate(
    string param0,
    object[] args,
    int stepNumber,
    ref object contextData
  );

  /////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// This <see cref="Delegate" /> type is used with the
  /// <see cref="SQLiteDelegateFunction.Final" /> method.
  /// </summary>
  /// <param name="param0">
  /// This is always the string literal "Final".
  /// </param>
  /// <param name="contextData">
  /// A placeholder for implementers to store contextual data pertaining
  /// to the current context.
  /// </param>
  /// <returns>
  /// The result of the aggregate function.
  /// </returns>
  public delegate object SQLiteFinalDelegate(
    string param0,
    object contextData
  );

  /////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// This <see cref="Delegate" /> type is used with the
  /// <see cref="SQLiteDelegateFunction.Compare" /> method.
  /// </summary>
  /// <param name="param0">
  /// This is always the string literal "Compare".
  /// </param>
  /// <param name="param1">
  /// The first string to compare.
  /// </param>
  /// <param name="param2">
  /// The second strnig to compare.
  /// </param>
  /// <returns>
  /// A positive integer if the <paramref name="param1" /> parameter is
  /// greater than the <paramref name="param2" /> parameter, a negative
  /// integer if the <paramref name="param1" /> parameter is less than
  /// the <paramref name="param2" /> parameter, or zero if they are
  /// equal.
  /// </returns>
  public delegate int SQLiteCompareDelegate(
    string param0,
    string param1,
    string param2
  );

  /////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// This class implements a SQLite function using a <see cref="Delegate" />.
  /// All the virtual methods of the <see cref="SQLiteFunction" /> class are
  /// implemented using calls to the <see cref="Delegate.DynamicInvoke" />
  /// method.  The arguments are presented in the same order they appear in
  /// the associated <see cref="SQLiteFunction" /> methods with one exception:
  /// the first argument is the name of the virtual method being implemented.
  /// </summary>
  public class SQLiteDelegateFunction : SQLiteFunction
  {
      #region Private Constants
      /// <summary>
      /// This error message is used by the overridden virtual methods when
      /// a required <see cref="Delegate" /> property (e.g.
      /// <see cref="Callback1" /> or <see cref="Callback2" />) has not been
      /// set.
      /// </summary>
      private const string NoCallbackError = "No \"{0}\" callback is set.";

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// This error message is used by the overridden <see cref="Compare" />
      /// method when the result does not have a type of <see cref="Int32" />.
      /// </summary>
      private const string ResultInt32Error = "\"{0}\" result must be Int32.";
      #endregion

      /////////////////////////////////////////////////////////////////////////

      #region Public Constructors
      /// <summary>
      /// Constructs an empty instance of this class.
      /// </summary>
      public SQLiteDelegateFunction()
          : this(null, null)
      {
          // do nothing.
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// Constructs an instance of this class using the specified
      /// <see cref="Delegate" /> as the <see cref="SQLiteFunction" />
      /// implementation.
      /// </summary>
      /// <param name="callback1">
      /// The <see cref="Delegate" /> to be used for all calls into the
      /// <see cref="Invoke" />, <see cref="Step" />, and
      /// <see cref="Compare" /> virtual methods needed by the
      /// <see cref="SQLiteFunction" /> base class.
      /// </param>
      /// <param name="callback2">
      /// The <see cref="Delegate" /> to be used for all calls into the
      /// <see cref="Final" /> virtual methods needed by the
      /// <see cref="SQLiteFunction" /> base class.
      /// </param>
      public SQLiteDelegateFunction(
          Delegate callback1,
          Delegate callback2
          )
      {
          this.callback1 = callback1;
          this.callback2 = callback2;
      }
      #endregion

      /////////////////////////////////////////////////////////////////////////

      #region Protected Methods
      /// <summary>
      /// Returns the list of arguments for the <see cref="Invoke" /> method,
      /// as an <see cref="Array" /> of <see cref="Object" />.  The first
      /// argument is always the literal string "Invoke".
      /// </summary>
      /// <param name="args">
      /// The original arguments received by the <see cref="Invoke" /> method.
      /// </param>
      /// <param name="earlyBound">
      /// Non-zero if the returned arguments are going to be used with the
      /// <see cref="SQLiteInvokeDelegate" /> type; otherwise, zero.
      /// </param>
      /// <returns>
      /// The arguments to pass to the configured <see cref="Delegate" />.
      /// </returns>
      protected virtual object[] GetInvokeArgs(
          object[] args,
          bool earlyBound
          )
      {
          object[] newArgs = new object[] { "Invoke", args };

          if (!earlyBound)
              newArgs = new object[] { newArgs }; // WRAP

          return newArgs;
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// Returns the list of arguments for the <see cref="Step" /> method,
      /// as an <see cref="Array" /> of <see cref="Object" />.  The first
      /// argument is always the literal string "Step".
      /// </summary>
      /// <param name="args">
      /// The original arguments received by the <see cref="Step" /> method.
      /// </param>
      /// <param name="stepNumber">
      /// The step number (one based).  This is incrememted each time the
      /// <see cref="Step" /> method is called.
      /// </param>
      /// <param name="contextData">
      /// A placeholder for implementers to store contextual data pertaining
      /// to the current context.
      /// </param>
      /// <param name="earlyBound">
      /// Non-zero if the returned arguments are going to be used with the
      /// <see cref="SQLiteStepDelegate" /> type; otherwise, zero.
      /// </param>
      /// <returns>
      /// The arguments to pass to the configured <see cref="Delegate" />.
      /// </returns>
      protected virtual object[] GetStepArgs(
          object[] args,
          int stepNumber,
          object contextData,
          bool earlyBound
          )
      {
          object[] newArgs = new object[] {
              "Step", args, stepNumber, contextData
          };

          if (!earlyBound)
              newArgs = new object[] { newArgs }; // WRAP

          return newArgs;
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// Updates the output arguments for the <see cref="Step" /> method,
      /// using an <see cref="Array" /> of <see cref="Object" />.  The first
      /// argument is always the literal string "Step".  Currently, only the
      /// <paramref name="contextData" /> parameter is updated.
      /// </summary>
      /// <param name="args">
      /// The original arguments received by the <see cref="Step" /> method.
      /// </param>
      /// <param name="contextData">
      /// A placeholder for implementers to store contextual data pertaining
      /// to the current context.
      /// </param>
      /// <param name="earlyBound">
      /// Non-zero if the returned arguments are going to be used with the
      /// <see cref="SQLiteStepDelegate" /> type; otherwise, zero.
      /// </param>
      /// <returns>
      /// The arguments to pass to the configured <see cref="Delegate" />.
      /// </returns>
      protected virtual void UpdateStepArgs(
          object[] args,
          ref object contextData,
          bool earlyBound
          )
      {
          object[] newArgs;

          if (earlyBound)
              newArgs = args;
          else
              newArgs = args[0] as object[];

          if (newArgs == null)
              return;

          contextData = newArgs[newArgs.Length - 1];
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// Returns the list of arguments for the <see cref="Final" /> method,
      /// as an <see cref="Array" /> of <see cref="Object" />.  The first
      /// argument is always the literal string "Final".
      /// </summary>
      /// <param name="contextData">
      /// A placeholder for implementers to store contextual data pertaining
      /// to the current context.
      /// </param>
      /// <param name="earlyBound">
      /// Non-zero if the returned arguments are going to be used with the
      /// <see cref="SQLiteFinalDelegate" /> type; otherwise, zero.
      /// </param>
      /// <returns>
      /// The arguments to pass to the configured <see cref="Delegate" />.
      /// </returns>
      protected virtual object[] GetFinalArgs(
          object contextData,
          bool earlyBound
          )
      {
          object[] newArgs = new object[] { "Final", contextData };

          if (!earlyBound)
              newArgs = new object[] { newArgs }; // WRAP

          return newArgs;
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// Returns the list of arguments for the <see cref="Compare" /> method,
      /// as an <see cref="Array" /> of <see cref="Object" />.  The first
      /// argument is always the literal string "Compare".
      /// </summary>
      /// <param name="param1">
      /// The first string to compare.
      /// </param>
      /// <param name="param2">
      /// The second strnig to compare.
      /// </param>
      /// <param name="earlyBound">
      /// Non-zero if the returned arguments are going to be used with the
      /// <see cref="SQLiteCompareDelegate" /> type; otherwise, zero.
      /// </param>
      /// <returns>
      /// The arguments to pass to the configured <see cref="Delegate" />.
      /// </returns>
      protected virtual object[] GetCompareArgs(
          string param1,
          string param2,
          bool earlyBound
          )
      {
          object[] newArgs = new object[] { "Compare", param1, param2 };

          if (!earlyBound)
              newArgs = new object[] { newArgs }; // WRAP

          return newArgs;
      }
      #endregion

      /////////////////////////////////////////////////////////////////////////

      #region Public Properties
      private Delegate callback1;
      /// <summary>
      /// The <see cref="Delegate" /> to be used for all calls into the
      /// <see cref="Invoke" />, <see cref="Step" />, and
      /// <see cref="Compare" /> virtual methods needed by the
      /// <see cref="SQLiteFunction" /> base class.
      /// </summary>
      public virtual Delegate Callback1
      {
          get { return callback1; }
          set { callback1 = value; }
      }

      /////////////////////////////////////////////////////////////////////////

      private Delegate callback2;
      /// <summary>
      /// The <see cref="Delegate" /> to be used for all calls into the
      /// <see cref="Final" /> virtual methods needed by the
      /// <see cref="SQLiteFunction" /> base class.
      /// </summary>
      public virtual Delegate Callback2
      {
          get { return callback2; }
          set { callback2 = value; }
      }
      #endregion

      /////////////////////////////////////////////////////////////////////////

      #region System.Data.SQLite.SQLiteFunction Overrides
      /// <summary>
      /// This virtual method is the implementation for scalar functions.
      /// See the <see cref="SQLiteFunction.Invoke" /> method for more
      /// details.
      /// </summary>
      /// <param name="args">
      /// The arguments for the scalar function.
      /// </param>
      /// <returns>
      /// The result of the scalar function.
      /// </returns>
      public override object Invoke(
          object[] args /* in */
          )
      {
          if (callback1 == null)
          {
              throw new InvalidOperationException(
                  UnsafeNativeMethods.StringFormat(
                  CultureInfo.CurrentCulture,
                  NoCallbackError, "Invoke"));
          }

          SQLiteInvokeDelegate invokeDelegate =
              callback1 as SQLiteInvokeDelegate;

          if (invokeDelegate != null)
          {
              return invokeDelegate.Invoke("Invoke", args); /* throw */
          }
          else
          {
              return callback1.DynamicInvoke(
                  GetInvokeArgs(args, false)); /* throw */
          }
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// This virtual method is part of the implementation for aggregate
      /// functions.  See the <see cref="SQLiteFunction.Step" /> method
      /// for more details.
      /// </summary>
      /// <param name="args">
      /// The arguments for the aggregate function.
      /// </param>
      /// <param name="stepNumber">
      /// The step number (one based).  This is incrememted each time the
      /// <see cref="Step" /> method is called.
      /// </param>
      /// <param name="contextData">
      /// A placeholder for implementers to store contextual data pertaining
      /// to the current context.
      /// </param>
      public override void Step(
          object[] args,         /* in */
          int stepNumber,        /* in */
          ref object contextData /* in, out */
          )
      {
          if (callback1 == null)
          {
              throw new InvalidOperationException(
                  UnsafeNativeMethods.StringFormat(
                  CultureInfo.CurrentCulture,
                  NoCallbackError, "Step"));
          }

          SQLiteStepDelegate stepDelegate = callback1 as SQLiteStepDelegate;

          if (stepDelegate != null)
          {
              stepDelegate.Invoke(
                  "Step", args, stepNumber, ref contextData); /* throw */
          }
          else
          {
              object[] newArgs = GetStepArgs(
                  args, stepNumber, contextData, false);

              /* IGNORED */
              callback1.DynamicInvoke(newArgs); /* throw */

              UpdateStepArgs(newArgs, ref contextData, false);
          }
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// This virtual method is part of the implementation for aggregate
      /// functions.  See the <see cref="SQLiteFunction.Final" /> method
      /// for more details.
      /// </summary>
      /// <param name="contextData">
      /// A placeholder for implementers to store contextual data pertaining
      /// to the current context.
      /// </param>
      /// <returns>
      /// The result of the aggregate function.
      /// </returns>
      public override object Final(
          object contextData /* in */
          )
      {
          if (callback2 == null)
          {
              throw new InvalidOperationException(
                  UnsafeNativeMethods.StringFormat(
                  CultureInfo.CurrentCulture,
                  NoCallbackError, "Final"));
          }

          SQLiteFinalDelegate finalDelegate = callback2 as SQLiteFinalDelegate;

          if (finalDelegate != null)
          {
              return finalDelegate.Invoke("Final", contextData); /* throw */
          }
          else
          {
              return callback1.DynamicInvoke(GetFinalArgs(
                  contextData, false)); /* throw */
          }
      }

      /////////////////////////////////////////////////////////////////////////

      /// <summary>
      /// This virtual method is part of the implementation for collating
      /// sequences.  See the <see cref="SQLiteFunction.Compare" /> method
      /// for more details.
      /// </summary>
      /// <param name="param1">
      /// The first string to compare.
      /// </param>
      /// <param name="param2">
      /// The second strnig to compare.
      /// </param>
      /// <returns>
      /// A positive integer if the <paramref name="param1" /> parameter is
      /// greater than the <paramref name="param2" /> parameter, a negative
      /// integer if the <paramref name="param1" /> parameter is less than
      /// the <paramref name="param2" /> parameter, or zero if they are
      /// equal.
      /// </returns>
      public override int Compare(
          string param1, /* in */
          string param2  /* in */
          )
      {
          if (callback1 == null)
          {
              throw new InvalidOperationException(
                  UnsafeNativeMethods.StringFormat(
                  CultureInfo.CurrentCulture,
                  NoCallbackError, "Compare"));
          }

          SQLiteCompareDelegate compareDelegate =
              callback1 as SQLiteCompareDelegate;

          if (compareDelegate != null)
          {
              return compareDelegate.Invoke(
                  "Compare", param1, param2); /* throw */
          }
          else
          {
              object result = callback1.DynamicInvoke(GetCompareArgs(
                  param1, param2, false)); /* throw */

              if (result is int)
                  return (int)result;

              throw new InvalidOperationException(
                  UnsafeNativeMethods.StringFormat(
                  CultureInfo.CurrentCulture,
                  ResultInt32Error, "Compare"));
          }
      }
      #endregion
  }

  /////////////////////////////////////////////////////////////////////////////

  /// <summary>
  /// Extends SQLiteFunction and allows an inherited class to obtain the collating sequence associated with a function call.
  /// </summary>
  /// <remarks>
  /// User-defined functions can call the GetCollationSequence() method in this class and use it to compare strings and char arrays.
  /// </remarks>

Changes to System.Data.SQLite/SQLiteFunctionAttribute.cs.

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
..
49
50
51
52
53
54
55


56
57
58
59
60
61
62
..
80
81
82
83
84
85
86
87


88
89
90
91
92
93
94









95













  [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
  public sealed class SQLiteFunctionAttribute : Attribute
  {
    private string       _name;
    private int          _argumentCount;
    private FunctionType _functionType;
    private Type         _instanceType;



    /// <summary>
    /// Default constructor, initializes the internal variables for the function.
    /// </summary>
    public SQLiteFunctionAttribute()
        : this(String.Empty, -1, FunctionType.Scalar)
    {
        // do nothing.
    }

    /// <summary>
    /// Constructs an instance of this class.


    /// </summary>
    /// <param name="name">
    /// The name of the function, as seen by the SQLite core library.
    /// </param>
    /// <param name="argumentCount">
    /// The number of arguments that the function will accept.
    /// </param>
................................................................................
        FunctionType functionType
        )
    {
        _name = name;
        _argumentCount = argumentCount;
        _functionType = functionType;
        _instanceType = null;


    }

    /// <summary>
    /// The function's name as it will be used in SQLite command text.
    /// </summary>
    public string Name
    {
................................................................................
    {
      get { return _functionType; }
      set { _functionType = value; }
    }

    /// <summary>
    /// The <see cref="System.Type" /> object instance that describes the class
    /// containing the implementation for the associated function.


    /// </summary>
    internal Type InstanceType
    {
        get { return _instanceType; }
        set { _instanceType = value; }
    }
  }









}




















>
>





|





|
>
>







 







>
>







 







|
>
>






|
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
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
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
..
86
87
88
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
  [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
  public sealed class SQLiteFunctionAttribute : Attribute
  {
    private string       _name;
    private int          _argumentCount;
    private FunctionType _functionType;
    private Type         _instanceType;
    private Delegate     _callback1;
    private Delegate     _callback2;

    /// <summary>
    /// Default constructor, initializes the internal variables for the function.
    /// </summary>
    public SQLiteFunctionAttribute()
        : this(null, -1, FunctionType.Scalar)
    {
        // do nothing.
    }

    /// <summary>
    /// Constructs an instance of this class.  This sets the initial
    /// <see cref="InstanceType" />, <see cref="Callback1" />, and
    /// <see cref="Callback2" /> properties to null.
    /// </summary>
    /// <param name="name">
    /// The name of the function, as seen by the SQLite core library.
    /// </param>
    /// <param name="argumentCount">
    /// The number of arguments that the function will accept.
    /// </param>
................................................................................
        FunctionType functionType
        )
    {
        _name = name;
        _argumentCount = argumentCount;
        _functionType = functionType;
        _instanceType = null;
        _callback1 = null;
        _callback2 = null;
    }

    /// <summary>
    /// The function's name as it will be used in SQLite command text.
    /// </summary>
    public string Name
    {
................................................................................
    {
      get { return _functionType; }
      set { _functionType = value; }
    }

    /// <summary>
    /// The <see cref="System.Type" /> object instance that describes the class
    /// containing the implementation for the associated function.  The value of
    /// this property will not be used if either the <see cref="Callback1" /> or
    /// <see cref="Callback2" /> property values are set to non-null.
    /// </summary>
    internal Type InstanceType
    {
        get { return _instanceType; }
        set { _instanceType = value; }
    }

    /// <summary>
    /// The <see cref="Delegate" /> that refers to the implementation for the
    /// associated function.  If this property value is set to non-null, it will
    /// be used instead of the <see cref="InstanceType" /> property value.
    /// </summary>
    internal Delegate Callback1
    {
        get { return _callback1; }
        set { _callback1 = value; }
    }

    /// <summary>
    /// The <see cref="Delegate" /> that refers to the implementation for the
    /// associated function.  If this property value is set to non-null, it will
    /// be used instead of the <see cref="InstanceType" /> property value.
    /// </summary>
    internal Delegate Callback2
    {
        get { return _callback2; }
        set { _callback2 = value; }
    }
  }
}

Changes to Tests/basic.eagle.

3587
3588
3589
3590
3591
3592
3593
3594
3595
3596







































































































































































































































































3597
3598
3599
3600
3601
3602
3603
System.Data.SQLite compileCSharp} -match regexp -result \
[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
\{\} 0 \{\} 0 (?:-)?\d+ 0 \{\} 1\
\{System\.Reflection\.TargetInvocationException: Exception has been thrown by\
the target of an invocation\. ---> System\.Data\.SQLite\.SQLiteException: SQL\
logic error or missing database
no such function: MyRandom.*\} 0 \{\} 0 (?:-)?\d+ 0 \{\}$}]}

###############################################################################








































































































































































































































































reportSQLiteResources $test_channel

###############################################################################

runSQLiteTestFilesEpilogue
runSQLiteTestEpilogue
runTestEpilogue










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







3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
System.Data.SQLite compileCSharp} -match regexp -result \
[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
\{\} 0 \{\} 0 (?:-)?\d+ 0 \{\} 1\
\{System\.Reflection\.TargetInvocationException: Exception has been thrown by\
the target of an invocation\. ---> System\.Data\.SQLite\.SQLiteException: SQL\
logic error or missing database
no such function: MyRandom.*\} 0 \{\} 0 (?:-)?\d+ 0 \{\}$}]}

###############################################################################

runTest {test data-1.74 {bind functions using delegate} -setup {
  proc getMyFuncArgs { argumentCount } {
    set result [list]

    for {set index 0} {$index < $argumentCount} {incr index} {
      lappend result [appendArgs 'myFuncArg [expr {$index + 1}] ']
    }

    return $result
  }

  proc getHashCode { value } {
    if {[isObjectHandle $value]} then {
      if {$value eq "null"} then {
        return 0
      } else {
        return [object invoke $value GetHashCode]
      }
    } else {
      if {[string length $value] == 0} then {
        return 0
      } else {
        set string [object create String $value]

        return [object invoke $string GetHashCode]
      }
    }
  }

  proc hashManagedArray { array } {
    set data ""

    if {[isObjectHandle $array] && $array ne "null"} then {
      if {[object invoke $array GetType.IsArray]} then {
        for {set index 0} {$index < [$array Length]} {incr index} {
          set element [$array -create -alias GetValue $index]

          if {[string length $element] > 0} then {
            append data [$element ToString]
          } else {
            append data null
          }
        }
      }
    }

    return [getHashCode [hash normal sha1 $data]]
  }

  proc myFuncCallback { args } {
    if {[llength $args] == 0} then {
      error "no function arguments"
    }

    set name [lindex $args 0]

    if {[isObjectHandle $name] && $name ne "null"} then {
      set name [object invoke $name ToString]
    }

    switch -exact -- $name {
      Invoke {
        return [hashManagedArray [lindex $args end]]
      }
      Step {
        set varName [lindex $args end]

        if {[string length $varName] == 0} then {
          error "invalid aggregate context variable name"
        }

        upvar 1 $varName ctx

        if {![info exists ctx] || [string length $ctx] == 0} then {
          set ctx [pid]
        }

        set hashCtx [getHashCode $ctx]
        set hashArgs [hashManagedArray [lindex $args end-2]]

        if {[info exists ::aggregateData($hashCtx)]} then {
          incr ::aggregateData($hashCtx) $hashArgs
        } else {
          set ::aggregateData($hashCtx) $hashArgs
        }
      }
      Final {
        set ctx [lindex $args end]

        if {[string length $ctx] == 0} then {
          error "invalid aggregate context"
        }

        set hashCtx [getHashCode $ctx]

        if {[info exists ::aggregateData($hashCtx)]} then {
          return $::aggregateData($hashCtx)
        } else {
          error "missing aggregate context data"
        }
      }
      Compare {
        lappend ::compareResults [object invoke -create \
            Int32 Parse [string compare -nocase [lindex \
            $args 1] [lindex $args 2]]]

        return [lindex $::compareResults end]
      }
      default {
        error [appendArgs "unknown function callback \"" $name \"]
      }
    }
  }

  proc myFuncInvokeCallback { param0 objs } {
    return [myFuncCallback $param0 $objs]
  }

  proc myFuncStepCallback { param0 objs stepNumber contextDataVarName } {
    upvar 1 $contextDataVarName $contextDataVarName
    return [myFuncCallback $param0 $objs $stepNumber $contextDataVarName]
  }

  proc myFuncFinalCallback { param0 contextData } {
    return [myFuncCallback $param0 $contextData ]
  }

  proc myFuncCompareCallback { param0 param1 param2 } {
    return [myFuncCallback $param0 $param1 $param2]
  }

  setupDb [set fileName data-1.74.db]
} -body {
  sql execute $db "CREATE TABLE t1(x);"
  sql execute $db "INSERT INTO t1 (x) VALUES(1);"
  sql execute $db "INSERT INTO t1 (x) VALUES(2);"
  sql execute $db "INSERT INTO t1 (x) VALUES(3);"
  sql execute $db "INSERT INTO t1 (x) VALUES('A');"
  sql execute $db "INSERT INTO t1 (x) VALUES('a');"
  sql execute $db "INSERT INTO t1 (x) VALUES('M');"
  sql execute $db "INSERT INTO t1 (x) VALUES('m');"
  sql execute $db "INSERT INTO t1 (x) VALUES('Z');"
  sql execute $db "INSERT INTO t1 (x) VALUES('z');"

  set connection [getDbConnection]

  for {set argumentCount 0} {$argumentCount < 3} {incr argumentCount} {
    set attribute(1,$argumentCount) [object create \
        System.Data.SQLite.SQLiteFunctionAttribute [appendArgs \
        myFunc1_74_1_ $argumentCount] $argumentCount Scalar]

    $connection -marshalflags \
        {-StrictMatchType +DynamicCallback ForceParameterType} \
        -parametertypes [list System.Data.SQLite.SQLiteFunctionAttribute \
            System.Data.SQLite.SQLiteInvokeDelegate Delegate] \
        BindFunction $attribute(1,$argumentCount) \
        myFuncInvokeCallback null

    set attribute(2,$argumentCount) [object create \
        System.Data.SQLite.SQLiteFunctionAttribute [appendArgs \
        myFunc1_74_2_ $argumentCount] $argumentCount Aggregate]

    $connection -marshalflags \
        {-StrictMatchType +DynamicCallback ForceParameterType} \
        -parametertypes [list System.Data.SQLite.SQLiteFunctionAttribute \
            System.Data.SQLite.SQLiteStepDelegate \
            System.Data.SQLite.SQLiteFinalDelegate] \
        BindFunction $attribute(2,$argumentCount) \
        myFuncStepCallback myFuncFinalCallback
  }

  set attribute(3,0) [object create \
      System.Data.SQLite.SQLiteFunctionAttribute myFunc1_74_3 0 \
      Collation]

  $connection -marshalflags \
      {-StrictMatchType +DynamicCallback ForceParameterType} \
      -parametertypes [list System.Data.SQLite.SQLiteFunctionAttribute \
          System.Data.SQLite.SQLiteCompareDelegate Delegate] \
      BindFunction $attribute(3,0) \
      myFuncCompareCallback null

  for {set argumentCount 0} {$argumentCount < 3} {incr argumentCount} {
    lappend result [catch {
      sql execute $db [appendArgs \
          "SELECT " myFunc1_74_1_ $argumentCount ( \
          [join [getMyFuncArgs $argumentCount] ,] )\;]
    } error] $error

    lappend result [catch {
      sql execute $db [appendArgs \
          "SELECT " myFunc1_74_2_ $argumentCount ( \
          [join [getMyFuncArgs $argumentCount] ,] )\;]
    } error] $error
  }

  lappend result [catch {
    sql execute -execute reader -format list $db \
        "SELECT x FROM t1 ORDER BY x COLLATE myFunc1_74_3;"
  } error] $error

  lappend result [$connection UnbindAllFunctions false]

  for {set argumentCount 0} {$argumentCount < 3} {incr argumentCount} {
    lappend result [catch {
      sql execute $db [appendArgs \
          "SELECT " myFunc1_74_1_ $argumentCount ( \
          [join [getMyFuncArgs $argumentCount] ,] )\;]
    } error] [expr {[string first [appendArgs \
        "no such function: myFunc1_74_1_" $argumentCount] $error] != -1}]

    lappend result [catch {
      sql execute $db [appendArgs \
          "SELECT " myFunc1_74_2_ $argumentCount ( \
          [join [getMyFuncArgs $argumentCount] ,] )\;]
    } error] [expr {[string first [appendArgs \
        "no such function: myFunc1_74_2_" $argumentCount] $error] != -1}]
  }

  lappend result [catch {
    sql execute -execute reader -format list $db \
        "SELECT x FROM t1 ORDER BY x COLLATE myFunc1_74_3;"
  } error] [expr {[string first "no such collation sequence: myFunc1_74_3" \
      $error] != -1}]

  lappend result [array size aggregateData]
  lappend result [testArrayGet aggregateData]

  set result
} -cleanup {
  cleanupDb $fileName

  freeDbConnection

  catch {object removecallback myFuncCompareCallback}
  catch {object removecallback myFuncFinalCallback}
  catch {object removecallback myFuncStepCallback}
  catch {object removecallback myFuncInvokeCallback}

  catch {
    foreach compareResult $compareResults {
      catch {object dispose $compareResult}
    }
  }

  unset -nocomplain result error compareResult compareResults \
      aggregateData argumentCount attribute connection db fileName

  rename myFuncCompareCallback ""
  rename myFuncFinalCallback ""
  rename myFuncStepCallback ""
  rename myFuncInvokeCallback ""
  rename myFuncCallback ""
  rename hashManagedArray ""
  rename getHashCode ""
  rename getMyFuncArgs ""
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite} -match regexp -result {^0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 \{1\
2 3 A a M m Z z\} True 1 True 1 True 1 True 1 True 1 True 1 True 1 True 1\
\{(?:-)?\d+ (?:-)?\d+\}$}}

###############################################################################

reportSQLiteResources $test_channel

###############################################################################

runSQLiteTestFilesEpilogue
runSQLiteTestEpilogue
runTestEpilogue

Changes to readme.htm.

210
211
212
213
214
215
216

217
218
219
220
221
222
223

<p>
    <b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/releaselog/3_8_11_1.html">SQLite 3.8.11.1</a>.</li>
    <li>Add full support for Visual Studio 2015 and the .NET Framework 4.6.</li>

    <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Prevent encrypted connections from being used with the connection pool. Pursuant to [89d3a159f1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the second argument to Math.Round when using LINQ.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the pre-existing flags for connections during the Open method. Fix for [964063da16].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for [9d353b0bd8].</li>
    <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to [71bedaca19].&nbsp;<b>** Potentially Incompatible Change **</b></li>







>







210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

<p>
    <b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/releaselog/3_8_11_1.html">SQLite 3.8.11.1</a>.</li>
    <li>Add full support for Visual Studio 2015 and the .NET Framework 4.6.</li>
    <li>Add support for creating custom SQL functions using delegates.</li>
    <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Prevent encrypted connections from being used with the connection pool. Pursuant to [89d3a159f1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the second argument to Math.Round when using LINQ.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the pre-existing flags for connections during the Open method. Fix for [964063da16].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for [9d353b0bd8].</li>
    <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to [71bedaca19].&nbsp;<b>** Potentially Incompatible Change **</b></li>

Changes to www/news.wiki.

4
5
6
7
8
9
10

11
12
13
14
15
16
17

<p>
    <b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/releaselog/3_8_11_1.html|SQLite 3.8.11.1].</li>
    <li>Add full support for Visual Studio 2015 and the .NET Framework 4.6.</li>

    <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Prevent encrypted connections from being used with the connection pool. Pursuant to [89d3a159f1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the second argument to Math.Round when using LINQ.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the pre-existing flags for connections during the Open method. Fix for [964063da16].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for [9d353b0bd8].</li>
    <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to [71bedaca19].&nbsp;<b>** Potentially Incompatible Change **</b></li>







>







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<p>
    <b>1.0.98.0 - August XX, 2015 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/releaselog/3_8_11_1.html|SQLite 3.8.11.1].</li>
    <li>Add full support for Visual Studio 2015 and the .NET Framework 4.6.</li>
    <li>Add support for creating custom SQL functions using delegates.</li>
    <li>Implement the Substring method for LINQ using the &quot;substr&quot; core SQL function.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Prevent encrypted connections from being used with the connection pool. Pursuant to [89d3a159f1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the second argument to Math.Round when using LINQ.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Honor the pre-existing flags for connections during the Open method. Fix for [964063da16].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Remove errant semi-colons from the SQL used by LINQ to INSERT and then SELECT rows with composite primary keys. Fix for [9d353b0bd8].</li>
    <li>Change the base type for the SQLiteConnectionFlags enumeration to long integer.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Add extended return codes to the SQLiteErrorCode enumeration. Pursuant to [71bedaca19].&nbsp;<b>** Potentially Incompatible Change **</b></li>