System.Data.SQLite

Check-in [b7ba6996c1]
Login

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

Overview
Comment:Add more database connection configuration options for the sqlite3_db_config() interface.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b7ba6996c1b59962482b171a4996684d571a2674
User & Date: mistachkin 2018-02-26 17:25:56.081
References
2018-02-26
19:17
Add tests for the database option values added via check-in [b7ba6996c1]. check-in: b3872ff42e user: mistachkin tags: trunk
Context
2018-02-26
17:32
Set HResult property of SQLiteException based on the SQLite core library error code. check-in: a4575fc8e7 user: mistachkin tags: trunk
17:25
Add more database connection configuration options for the sqlite3_db_config() interface. check-in: b7ba6996c1 user: mistachkin tags: trunk
15:10
Fix typo in the MSBuild targets file used by the NuGet packages. check-in: 7f37c83ffd user: mistachkin tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to System.Data.SQLite/SQLite3.cs.
43
44
45
46
47
48
49









50
51
52
53
54
55
56
  /// <summary>
  /// This class implements SQLiteBase completely, and is the guts of the code that interop's SQLite with .NET
  /// </summary>
  internal class SQLite3 : SQLiteBase
  {
    private static object syncRoot = new object();










    //
    // NOTE: This is the public key for the System.Data.SQLite assembly.  If you change the
    //       SNK file, you will need to change this as well.
    //
    internal const string PublicKey =
        "002400000480000094000000060200000024000052534131000400000100010005a288de5687c4e1" +
        "b621ddff5d844727418956997f475eb829429e411aff3e93f97b70de698b972640925bdd44280df0" +







>
>
>
>
>
>
>
>
>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  /// <summary>
  /// This class implements SQLiteBase completely, and is the guts of the code that interop's SQLite with .NET
  /// </summary>
  internal class SQLite3 : SQLiteBase
  {
    private static object syncRoot = new object();

    /// <summary>
    /// This field is used to refer to memory allocated for the
    /// SQLITE_DBCONFIG_MAINDBNAME value used with the native
    /// "sqlite3_db_config" API.  If allocated, the associated
    /// memeory will be freed when the underlying connection is
    /// closed.
    /// </summary>
    private IntPtr dbName = IntPtr.Zero;

    //
    // NOTE: This is the public key for the System.Data.SQLite assembly.  If you change the
    //       SNK file, you will need to change this as well.
    //
    internal const string PublicKey =
        "002400000480000094000000060200000024000052534131000400000100010005a288de5687c4e1" +
        "b621ddff5d844727418956997f475eb829429e411aff3e93f97b70de698b972640925bdd44280df0" +
359
360
361
362
363
364
365


366
367
368
369
370
371
372
                          "UnbindFunctions Failure: {0}",
                          HandleToString()));
#endif
                  }
              }

              _sql.Dispose();


          }
          _sql = null;
      }
    }

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








>
>







368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
                          "UnbindFunctions Failure: {0}",
                          HandleToString()));
#endif
                  }
              }

              _sql.Dispose();

              FreeDbName(!disposing);
          }
          _sql = null;
      }
    }

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

2756
2757
2758
2759
2760
2761
2762














































2763
2764
2765
2766
2767
2768
2769
2770

2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786

2787


2788




2789
2790

2791
2792


2793





































































































2794
2795








2796
2797
2798
2799
2800
2801
2802
                pName = IntPtr.Zero;
            }
        }
    }
#endif

    /// <summary>














































    /// Enables or disables a configuration option for the database.
    /// connection.
    /// </summary>
    /// <param name="option">
    /// The database configuration option to enable or disable.
    /// </param>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.

    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal override SQLiteErrorCode SetConfigurationOption(
        SQLiteConfigDbOpsEnum option,
        bool bOnOff
        )
    {
        if ((option < SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FKEY) ||
            (option > SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION))
        {
            throw new SQLiteException(HelperMethods.StringFormat(
                CultureInfo.CurrentCulture,
                "unsupported configuration option, must be: {0}, {1}, {2}, or {3}",
                SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FKEY,

                SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_TRIGGER,


                SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER,




                SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION));
        }


        int result = 0; /* NOT USED */








































































































        return UnsafeNativeMethods.sqlite3_db_config_int_refint(
            _sql, option, (bOnOff ? 1 : 0), ref result);








    }

    /// <summary>
    /// Enables or disables extension loading by SQLite.
    /// </summary>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.







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



