System.Data.SQLite

Check-in [3c25655a66]
Login

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

Overview
Comment:Refactor SQLiteDelegateFunction constructor to accept two delegates, not one. Use early-bound delegates by default. Get tests passing.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | delegateFunction
Files: files | file ages | folders
SHA1: 3c25655a663df54c7bee64fcc2973c1b2c604ea0
User & Date: mistachkin 2015-08-15 03:48:55.450
Original Comment: Refactor SQLiteDelegateFunction constructor to accept two delegates, not one. Use late-bound delegates by default. Get tests passing.
Context
2015-08-15
04:04
Remove incorrect comments. check-in: b33115baa1 user: mistachkin tags: delegateFunction
03:48
Refactor SQLiteDelegateFunction constructor to accept two delegates, not one. Use early-bound delegates by default. Get tests passing. check-in: 3c25655a66 user: mistachkin tags: delegateFunction
2015-08-14
23:20
More work in progress fine-tuning the implementation. check-in: ef7ebd6469 user: mistachkin tags: delegateFunction
Changes
Unified Diff Ignore Whitespace Patch
Changes to System.Data.SQLite/SQLiteConnection.cs.
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
    /// 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="callback">
    /// The <see cref="Delegate" /> object instance that implements the
    /// function to be bound.







    /// </param>
    public void BindFunction(
        SQLiteFunctionAttribute functionAttribute,

        Delegate callback
        )
    {
        CheckDisposed();

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

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

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

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







|
|
|
>
>
>
>
>
>
>



>
|









|







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
    /// 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 aggregate functions, this corresponds to the
    /// <see cref="SQLiteStepDelegate" /> callback.
    /// </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" /> callback.  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.
Changes to System.Data.SQLite/SQLiteFunction.cs.
736
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
            SQLiteFunctionAttribute at = arAtt[y] as SQLiteFunctionAttribute;

            if (at == null)
                continue;

            RegisterFunction(
                at.Name, at.Arguments, at.FuncType, at.InstanceType,
                at.Callback);
        }
    }

    /// <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="callback" />
    /// parameter is null.
    /// </param>
    /// <param name="callback">
    /// The <see cref="Delegate" /> that implements the function.  If




    /// this is non-null, the <paramref name="instanceType" /> parameter

    /// will be ignored when the function is invoked.
    /// </param>
    public static void RegisterFunction(
        string name,
        int argumentCount,
        FunctionType functionType,
        Type instanceType,

        Delegate callback
        )
    {
        SQLiteFunctionAttribute at = new SQLiteFunctionAttribute(
            name, argumentCount, functionType);

        at.InstanceType = instanceType;
        at.Callback = callback;


        _registeredFunctions.Add(at, null);
    }

    /// <summary>
    /// Creates a <see cref="SQLiteFunction" /> instance based on the specified
    /// <see cref="SQLiteFunctionAttribute" />.







|




















|
|

|
|
>
>
>
>
|
>
|






>
|






|
>







736
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
            SQLiteFunctionAttribute at = arAtt[y] as SQLiteFunctionAttribute;

            if (at == null)
                continue;

            RegisterFunction(
                at.Name, at.Arguments, at.FuncType, at.InstanceType,
                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.
    /// </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" />.
806
807
808
809
810
811
812

813
814
815
816

817
818
819
820
821
822
823
        )
    {
        if (functionAttribute == null)
        {
            function = null;
            return false;
        }

        else if (functionAttribute.Callback != null)
        {
            function = new SQLiteDelegateFunction(
                functionAttribute.Callback);


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







>
|


|
>







813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
        )
    {
        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);
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
  /// 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 the

      /// callback has not been set.

      /// </summary>
      private const string NoCallbackError = "No 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)
      {
          // do nothing.
      }

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

      /// <summary>
      /// Constructs an instance of this class using the specified
      /// <see cref="Delegate" /> as the <see cref="SQLiteFunction" />
      /// implementation.
      /// </summary>
      /// <param name="callback">
      /// The <see cref="Delegate" /> to be used for all calls into the


      /// virtual methods needed by the <see cref="SQLiteFunction" />
      /// class.




      /// </param>
      public SQLiteDelegateFunction(

          Delegate callback
          )
      {
          this.callback = callback;

      }
      #endregion

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

      #region Protected Methods
      /// <summary>







|
>
|
>

|

















|











|

>
>
|
|
>
>
>
>


>
|


|
>







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
  /// 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>
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425


1426










1427



1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
          return newArgs;
      }
      #endregion

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

      #region Public Properties
      private Delegate callback;
      /// <summary>
      /// The <see cref="Delegate" /> to be used for all calls into the


      /// virtual methods needed by the <see cref="SQLiteFunction" />










      /// class.



      /// </summary>
      public virtual Delegate Callback
      {
          get { return callback; }
          set { callback = value; }
      }
      #endregion

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

      #region System.Data.SQLite.SQLiteFunction Overrides
      /// <summary>







|


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

|

