System.Data.SQLite
Check-in [2a6efe65f7]
Not logged in

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

Overview
Comment:Add the UnbindFunctionsOnClose connection flag.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2a6efe65f788ce18befd0e5993673b1fc1cfbd65
User & Date: mistachkin 2015-07-17 02:01:51
Context
2015-07-17
02:05
Refine the changes made in the previous check-in. check-in: a0724d224c user: mistachkin tags: trunk
02:01
Add the UnbindFunctionsOnClose connection flag. check-in: 2a6efe65f7 user: mistachkin tags: trunk
01:08
Add UnbindFunction method to the SQLiteConnection class. check-in: 187e8cb03d user: mistachkin tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

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

62
63
64
65
66
67
68

69
70
71
72
73
74
75
...
225
226
227
228
229
230
231






232
233
234
235
236
237
238
...
806
807
808
809
810
811
812

813
814
815
816
817
818
819
#endif

    /// <summary>
    /// The opaque pointer returned to us by the sqlite provider
    /// </summary>
    protected internal SQLiteConnectionHandle _sql;
    protected string _fileName;

    protected bool _usePool;
    protected int _poolVersion;
    private int _cancelCount;

#if (NET_35 || NET_40 || NET_45 || NET_451) && !PLATFORM_COMPACTFRAMEWORK
    private bool _buildingSchema;