|

|
<
>






|


|
<



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

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







2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826

2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837

2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855

2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
                pName = IntPtr.Zero;
            }
        }
    }
#endif

    /// <summary>
    /// Builds an error message string fragment containing the
    /// defined values of the <see cref="SQLiteConfigDbOpsEnum" />
    /// enumeration.
    /// </summary>
    /// <returns>
    /// The built string fragment.
    /// </returns>
    private static string GetConfigDbOpsNames()
    {
        StringBuilder builder = new StringBuilder();

#if !PLATFORM_COMPACTFRAMEWORK
        foreach (string name in Enum.GetNames(
                typeof(SQLiteConfigDbOpsEnum)))
        {
            if (String.IsNullOrEmpty(name))
                continue;

            if (builder.Length > 0)
                builder.Append(", ");

            builder.Append(name);
        }
#else
        //
        // TODO: Update this list if the available values in the
        //       "SQLiteConfigDbOpsEnum" enumeration change.
        //
        builder.AppendFormat(CultureInfo.InvariantCulture,
            "{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}",
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NONE,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_MAINDBNAME,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_LOOKASIDE,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FKEY,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_TRIGGER,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_QPSG,
            SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_TRIGGER_EQP);
#endif

        return builder.ToString();
    }

    /// <summary>
    /// Change a configuration option value for the database.
    /// connection.
    /// </summary>
    /// <param name="option">
    /// The database configuration option to change.
    /// </param>
    /// <param name="value">

    /// The new value for the specified configuration option.
    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal override SQLiteErrorCode SetConfigurationOption(
        SQLiteConfigDbOpsEnum option,
        object value
        )
    {
        if (!Enum.IsDefined(typeof(SQLiteConfigDbOpsEnum), option))

        {
            throw new SQLiteException(HelperMethods.StringFormat(
                CultureInfo.CurrentCulture,
                "unrecognized configuration option, must be: {0}",
                GetConfigDbOpsNames()));
        }

        switch (option)
        {
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NONE: // nil
                {
                    //
                    // NOTE: Do nothing, return success.
                    //
                    return SQLiteErrorCode.Ok;
                }
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_MAINDBNAME: // char*
                {

                    if (value == null)
                        throw new ArgumentNullException("value");

                    if (!(value is string))
                    {
                        throw new SQLiteException(HelperMethods.StringFormat(
                            CultureInfo.CurrentCulture,
                            "configuration value type mismatch, must be of type {0}",
                            typeof(string)));
                    }

                    SQLiteErrorCode rc = SQLiteErrorCode.Ok;
                    IntPtr pDbName = IntPtr.Zero;

                    try
                    {
                        pDbName = SQLiteString.Utf8IntPtrFromString(
                            (string)value);

                        if (pDbName == IntPtr.Zero)
                        {
                            throw new SQLiteException(
                                SQLiteErrorCode.NoMem,
                                "cannot allocate database name");
                        }

                        rc = UnsafeNativeMethods.sqlite3_db_config_charptr(
                            _sql, option, pDbName);

                        if (rc == SQLiteErrorCode.Ok)
                        {
                            FreeDbName(true);
                            dbName = pDbName;
                        }
                    }
                    finally
                    {
                        if ((rc != SQLiteErrorCode.Ok) &&
                            (pDbName != IntPtr.Zero))
                        {
                            SQLiteMemory.Free(pDbName);
                            pDbName = IntPtr.Zero;
                        }
                    }

                    return rc;
                }
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_LOOKASIDE: // void* int int
                {
                    object[] array = value as object[];

                    if (array == null)
                    {
                        throw new SQLiteException(HelperMethods.StringFormat(
                            CultureInfo.CurrentCulture,
                            "configuration value type mismatch, must be of type {0}",
                            typeof(object[])));
                    }

                    if (!(array[0] is IntPtr))
                    {
                        throw new SQLiteException(HelperMethods.StringFormat(
                            CultureInfo.CurrentCulture,
                            "configuration element zero (0) type mismatch, must be of type {0}",
                            typeof(IntPtr)));
                    }

                    if (!(array[1] is int))
                    {
                        throw new SQLiteException(HelperMethods.StringFormat(
                            CultureInfo.CurrentCulture,
                            "configuration element one (1) type mismatch, must be of type {0}",
                            typeof(int)));
                    }

                    if (!(array[2] is int))
                    {
                        throw new SQLiteException(HelperMethods.StringFormat(
                            CultureInfo.CurrentCulture,
                            "configuration element two (2) type mismatch, must be of type {0}",
                            typeof(int)));
                    }

                    return UnsafeNativeMethods.sqlite3_db_config_intptr_two_ints(
                        _sql, option, (IntPtr)array[0], (int)array[1], (int)array[2]);
                }
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FKEY: // int int*
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_TRIGGER: // int int*
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: // int int*
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: // int int*
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: // int int*
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_QPSG: // int int*
            case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_TRIGGER_EQP: // int int*
                {
                    if (!(value is bool))
                    {
                        throw new SQLiteException(HelperMethods.StringFormat(
                            CultureInfo.CurrentCulture,
                            "configuration value type mismatch, must be of type {0}",
                            typeof(bool)));
                    }

                    int result = 0; /* NOT USED */

                    return UnsafeNativeMethods.sqlite3_db_config_int_refint(
                        _sql, option, ((bool)value ? 1 : 0), ref result);
                }
            default:
                {
                    throw new SQLiteException(HelperMethods.StringFormat(
                        CultureInfo.CurrentCulture,
                        "unsupported configuration option {0}", option));
                }
        }
    }

    /// <summary>
    /// Enables or disables extension loading by SQLite.
    /// </summary>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.