|
|







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
          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>
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
      /// <returns>
      /// The result of the scalar function.
      /// </returns>
      public override object Invoke(
          object[] args /* in */
          )
      {
          if (callback == null)

              throw new InvalidOperationException(NoCallbackError);



          SQLiteInvokeDelegate invokeDelegate =
              callback as SQLiteInvokeDelegate;

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

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

      /// <summary>







|
>
|
>
|
>

|







|







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
      /// <returns>
      /// The result of the scalar function.
      /// </returns>
      public override object Invoke(
          object[] args /* in */
          )
      {
          if (callback1 == null)
          {
              throw new InvalidOperationException(String.Format(
                  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>
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
      /// </param>
      public override void Step(
          object[] args,         /* in */
          int stepNumber,        /* in */
          ref object contextData /* in, out */
          )
      {
          if (callback == null)

              throw new InvalidOperationException(NoCallbackError);



          SQLiteStepDelegate stepDelegate = callback as SQLiteStepDelegate;

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

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

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

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








|
>
|
>
|
>
|












|







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
      /// </param>
      public override void Step(
          object[] args,         /* in */
          int stepNumber,        /* in */
          ref object contextData /* in, out */
          )
      {
          if (callback1 == null)
          {
              throw new InvalidOperationException(String.Format(
                  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);
          }
      }

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

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
      /// <returns>
      /// The result of the aggregate function.
      /// </returns>
      public override object Final(
          object contextData /* in */
          )
      {
          if (callback == null)

              throw new InvalidOperationException(NoCallbackError);



          SQLiteFinalDelegate finalDelegate = callback as SQLiteFinalDelegate;

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

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

      /// <summary>
      /// This virtual method is part of the implementation for collating







|
>
|
>
|
>
|



|



|
|







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
      /// <returns>
      /// The result of the aggregate function.
      /// </returns>
      public override object Final(
          object contextData /* in */
          )
      {
          if (callback2 == null)
          {
              throw new InvalidOperationException(String.Format(
                  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
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
      /// equal.
      /// </returns>
      public override int Compare(
          string param1, /* in */
          string param2  /* in */
          )
      {
          if (callback == null)

              throw new InvalidOperationException(NoCallbackError);



          SQLiteCompareDelegate compareDelegate =
              callback as SQLiteCompareDelegate;

          if (compareDelegate != null)
          {
              return compareDelegate.Invoke("Compare", param1, param2);

          }
          else
          {
              object result = callback.DynamicInvoke(GetCompareArgs(
                  param1, param2, false)); /* throw */

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

              throw new InvalidOperationException(String.Format(
                  ResultInt32Error, "Compare"));







|
>
|
>
|
>

|



|
>



|







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
      /// equal.
      /// </returns>
      public override int Compare(
          string param1, /* in */
          string param2  /* in */
          )
      {
          if (callback1 == null)
          {
              throw new InvalidOperationException(String.Format(
                  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(String.Format(
                  ResultInt32Error, "Compare"));
Changes to System.Data.SQLite/SQLiteFunctionAttribute.cs.
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
  [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     _callback;


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







|
>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  [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(String.Empty, -1, FunctionType.Scalar)
    {
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
        FunctionType functionType
        )
    {
        _name = name;
        _argumentCount = argumentCount;
        _functionType = functionType;
        _instanceType = null;
        _callback = null;

    }

    /// <summary>
    /// The function's name as it will be used in SQLite command text.
    /// </summary>
    public string Name
    {







|
>







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        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
    {
83
84
85
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
      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 the <see cref="Callback" /> property
    /// value is 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 Callback
    {
        get { return _callback; }
        set { _callback = value; }











    }
  }
}







|
|












|

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



85
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
      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.
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

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

    return $result
  }







































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

    set name [lindex $args 0]





    switch -exact -- $name {
      Invoke {
        return $args
      }
      Step {
        set ctx [lindex $args 1]

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


        global aggregateData







        if {[info exists aggregateData($ctx)]} then {
          incr aggregateData($ctx)
        } else {
          set aggregateData($ctx) 1
        }
      }
      Final {
        set ctx [lindex $args 1]

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

        global aggregateData

        if {[info exists aggregateData($ctx)]} then {
          return $aggregateData($ctx)
        } else {
          error "missing aggregate context data"
        }
      }
      Compare {

        return [string compare -nocase [lindex $args 1] [lindex $args 2]]



      }
      default {
        error [appendArgs "unknown function callback \"" $name \"]
      }
    }
  }


















  setupDb [set fileName data-1.74.db]
} -body {
  sql execute $db "CREATE TABLE t1(x INTEGER);"
  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 BindFunction $attribute(1,$argumentCount) myFuncCallback


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






    $connection BindFunction $attribute(2,$argumentCount) myFuncCallback

  }

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






  $connection BindFunction $attribute(3,0) myFuncCallback


  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 $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 \







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








>
>
>
>


|


|

|
|


>
|
>
>
|
>
>
>
>
|
|

|



|





|

|
|





>
|
>
>
>






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



|

















>
>
>
>
|
>





>
>
>
>
>
|
>



|
>

>
>
>
>
|
>
















>
|







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

    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 \
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
          "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 $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}]




  set result
} -cleanup {
  cleanupDb $fileName

  freeDbConnection

  catch {object removecallback myFuncCallback}










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





  rename myFuncCallback ""


  rename getMyFuncArgs ""
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite} -result {}}



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

reportSQLiteResources $test_channel

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

runSQLiteTestFilesEpilogue
runSQLiteTestEpilogue
runTestEpilogue







>
|



>
>
>






|
>
>
>

>
>
>
>
>
>
|
|

>
>
>
>

>
>


|
>
>










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
          "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