#endif
................................................................................
    // goes to the pool and is resurrected later, re-registered functions will overwrite the
    // previous functions.  The SQLiteFunctionCookieHandle will take care of freeing unmanaged
    // resources belonging to the previously-registered functions.
    internal override void Close(bool canThrow)
    {
      if (_sql != null)
      {






          if (!_sql.OwnHandle)
          {
              _sql = null;
              return;
          }

          if (_usePool)
................................................................................
      //       exception now.
      //
      if (_sql != null)
          throw new SQLiteException("connection handle is still active");

      _usePool = usePool;
      _fileName = strFilename;


      if (usePool)
      {
        _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);

        SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
            SQLiteConnectionEventType.OpenedFromPool, null, null,







>







 







>
>
>
>
>
>







 







>







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
...
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
#endif

    /// <summary>
    /// The opaque pointer returned to us by the sqlite provider
    /// </summary>
    protected internal SQLiteConnectionHandle _sql;
    protected string _fileName;
    protected SQLiteConnectionFlags _flags;
    protected bool _usePool;
    protected int _poolVersion;
    private int _cancelCount;

#if (NET_35 || NET_40 || NET_45 || NET_451) && !PLATFORM_COMPACTFRAMEWORK
    private bool _buildingSchema;
#endif
................................................................................
    // goes to the pool and is resurrected later, re-registered functions will overwrite the
    // previous functions.  The SQLiteFunctionCookieHandle will take care of freeing unmanaged
    // resources belonging to the previously-registered functions.
    internal override void Close(bool canThrow)
    {
      if (_sql != null)
      {
          if ((_flags & SQLiteConnectionFlags.UnbindFunctionsOnClose) ==
                SQLiteConnectionFlags.UnbindFunctionsOnClose)
          {
              SQLiteFunction.UnbindFunctions(this, _flags, false);
          }

          if (!_sql.OwnHandle)
          {
              _sql = null;
              return;
          }

          if (_usePool)
................................................................................
      //       exception now.
      //
      if (_sql != null)
          throw new SQLiteException("connection handle is still active");

      _usePool = usePool;
      _fileName = strFilename;
      _flags = connectionFlags;

      if (usePool)
      {
        _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);

        SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
            SQLiteConnectionEventType.OpenedFromPool, null, null,

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

143
144
145
146
147
148
149

150
151
152
153
154
155
156
      //       exception now.
      //
      if (_sql != null)
          throw new SQLiteException("connection handle is still active");

      _usePool = usePool;
      _fileName = strFilename;


      if (usePool)
      {
        _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);

        SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
            SQLiteConnectionEventType.OpenedFromPool, null, null,







>







143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
      //       exception now.
      //
      if (_sql != null)
          throw new SQLiteException("connection handle is still active");

      _usePool = usePool;
      _fileName = strFilename;
      _flags = connectionFlags;

      if (usePool)
      {
        _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);

        SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
            SQLiteConnectionEventType.OpenedFromPool, null, null,

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

1121
1122
1123
1124
1125
1126
1127






1128
1129
1130
1131
1132
1133
1134
      /// If an exception is caught when raising the
      /// <see cref="SQLiteConnection.Progress" /> event, the operation
      /// should be interrupted.  If this is not specified, the operation
      /// will simply continue.
      /// </summary>
      InterruptOnException = 0x80000000,







      /// <summary>
      /// When binding parameter values or returning column values, always
      /// treat them as though they were plain text (i.e. no numeric,
      /// date/time, or other conversions should be attempted).
      /// </summary>
      BindAndGetAllAsText = BindAllAsText | GetAllAsText,








>
>
>
>
>
>







1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
      /// If an exception is caught when raising the
      /// <see cref="SQLiteConnection.Progress" /> event, the operation
      /// should be interrupted.  If this is not specified, the operation
      /// will simply continue.
      /// </summary>
      InterruptOnException = 0x80000000,

      /// <summary>
      /// Attempt to unbind all functions provided by other managed assemblies
      /// when closing the connection.
      /// </summary>
      UnbindFunctionsOnClose = 0x100000000,

      /// <summary>
      /// When binding parameter values or returning column values, always
      /// treat them as though they were plain text (i.e. no numeric,
      /// date/time, or other conversions should be attempted).
      /// </summary>
      BindAndGetAllAsText = BindAllAsText | GetAllAsText,

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

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

    /// <summary>
    /// Called by SQLiteBase derived classes, this function unbinds all previously bound
    /// user-defined functions from a connection.
    /// </summary>
    /// <param name="sqlbase">The base object from which the functions are to be unbound.</param>
    /// <param name="flags">The flags associated with the parent connection object.</param>




    /// <returns>Non-zero if all previously bound user-defined functions were unbound.</returns>
    internal static bool UnbindFunctions(
        SQLiteBase sqlbase,
        SQLiteConnectionFlags flags

        )
    {
        if (sqlbase == null)
            return false;

        IDictionary<SQLiteFunctionAttribute, SQLiteFunction> lFunctions =
            sqlbase.Functions;

        if (lFunctions == null)
            return false;








        bool result = true;



        foreach (KeyValuePair<SQLiteFunctionAttribute, object> pair
                in _registeredFunctions)
        {
            SQLiteFunctionAttribute pr = pair.Key;

            if (pr == null)
                continue;

            SQLiteFunction f;

            if (!lFunctions.TryGetValue(pr, out f) ||
                (f == null) ||
                !UnbindFunction(sqlbase, pr, f, flags))
            {
                result = false;




















            }
        }

        return result;
    }

    /// <summary>







>
>
>
>



|
>











>
>
>
>
>
>
>


>
>
|
|
|
|

|
|

|

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







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
851
852
853
854
855
856
857
858
859
860
861
862

    /// <summary>
    /// Called by SQLiteBase derived classes, this function unbinds all previously bound
    /// user-defined functions from a connection.
    /// </summary>
    /// <param name="sqlbase">The base object from which the functions are to be unbound.</param>
    /// <param name="flags">The flags associated with the parent connection object.</param>
    /// <param name="registered">
    /// Non-zero to unbind all registered (known) functions -OR- zero to unbind all functions
    /// currently bound to the connection.
    /// </param>
    /// <returns>Non-zero if all previously bound user-defined functions were unbound.</returns>
    internal static bool UnbindFunctions(
        SQLiteBase sqlbase,
        SQLiteConnectionFlags flags,
        bool registered
        )
    {
        if (sqlbase == null)
            return false;

        IDictionary<SQLiteFunctionAttribute, SQLiteFunction> lFunctions =
            sqlbase.Functions;

        if (lFunctions == null)
            return false;

        //
        // NOTE: Need to use a copy in this method here because the underlying
        //       dictionary is modified by the UnbindFunction method.
        //
        lFunctions = new Dictionary<SQLiteFunctionAttribute, SQLiteFunction>(
            lFunctions);

        bool result = true;

        if (registered)
        {
            foreach (KeyValuePair<SQLiteFunctionAttribute, object> pair
                    in _registeredFunctions)
            {
                SQLiteFunctionAttribute pr = pair.Key;

                if (pr == null)
                    continue;

                SQLiteFunction f;

                if (!lFunctions.TryGetValue(pr, out f) ||
                    (f == null) ||
                    !UnbindFunction(sqlbase, pr, f, flags))
                {
                    result = false;
                }
            }
        }
        else
        {
            foreach (KeyValuePair<SQLiteFunctionAttribute, SQLiteFunction> pair
                    in lFunctions)
            {
                SQLiteFunctionAttribute pr = pair.Key;

                if (pr == null)
                    continue;

                SQLiteFunction f = pair.Value;

                if ((f == null) ||
                    !UnbindFunction(sqlbase, pr, f, flags))
                {
                    result = false;
                }
            }
        }

        return result;
    }

    /// <summary>

Changes to Tests/basic.eagle.

2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
....
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
....
3425
3426
3427
3428
3429
3430
3431


























































































































































3432
3433
3434
3435
3436
3437
3438

  unset -nocomplain dateTime db fileName
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite} -result {1 630874007980000000}}

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

runTest {test data-1.54 {bind function to a SQLiteConnection} -setup {
  set fileName data-1.54.db
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql { \
    SELECT MyRandom(); \
................................................................................
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite} -match regexp -result {^\{fts5: \d{4}-\d{2}-\d{2}\
\d{2}:\d{2}:\d{2} [0-9a-f]{40}\} \{\} \{\} \{\} \{\} \{\} \{rowid 3 x horse\
rowid 4 x house\}$}}

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

runTest {test data-1.72 {unbind function from a SQLiteConnection} -setup {
  set fileName data-1.72.db
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql { \
    SELECT MyRandom(); \
................................................................................
  cleanupDb $fileName

  unset -nocomplain result code results errors sql dataSource id fileName
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite compileCSharp} -match regexp -result \
[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
\{\} 0 \{\} 0 (?:-)?\d+ 0 True 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 \{\}$}]}

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








|







 







|







 







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







2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
....
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
....
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592

  unset -nocomplain dateTime db fileName
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite} -result {1 630874007980000000}}

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

runTest {test data-1.54 {bind function to a connection} -setup {
  set fileName data-1.54.db
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql { \
    SELECT MyRandom(); \
................................................................................
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite} -match regexp -result {^\{fts5: \d{4}-\d{2}-\d{2}\
\d{2}:\d{2}:\d{2} [0-9a-f]{40}\} \{\} \{\} \{\} \{\} \{\} \{rowid 3 x horse\
rowid 4 x house\}$}}

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

runTest {test data-1.72 {unbind function from a connection} -setup {
  set fileName data-1.72.db
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql { \
    SELECT MyRandom(); \
................................................................................
  cleanupDb $fileName

  unset -nocomplain result code results errors sql dataSource id fileName
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite compileCSharp} -match regexp -result \
[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
\{\} 0 \{\} 0 (?:-)?\d+ 0 True 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.73 {unbind functions from a connection on close} -setup {
  set fileName data-1.73.db
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql { \
    SELECT MyRandom(); \
  }

  unset -nocomplain results errors

  set code [compileCSharpWith [subst {
    using System;
    using System.Data.SQLite;

    namespace _Dynamic${id}
    {
      public class Test${id} : SQLiteFunction
      {
        private Random random;
        private static SQLiteFunctionAttribute functionAttribute;
        private static SQLiteConnection connection;

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

        public Test${id}()
        {
          random = new Random();
        }

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

        public override object Invoke(
          object\[\] args
          )
        {
          return random.Next();
        }

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

        private static void Initialize()
        {
          if (functionAttribute == null)
          {
            functionAttribute = new SQLiteFunctionAttribute(
                "MyRandom", 0, FunctionType.Scalar);
          }

          if (connection == null)
          {
            connection = new SQLiteConnection(
                "Data Source=${dataSource};Pooling=True;" +
                "[getFlagsProperty UnbindFunctionsOnClose]");

            connection.Open();
          }
        }

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

        public static void BindFunction()
        {
          Initialize();

          connection.BindFunction(functionAttribute, new Test${id}());
        }

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

        public static object CallFunction()
        {
          Initialize();

          using (SQLiteCommand command = new SQLiteCommand("${sql}",
              connection))
          {
            return command.ExecuteScalar();
          }
        }

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

        public static void CloseAndReopen()
        {
          Initialize();

          connection.Close();
          connection.Open();
        }

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

        public static void Uninitialize()
        {
          if (connection != null)
          {
            connection.Close();
            connection = null;
          }

          if (functionAttribute != null)
            functionAttribute = null;
        }

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

        public static void Main()
        {
          // do nothing.
        }
      }
    }
  }] true true true results errors System.Data.SQLite.dll]

  list $code $results \
      [expr {[info exists errors] ? $errors : ""}] \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} BindFunction
      } result] : [set result ""]}] $result \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} CallFunction
      } result] : [set result ""]}] $result \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} CloseAndReopen
      } result] : [set result ""]}] $result \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} CallFunction
      } result] : [set result ""]}] $result \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} BindFunction
      } result] : [set result ""]}] $result \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} CallFunction
      } result] : [set result ""]}] $result \
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} Uninitialize
      } result] : [set result ""]}] $result
} -cleanup {
  cleanupDb $fileName

  unset -nocomplain result code results errors sql dataSource id fileName
} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
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 \{\}$}]}

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