3018
3019
3020
3021
3022
3023
3024

3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
    /// This method attempts to cause the SQLite native library to invalidate
    /// its function pointers that refer to this instance.  This is necessary
    /// to prevent calls from native code into delegates that may have been
    /// garbage collected.  Normally, these types of issues can only arise for
    /// connections that are added to the pool; howver, it is good practice to
    /// unconditionally invalidate function pointers that may refer to objects
    /// being disposed.

    /// <param name="includeGlobal">
    /// Non-zero to also invalidate global function pointers (i.e. those that
    /// are not directly associated with this connection on the native side).
    /// </param>
    /// <param name="canThrow">
    /// Non-zero if this method is being executed within a context where it can
    /// throw an exception in the event of failure; otherwise, zero.
    /// </param>
    /// </summary>
    /// <returns>
    /// Non-zero if this method was successful; otherwise, zero.
    /// </returns>
    private bool UnhookNativeCallbacks(
        bool includeGlobal,
        bool canThrow
        )







>








<







3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207

3208
3209
3210
3211
3212
3213
3214
    /// This method attempts to cause the SQLite native library to invalidate
    /// its function pointers that refer to this instance.  This is necessary
    /// to prevent calls from native code into delegates that may have been
    /// garbage collected.  Normally, these types of issues can only arise for
    /// connections that are added to the pool; howver, it is good practice to
    /// unconditionally invalidate function pointers that may refer to objects
    /// being disposed.
    /// </summary>
    /// <param name="includeGlobal">
    /// Non-zero to also invalidate global function pointers (i.e. those that
    /// are not directly associated with this connection on the native side).
    /// </param>
    /// <param name="canThrow">
    /// Non-zero if this method is being executed within a context where it can
    /// throw an exception in the event of failure; otherwise, zero.
    /// </param>

    /// <returns>
    /// Non-zero if this method was successful; otherwise, zero.
    /// </returns>
    private bool UnhookNativeCallbacks(
        bool includeGlobal,
        bool canThrow
        )
3316
3317
3318
3319
3320
3321
3322






















































3323
3324
3325
3326
3327
3328
3329
        ///////////////////////////////////////////////////////////////////////////////////////////

        if (!result && canThrow)
            throw new SQLiteException(rc, builder.ToString());

        return result;
    }























































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

    /// <summary>
    /// Creates a new SQLite backup object based on the provided destination
    /// database connection.  The source database connection is the one
    /// associated with this object.  The source and destination database







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







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

        if (!result && canThrow)
            throw new SQLiteException(rc, builder.ToString());

        return result;
    }

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

    /// <summary>
    /// This method attempts to free the cached database name used with the
    /// <see cref="SetConfigurationOption" /> method.
    /// </summary>
    /// <param name="canThrow">
    /// Non-zero if this method is being executed within a context where it can
    /// throw an exception in the event of failure; otherwise, zero.
    /// </param>
    /// <returns>
    /// Non-zero if this method was successful; otherwise, zero.
    /// </returns>
    private bool FreeDbName(
        bool canThrow
        )
    {
        try
        {
            if (dbName != IntPtr.Zero)
            {
                SQLiteMemory.Free(dbName);
                dbName = IntPtr.Zero;
            }

            return true;
        }
#if !NET_COMPACT_20 && TRACE_CONNECTION
        catch (Exception e)
#else
        catch (Exception)
#endif
        {
#if !NET_COMPACT_20 && TRACE_CONNECTION
            try
            {
                Trace.WriteLine(HelperMethods.StringFormat(
                    CultureInfo.CurrentCulture,
                    "Failed to free database name: {0}",
                    e)); /* throw */
            }
            catch
            {
                // do nothing.
            }
#endif

            if (canThrow)
                throw;
        }

        return false;
    }

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

    /// <summary>
    /// Creates a new SQLite backup object based on the provided destination
    /// database connection.  The source database connection is the one
    /// associated with this object.  The source and destination database
Changes to System.Data.SQLite/SQLiteBase.cs.
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384

385
386
387
388
389
390
391
392
393
394
395
396
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal abstract SQLiteErrorCode DeclareVirtualFunction(SQLiteModule module, int argumentCount, string name, ref string error);
#endif

    /// <summary>
    /// Enables or disables a configuration option for the database.
    /// connection.
    /// </summary>
    /// <param name="option">
    /// The database configuration option to enable or disable.
    /// </param>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.

    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal abstract SQLiteErrorCode SetConfigurationOption(SQLiteConfigDbOpsEnum option, bool bOnOff);
    /// <summary>
    /// Enables or disables extension loading by SQLite.
    /// </summary>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.
    /// </param>
    internal abstract void SetLoadExtension(bool bOnOff);







|
<


|

|
<
>




|







370
371
372
373
374
375
376
377

378
379
380
381
382

383
384
385
386
387
388
389
390
391
392
393
394
395
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal abstract SQLiteErrorCode DeclareVirtualFunction(SQLiteModule module, int argumentCount, string name, ref string error);
#endif

    /// <summary>
    /// Change a configuration option value for the database.

    /// </summary>
    /// <param name="option">
    /// The database configuration option to change.
    /// </param>
    /// <param name="value">

    /// The new value for the specified configuration option.
    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal abstract SQLiteErrorCode SetConfigurationOption(SQLiteConfigDbOpsEnum option, object value);
    /// <summary>
    /// Enables or disables extension loading by SQLite.
    /// </summary>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.
    /// </param>
    internal abstract void SetLoadExtension(bool bOnOff);
1376
1377
1378
1379
1380
1381
1382
1383




1384








1385
1386
1387
1388
1389
1390
1391
  {
    /// <summary>
    /// This value represents an unknown (or invalid) option, do not use it.
    /// </summary>
    SQLITE_DBCONFIG_NONE = 0, // nil

    /// <summary>
    /// This option is not currently supported by System.Data.SQLite.  It




    /// may be supported in the future.








    /// </summary>
    SQLITE_DBCONFIG_LOOKASIDE = 1001, // void* int int

    /// <summary>
    /// This option is used to enable or disable the enforcement of
    /// foreign key constraints.
    /// </summary>







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







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
  {
    /// <summary>
    /// This value represents an unknown (or invalid) option, do not use it.
    /// </summary>
    SQLITE_DBCONFIG_NONE = 0, // nil

    /// <summary>
    /// This option is used to change the name of the "main" database
    /// schema.  The sole argument is a pointer to a constant UTF8 string
    /// which will become the new schema name in place of "main".
    /// </summary>
    SQLITE_DBCONFIG_MAINDBNAME = 1000, // char*

    /// <summary>
    /// This option is used to configure the lookaside memory allocator.
    /// The value must be an array with three elements.  The second element
    /// must be an <see cref="Int32" /> containing the size of each buffer
    /// slot.  The third element must be an <see cref="Int32" /> containing
    /// the number of slots.  The first element must be an <see cref="IntPtr" />
    /// that points to a native memory buffer of bytes equal to or greater
    /// than the product of the second and third element values.
    /// </summary>
    SQLITE_DBCONFIG_LOOKASIDE = 1001, // void* int int

    /// <summary>
    /// This option is used to enable or disable the enforcement of
    /// foreign key constraints.
    /// </summary>
1402
1403
1404
1405
1406
1407
1408
1409


















1410
1411
1412
1413
1414
1415
1416
    /// search engine extension.
    /// </summary>
    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, // int int*

    /// <summary>
    /// This option is used to enable or disable the loading of extensions.
    /// </summary>
    SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005 // int int*


















  }

  // These are the options to the internal sqlite3_config call.
  internal enum SQLiteConfigOpsEnum
  {
    SQLITE_CONFIG_NONE = 0, // nil
    SQLITE_CONFIG_SINGLETHREAD = 1, // nil







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







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
    /// search engine extension.
    /// </summary>
    SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, // int int*

    /// <summary>
    /// This option is used to enable or disable the loading of extensions.
    /// </summary>
    SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005, // int int*

    /// <summary>
    /// This option is used to enable or disable the automatic checkpointing
    /// when a WAL database is closed.
    /// </summary>
    SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006, // int int*

    /// <summary>
    /// This option is used to enable or disable the query planner stability
    /// guarantee (QPSG).
    /// </summary>
    SQLITE_DBCONFIG_ENABLE_QPSG = 1007, // int int*

    /// <summary>
    /// This option is used to enable or disable the extra EXPLAIN QUERY PLAN
    /// output for trigger programs.
    /// </summary>
    SQLITE_DBCONFIG_TRIGGER_EQP = 1008 // int int*
  }

  // These are the options to the internal sqlite3_config call.
  internal enum SQLiteConfigOpsEnum
  {
    SQLITE_CONFIG_NONE = 0, // nil
    SQLITE_CONFIG_SINGLETHREAD = 1, // nil
Changes to System.Data.SQLite/SQLiteConnection.cs.
1578
1579
1580
1581
1582
1583
1584

1585
1586
1587
1588
1589
1590
1591

1592
1593
1594
1595
1596
1597
1598

    /// <summary>
    /// The default busy timeout to use with the SQLite core library.  This is
    /// only used when opening a connection.
    /// </summary>
    private int _busyTimeout = DefaultBusyTimeout;


    /// <summary>
    /// The default wait timeout to use with <see cref="WaitForEnlistmentReset" />
    /// method.  This is only used when waiting for the enlistment to be reset
    /// prior to enlisting in a transaction, and then only when the appropriate
    /// connection flag is set.
    /// </summary>
    private int _waitTimeout = DefaultWaitTimeout;


    /// <summary>
    /// The maximum number of retries when preparing SQL to be executed.  This
    /// normally only applies to preparation errors resulting from the database
    /// schema being changed.
    /// </summary>
    internal int _prepareRetries = DefaultPrepareRetries;







>







>







1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600

    /// <summary>
    /// The default busy timeout to use with the SQLite core library.  This is
    /// only used when opening a connection.
    /// </summary>
    private int _busyTimeout = DefaultBusyTimeout;

#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// The default wait timeout to use with <see cref="WaitForEnlistmentReset" />
    /// method.  This is only used when waiting for the enlistment to be reset
    /// prior to enlisting in a transaction, and then only when the appropriate
    /// connection flag is set.
    /// </summary>
    private int _waitTimeout = DefaultWaitTimeout;
#endif

    /// <summary>
    /// The maximum number of retries when preparing SQL to be executed.  This
    /// normally only applies to preparation errors resulting from the database
    /// schema being changed.
    /// </summary>
    internal int _prepareRetries = DefaultPrepareRetries;
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
                strictEnlistment);

            OnChanged(this, new ConnectionEventArgs(
                SQLiteConnectionEventType.EnlistTransaction, null, null, null, null,
                null, null, new object[] { _enlistment }));
        }
    }



    /// <summary>
    /// <b>EXPERIMENTAL</b> --
    /// Waits for the enlistment associated with this connection to be reset.


    /// </summary>
    /// <param name="timeoutMilliseconds">
    /// The approximate maximum number of milliseconds to wait before timing
    /// out the wait operation.
    /// </param>
    /// <returns>
    /// Non-zero if the enlistment assciated with this connection was reset;
    /// otherwise, zero.  It should be noted that this method returning a
    /// non-zero value does not necessarily guarantee that the connection
    /// can enlist in a new transaction (i.e. due to potentical race with
    /// other threads); therefore, callers should generally use try/catch
    /// when calling the <see cref="EnlistTransaction" /> method.
    /// </returns>




















    public bool WaitForEnlistmentReset(
        int timeoutMilliseconds
        )
    {
        CheckDisposed();


        if (timeoutMilliseconds < 0)
            throw new ArgumentException("timeout cannot be negative");

        const int defaultMilliseconds = 100;
        int sleepMilliseconds;

        if (timeoutMilliseconds == 0)







>

>



>
>













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






>







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
                strictEnlistment);

            OnChanged(this, new ConnectionEventArgs(
                SQLiteConnectionEventType.EnlistTransaction, null, null, null, null,
                null, null, new object[] { _enlistment }));
        }
    }
#endif

#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// <b>EXPERIMENTAL</b> --
    /// Waits for the enlistment associated with this connection to be reset.
    /// This method always throws <see cref="NotImplementedException" /> when
    /// running on the .NET Compact Framework.
    /// </summary>
    /// <param name="timeoutMilliseconds">
    /// The approximate maximum number of milliseconds to wait before timing
    /// out the wait operation.
    /// </param>
    /// <returns>
    /// Non-zero if the enlistment assciated with this connection was reset;
    /// otherwise, zero.  It should be noted that this method returning a
    /// non-zero value does not necessarily guarantee that the connection
    /// can enlist in a new transaction (i.e. due to potentical race with
    /// other threads); therefore, callers should generally use try/catch
    /// when calling the <see cref="EnlistTransaction" /> method.
    /// </returns>
#else
    /// <summary>
    /// <b>EXPERIMENTAL</b> --
    /// Waits for the enlistment associated with this connection to be reset.
    /// This method always throws <see cref="NotImplementedException" /> when
    /// running on the .NET Compact Framework.
    /// </summary>
    /// <param name="timeoutMilliseconds">
    /// The approximate maximum number of milliseconds to wait before timing
    /// out the wait operation.
    /// </param>
    /// <returns>
    /// Non-zero if the enlistment assciated with this connection was reset;
    /// otherwise, zero.  It should be noted that this method returning a
    /// non-zero value does not necessarily guarantee that the connection
    /// can enlist in a new transaction (i.e. due to potentical race with
    /// other threads); therefore, callers should generally use try/catch
    /// when calling the EnlistTransaction method.
    /// </returns>
#endif
    public bool WaitForEnlistmentReset(
        int timeoutMilliseconds
        )
    {
        CheckDisposed();

#if !PLATFORM_COMPACTFRAMEWORK
        if (timeoutMilliseconds < 0)
            throw new ArgumentException("timeout cannot be negative");

        const int defaultMilliseconds = 100;
        int sleepMilliseconds;

        if (timeoutMilliseconds == 0)
3583
3584
3585
3586
3587
3588
3589
3590


3591

3592
3593
3594
3595
3596
3597
3598
            }

            //
            // NOTE: Sleep for a bit and then try again.
            //
            Thread.Sleep(sleepMilliseconds);
        }
    }


#endif


    /// <summary>
    /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value
    /// </summary>
    /// <param name="items">The list to look in</param>
    /// <param name="key">The key to find</param>
    /// <param name="defValue">The default value to return if the key is not found</param>







<
>
>

>







3610
3611
3612
3613
3614
3615
3616

3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
            }

            //
            // NOTE: Sleep for a bit and then try again.
            //
            Thread.Sleep(sleepMilliseconds);
        }

#else
        throw new NotImplementedException();
#endif
    }

    /// <summary>
    /// Looks for a key in the array of key/values of the parameter string.  If not found, return the specified default value
    /// </summary>
    /// <param name="items">The list to look in</param>
    /// <param name="key">The key to find</param>
    /// <param name="defValue">The default value to return if the key is not found</param>
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
            result = 0;
            return false;
        }
#endif
    }

    /// <summary>
    /// Enables or disables a configuration option for the database.
    /// </summary>
    /// <param name="option">
    /// The database configuration option to enable or disable.
    /// </param>
    /// <param name="enable">
    /// True to enable loading of extensions, false to disable.
    /// </param>
    public void SetConfigurationOption(
        SQLiteConfigDbOpsEnum option,
        bool enable
        )
    {
        CheckDisposed();

        if (_sql == null)
        {
            throw new InvalidOperationException(HelperMethods.StringFormat(
                CultureInfo.CurrentCulture,
                "Database connection not valid for {0} a configuration option.",
                enable ? "enabling" : "disabling"));
        }

        if ((option == SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION) &&
            ((_flags & SQLiteConnectionFlags.NoLoadExtension) == SQLiteConnectionFlags.NoLoadExtension))
        {
            throw new SQLiteException("Loading extensions is disabled for this database connection.");
        }

        _sql.SetConfigurationOption(option, enable);
    }

    /// <summary>
    /// Enables or disabled extension loading.
    /// </summary>
    /// <param name="enable">
    /// True to enable loading of extensions, false to disable.







|


|

|
|



|






|
<
|
<








|







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
            result = 0;
            return false;
        }
#endif
    }

    /// <summary>
    /// Change a configuration option value for the database.
    /// </summary>
    /// <param name="option">
    /// The database configuration option to change.
    /// </param>
    /// <param name="value">
    /// The new value for the specified configuration option.
    /// </param>
    public void SetConfigurationOption(
        SQLiteConfigDbOpsEnum option,
        object value
        )
    {
        CheckDisposed();

        if (_sql == null)
        {
            throw new InvalidOperationException(

                "Database connection not valid for changing a configuration option.");

        }

        if ((option == SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION) &&
            ((_flags & SQLiteConnectionFlags.NoLoadExtension) == SQLiteConnectionFlags.NoLoadExtension))
        {
            throw new SQLiteException("Loading extensions is disabled for this database connection.");
        }

        _sql.SetConfigurationOption(option, value);
    }

    /// <summary>
    /// Enables or disabled extension loading.
    /// </summary>
    /// <param name="enable">
    /// True to enable loading of extensions, false to disable.
4144
4145
4146
4147
4148
4149
4150


4151


4152
4153
4154
4155
4156
4157
4158
      try
      {
        bool usePooling = SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", GetDefaultPooling().ToString()));
        int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", SQLiteConvert.ToString(DefaultMaxPoolSize)), CultureInfo.InvariantCulture);

        _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", SQLiteConvert.ToString(DefaultConnectionTimeout)), CultureInfo.InvariantCulture);
        _busyTimeout = Convert.ToInt32(FindKey(opts, "BusyTimeout", SQLiteConvert.ToString(DefaultBusyTimeout)), CultureInfo.InvariantCulture);


        _waitTimeout = Convert.ToInt32(FindKey(opts, "WaitTimeout", SQLiteConvert.ToString(DefaultWaitTimeout)), CultureInfo.InvariantCulture);


        _prepareRetries = Convert.ToInt32(FindKey(opts, "PrepareRetries", SQLiteConvert.ToString(DefaultPrepareRetries)), CultureInfo.InvariantCulture);
        _progressOps = Convert.ToInt32(FindKey(opts, "ProgressOps", SQLiteConvert.ToString(DefaultProgressOps)), CultureInfo.InvariantCulture);

        enumValue = TryParseEnum(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", DefaultIsolationLevel.ToString()), true);
        _defaultIsolation = (enumValue is IsolationLevel) ? (IsolationLevel)enumValue : DefaultIsolationLevel;
        _defaultIsolation = GetEffectiveIsolationLevel(_defaultIsolation);








>
>

>
>







4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
      try
      {
        bool usePooling = SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", GetDefaultPooling().ToString()));
        int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", SQLiteConvert.ToString(DefaultMaxPoolSize)), CultureInfo.InvariantCulture);

        _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", SQLiteConvert.ToString(DefaultConnectionTimeout)), CultureInfo.InvariantCulture);
        _busyTimeout = Convert.ToInt32(FindKey(opts, "BusyTimeout", SQLiteConvert.ToString(DefaultBusyTimeout)), CultureInfo.InvariantCulture);

#if !PLATFORM_COMPACTFRAMEWORK
        _waitTimeout = Convert.ToInt32(FindKey(opts, "WaitTimeout", SQLiteConvert.ToString(DefaultWaitTimeout)), CultureInfo.InvariantCulture);
#endif

        _prepareRetries = Convert.ToInt32(FindKey(opts, "PrepareRetries", SQLiteConvert.ToString(DefaultPrepareRetries)), CultureInfo.InvariantCulture);
        _progressOps = Convert.ToInt32(FindKey(opts, "ProgressOps", SQLiteConvert.ToString(DefaultProgressOps)), CultureInfo.InvariantCulture);

        enumValue = TryParseEnum(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", DefaultIsolationLevel.ToString()), true);
        _defaultIsolation = (enumValue is IsolationLevel) ? (IsolationLevel)enumValue : DefaultIsolationLevel;
        _defaultIsolation = GetEffectiveIsolationLevel(_defaultIsolation);

4421
4422
4423
4424
4425
4426
4427

4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439

4440
4441
4442
4443
4444
4445
4446
    /// </summary>
    public int BusyTimeout
    {
        get { CheckDisposed(); return _busyTimeout; }
        set { CheckDisposed(); _busyTimeout = value; }
    }


    /// <summary>
    /// <b>EXPERIMENTAL</b> --
    /// The wait timeout to use with <see cref="WaitForEnlistmentReset" /> method.
    /// This is only used when waiting for the enlistment to be reset prior to
    /// enlisting in a transaction, and then only when the appropriate connection
    /// flag is set.
    /// </summary>
    public int WaitTimeout
    {
        get { CheckDisposed(); return _waitTimeout; }
        set { CheckDisposed(); _waitTimeout = value; }
    }


    /// <summary>
    /// The maximum number of retries when preparing SQL to be executed.  This
    /// normally only applies to preparation errors resulting from the database
    /// schema being changed.
    /// </summary>
    public int PrepareRetries







>












>







4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
    /// </summary>
    public int BusyTimeout
    {
        get { CheckDisposed(); return _busyTimeout; }
        set { CheckDisposed(); _busyTimeout = value; }
    }

#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// <b>EXPERIMENTAL</b> --
    /// The wait timeout to use with <see cref="WaitForEnlistmentReset" /> method.
    /// This is only used when waiting for the enlistment to be reset prior to
    /// enlisting in a transaction, and then only when the appropriate connection
    /// flag is set.
    /// </summary>
    public int WaitTimeout
    {
        get { CheckDisposed(); return _waitTimeout; }
        set { CheckDisposed(); _waitTimeout = value; }
    }
#endif

    /// <summary>
    /// The maximum number of retries when preparing SQL to be executed.  This
    /// normally only applies to preparation errors resulting from the database
    /// schema being changed.
    /// </summary>
    public int PrepareRetries
Changes to System.Data.SQLite/UnsafeNativeMethods.cs.
3840
3841
3842
3843
3844
3845
3846







3847
3848
3849
3850







3851
3852
3853
3854
3855
3856
3857
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config")]
#endif
    internal static extern SQLiteErrorCode sqlite3_config_log(SQLiteConfigOpsEnum op, SQLiteLogCallback func, IntPtr pvUser);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config", CallingConvention = CallingConvention.Cdecl)]
#else







    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config")]
#endif
    internal static extern SQLiteErrorCode sqlite3_db_config_int_refint(IntPtr db, SQLiteConfigDbOpsEnum op, int value, ref int result);








#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_rollback_hook(IntPtr db, SQLiteRollbackCallback func, IntPtr pvUser);








>
>
>
>
>
>
>




>
>
>
>
>
>
>







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
3867
3868
3869
3870
3871
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config")]
#endif
    internal static extern SQLiteErrorCode sqlite3_config_log(SQLiteConfigOpsEnum op, SQLiteLogCallback func, IntPtr pvUser);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config", CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config")]
#endif
    internal static extern SQLiteErrorCode sqlite3_db_config_charptr(IntPtr db, SQLiteConfigDbOpsEnum op, IntPtr charPtr);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config", CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config")]
#endif
    internal static extern SQLiteErrorCode sqlite3_db_config_int_refint(IntPtr db, SQLiteConfigDbOpsEnum op, int value, ref int result);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config", CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config")]
#endif
    internal static extern SQLiteErrorCode sqlite3_db_config_intptr_two_ints(IntPtr db, SQLiteConfigDbOpsEnum op, IntPtr ptr, int int0, int int1);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_rollback_hook(IntPtr db, SQLiteRollbackCallback func, IntPtr pvUser);