System.Data.SQLite

Check-in [e1bef414b7]
Login

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

Overview
Comment:Fix handling of copied connection handles when calling Create and Connect virtual methods. Get initial test passing.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | virtualTables
Files: files | file ages | folders
SHA1: e1bef414b7415f615eafcb7fa8f2b183a4f4215f
User & Date: mistachkin 2013-06-20 03:08:43.803
Context
2013-06-21
03:11
Major cleanup and refactoring of the ISQLiteManagedModule interface. Fix a small memory leak in DeclareVirtualTable. Add the ability to persist SQLiteValue values beyond the duration of the method call in progress. Add SQLiteVirtualTable base class. Update SQLiteVirtualTableCursor base class to be IDisposable and to support storing the filtering parameters. Make parameter naming in the ISQLiteManagedModule interface consistent with other managed code. Add SQLiteVirtualTable parameter to the ISQLiteManagedModule interface methods where necessary. Remove incorrect SQLiteIndex object creation from the IndexToIntPtr method. Keep track of all managed table and cursor object instances associated with their native pointers. Zero out the native sqlite3_vtab object right after allocation. Correct declarations for the sqlite3_vtab and sqlite3_vtab_cursor structures. Add SQLiteModuleEnumerable and SQLiteVirtualTableCursorEnumerable classes to allow an IEnumerable to provide the data for a virtual table. check-in: d3ac8fc2c3 user: mistachkin tags: virtualTables
2013-06-20
03:08
Fix handling of copied connection handles when calling Create and Connect virtual methods. Get initial test passing. check-in: e1bef414b7 user: mistachkin tags: virtualTables
01:40
More work on disposable vtable integration. check-in: 5a2bdec84f user: mistachkin tags: virtualTables
Changes
Unified Diff Ignore Whitespace Patch
Changes to System.Data.SQLite/SQLite3.cs.
86
87
88
89
90
91
92
93







94
95




96

97
98
99
100
101
102
103
    /// <summary>
    /// The modules created using this connection.
    /// </summary>
    protected Dictionary<string, SQLiteModuleBase> _modules;

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

    internal SQLite3(SQLiteDateFormats fmt, DateTimeKind kind, string fmtString, bool ownHandle)







      : base(fmt, kind, fmtString)
    {




        _ownHandle = ownHandle;

    }

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

    #region IDisposable "Pattern" Members
    private bool disposed;
    private void CheckDisposed() /* throw */







|
>
>
>
>
>
>
>


>
>
>
>
|
>







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
    /// <summary>
    /// The modules created using this connection.
    /// </summary>
    protected Dictionary<string, SQLiteModuleBase> _modules;

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

    internal SQLite3(
        SQLiteDateFormats fmt,
        DateTimeKind kind,
        string fmtString,
        IntPtr db,
        string fileName,
        bool ownHandle
        )
      : base(fmt, kind, fmtString)
    {
        if (db != IntPtr.Zero)
        {
            _sql = new SQLiteConnectionHandle(db, ownHandle);
            _fileName = fileName;
            _ownHandle = ownHandle;
        }
    }

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

    #region IDisposable "Pattern" Members
    private bool disposed;
    private void CheckDisposed() /* throw */
1531
1532
1533
1534
1535
1536
1537



1538
1539
1540
1541
1542
1543
1544
    /// The module object to be used when creating the native disposable module.
    /// </param>
    internal override void CreateModule(SQLiteModuleBase module)
    {
        if (module == null)
            throw new ArgumentNullException("module");




        SetLoadExtension(true);
        LoadExtension(UnsafeNativeMethods.SQLITE_DLL, "sqlite3_vtshim_init");

        IntPtr pName = IntPtr.Zero;

        try
        {







>
>
>







1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
    /// The module object to be used when creating the native disposable module.
    /// </param>
    internal override void CreateModule(SQLiteModuleBase module)
    {
        if (module == null)
            throw new ArgumentNullException("module");

        if (_sql == null)
            throw new SQLiteException("connection has an invalid handle");

        SetLoadExtension(true);
        LoadExtension(UnsafeNativeMethods.SQLITE_DLL, "sqlite3_vtshim_init");

        IntPtr pName = IntPtr.Zero;

        try
        {
1615
1616
1617
1618
1619
1620
1621






1622
1623
1624
1625
1626
1627
1628
    /// </returns>
    internal override SQLiteErrorCode DeclareVirtualTable(
        SQLiteModuleBase module,
        string strSql,
        ref string error
        )
    {






        SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_declare_vtab(
            _sql, SQLiteMarshal.Utf8IntPtrFromString(strSql));

        if ((n == SQLiteErrorCode.Ok) && (module != null))
            module.Declared = true;

        if (n != SQLiteErrorCode.Ok) error = GetLastError();







>
>
>
>
>
>







1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
    /// </returns>
    internal override SQLiteErrorCode DeclareVirtualTable(
        SQLiteModuleBase module,
        string strSql,
        ref string error
        )
    {
        if (_sql == null)
        {
            error = "connection has an invalid handle";
            return SQLiteErrorCode.Error;
        }

        SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_declare_vtab(
            _sql, SQLiteMarshal.Utf8IntPtrFromString(strSql));

        if ((n == SQLiteErrorCode.Ok) && (module != null))
            module.Declared = true;

        if (n != SQLiteErrorCode.Ok) error = GetLastError();
Changes to System.Data.SQLite/SQLite3_UTF16.cs.
17
18
19
20
21
22
23
24







25
26
27
28
29
30
31
32
  using System.Runtime.InteropServices;

  /// <summary>
  /// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode)
  /// </summary>
  internal sealed class SQLite3_UTF16 : SQLite3
  {
    internal SQLite3_UTF16(SQLiteDateFormats fmt, DateTimeKind kind, string fmtString, bool ownHandle)







      : base(fmt, kind, fmtString, ownHandle)
    {
    }

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

    #region IDisposable "Pattern" Members
    private bool disposed;







|
>
>
>
>
>
>
>
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  using System.Runtime.InteropServices;

  /// <summary>
  /// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode)
  /// </summary>
  internal sealed class SQLite3_UTF16 : SQLite3
  {
    internal SQLite3_UTF16(
        SQLiteDateFormats fmt,
        DateTimeKind kind,
        string fmtString,
        IntPtr db,
        string fileName,
        bool ownHandle
        )
        : base(fmt, kind, fmtString, db, fileName, ownHandle)
    {
    }

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

    #region IDisposable "Pattern" Members
    private bool disposed;
Changes to System.Data.SQLite/SQLiteConnection.cs.
497
498
499
500
501
502
503



504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522

    /// <summary>
    /// Initializes the connection with a pre-existing native connection handle.
    /// </summary>
    /// <param name="db">
    /// The native connection handle to use.
    /// </param>



    /// <param name="ownHandle">
    /// Non-zero if this instance owns the native connection handle and
    /// should dispose of it when it is no longer needed.
    /// </param>
    internal SQLiteConnection(IntPtr db, bool ownHandle)
        : this()
    {
        _ownHandle = ownHandle;

        _sql = new SQLite3(
            SQLiteDateFormats.Default, DateTimeKind.Unspecified, null,
            _ownHandle);
    }

    /// <summary>
    /// Initializes the connection with the specified connection string.
    /// </summary>
    /// <param name="connectionString">
    /// The connection string to use.







>
>
>




|






|







497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525

    /// <summary>
    /// Initializes the connection with a pre-existing native connection handle.
    /// </summary>
    /// <param name="db">
    /// The native connection handle to use.
    /// </param>
    /// <param name="fileName">
    /// The file name corresponding to the native connection handle.
    /// </param>
    /// <param name="ownHandle">
    /// Non-zero if this instance owns the native connection handle and
    /// should dispose of it when it is no longer needed.
    /// </param>
    internal SQLiteConnection(IntPtr db, string fileName, bool ownHandle)
        : this()
    {
        _ownHandle = ownHandle;

        _sql = new SQLite3(
            SQLiteDateFormats.Default, DateTimeKind.Unspecified, null,
            db, fileName, _ownHandle);
    }

    /// <summary>
    /// Initializes the connection with the specified connection string.
    /// </summary>
    /// <param name="connectionString">
    /// The connection string to use.
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
            //       UTF16 if called from sqlite3_open16().
            //
            _ownHandle = true;

            if (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding",
                      DefaultUseUTF16Encoding.ToString())))
            {
                _sql = new SQLite3_UTF16(dateFormat, kind, dateTimeFormat, _ownHandle);
            }
            else
            {
                _sql = new SQLite3(dateFormat, kind, dateTimeFormat, _ownHandle);
            }
        }

        SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None;

        if (!SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", DefaultFailIfMissing.ToString())))
          flags |= SQLiteOpenFlagsEnum.Create;







|



|







1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
            //       UTF16 if called from sqlite3_open16().
            //
            _ownHandle = true;

            if (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding",
                      DefaultUseUTF16Encoding.ToString())))
            {
                _sql = new SQLite3_UTF16(dateFormat, kind, dateTimeFormat, IntPtr.Zero, null, _ownHandle);
            }
            else
            {
                _sql = new SQLite3(dateFormat, kind, dateTimeFormat, IntPtr.Zero, null, _ownHandle);
            }
        }

        SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None;

        if (!SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", DefaultFailIfMissing.ToString())))
          flags |= SQLiteOpenFlagsEnum.Create;
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
            //
            // NOTE: SQLite automatically sets the encoding of the database to
            //       UTF16 if called from sqlite3_open16().
            //
            if (SQLiteConvert.ToBoolean(FindKey(opts,
                    "UseUTF16Encoding", DefaultUseUTF16Encoding.ToString())))
            {
                _sql = new SQLite3_UTF16(dateFormat, kind, dateTimeFormat, _ownHandle);
            }
            else
            {
                _sql = new SQLite3(dateFormat, kind, dateTimeFormat, _ownHandle);
            }
        }
        if (_sql != null) return _sql.Shutdown();
        throw new InvalidOperationException("Database connection not active.");
    }

    /// Enables or disabled extended result codes returned by SQLite







|



|







2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
            //
            // NOTE: SQLite automatically sets the encoding of the database to
            //       UTF16 if called from sqlite3_open16().
            //
            if (SQLiteConvert.ToBoolean(FindKey(opts,
                    "UseUTF16Encoding", DefaultUseUTF16Encoding.ToString())))
            {
                _sql = new SQLite3_UTF16(dateFormat, kind, dateTimeFormat, IntPtr.Zero, null, _ownHandle);
            }
            else
            {
                _sql = new SQLite3(dateFormat, kind, dateTimeFormat, IntPtr.Zero, null, _ownHandle);
            }
        }
        if (_sql != null) return _sql.Shutdown();
        throw new InvalidOperationException("Database connection not active.");
    }

    /// Enables or disabled extended result codes returned by SQLite
Changes to System.Data.SQLite/SQLiteLog.cs.
176
177
178
179
180
181
182

183
184


185
186
187
188
189
190
191
                }
#endif

                //
                // NOTE: Create an instance of the SQLite wrapper class.
                //
                if (_sql == null)

                    _sql = new SQLite3(SQLiteDateFormats.Default,
                        DateTimeKind.Unspecified, null, true);



                //
                // NOTE: Create a single "global" (i.e. per-process) callback
                //       to register with SQLite.  This callback will pass the
                //       event on to any registered handler.  We only want to
                //       do this once.
                //







>
|
|
>
>







176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
                }
#endif

                //
                // NOTE: Create an instance of the SQLite wrapper class.
                //
                if (_sql == null)
                {
                    _sql = new SQLite3(
                        SQLiteDateFormats.Default, DateTimeKind.Unspecified,
                        null, IntPtr.Zero, null, true);
                }

                //
                // NOTE: Create a single "global" (i.e. per-process) callback
                //       to register with SQLite.  This callback will pass the
                //       event on to any registered handler.  We only want to
                //       do this once.
                //
Changes to System.Data.SQLite/SQLiteModuleBase.cs.
1704
1705
1706
1707
1708
1709
1710



1711
1712
1713
1714
1715
1716
1717
1718
1719
            IntPtr[] argv,
            ref IntPtr pVtab,
            ref IntPtr pError
            )
        {
            try
            {



                using (SQLiteConnection connection = new SQLiteConnection(
                        pDb, false))
                {
                    string error = null;

                    if (Create(connection, pAux,
                            SQLiteMarshal.StringArrayFromUtf8IntPtrArray(argv),
                            ref error) == SQLiteErrorCode.Ok)
                    {







>
>
>

|







1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
            IntPtr[] argv,
            ref IntPtr pVtab,
            ref IntPtr pError
            )
        {
            try
            {
                string fileName = SQLiteMarshal.StringFromUtf8IntPtr(
                    UnsafeNativeMethods.sqlite3_db_filename(pDb, IntPtr.Zero));

                using (SQLiteConnection connection = new SQLiteConnection(
                        pDb, fileName, false))
                {
                    string error = null;

                    if (Create(connection, pAux,
                            SQLiteMarshal.StringArrayFromUtf8IntPtrArray(argv),
                            ref error) == SQLiteErrorCode.Ok)
                    {
1743
1744
1745
1746
1747
1748
1749



1750
1751
1752
1753
1754
1755
1756
1757
1758
            IntPtr[] argv,
            ref IntPtr pVtab,
            ref IntPtr pError
            )
        {
            try
            {



                using (SQLiteConnection connection = new SQLiteConnection(
                        pDb, false))
                {
                    string error = null;

                    if (Connect(connection, pAux,
                            SQLiteMarshal.StringArrayFromUtf8IntPtrArray(argv),
                            ref error) == SQLiteErrorCode.Ok)
                    {







>
>
>

|







1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
            IntPtr[] argv,
            ref IntPtr pVtab,
            ref IntPtr pError
            )
        {
            try
            {
                string fileName = SQLiteMarshal.StringFromUtf8IntPtr(
                    UnsafeNativeMethods.sqlite3_db_filename(pDb, IntPtr.Zero));

                using (SQLiteConnection connection = new SQLiteConnection(
                        pDb, fileName, false))
                {
                    string error = null;

                    if (Connect(connection, pAux,
                            SQLiteMarshal.StringArrayFromUtf8IntPtrArray(argv),
                            ref error) == SQLiteErrorCode.Ok)
                    {
Changes to System.Data.SQLite/SQLiteModuleNoop.cs.
1
2
3
4
5
6
7
8
9
10
11
12
13


14
15
16
17
18
19
20
/********************************************************
 * ADO.NET 2.0 Data Provider for SQLite Version 3.X
 * Written by Joe Mistachkin (joe@mistachkin.com)
 *
 * Released to the public domain, use at your own risk!
 ********************************************************/

namespace System.Data.SQLite
{
    public class SQLiteModuleNoop : SQLiteModuleBase
    {
        #region Public Constructors
        public SQLiteModuleNoop(string name)


            : base(name)
        {
            // do nothing.
        }
        #endregion

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












|
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/********************************************************
 * ADO.NET 2.0 Data Provider for SQLite Version 3.X
 * Written by Joe Mistachkin (joe@mistachkin.com)
 *
 * Released to the public domain, use at your own risk!
 ********************************************************/

namespace System.Data.SQLite
{
    public class SQLiteModuleNoop : SQLiteModuleBase
    {
        #region Public Constructors
        public SQLiteModuleNoop(
            string name
            )
            : base(name)
        {
            // do nothing.
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////
Changes to System.Data.SQLite/UnsafeNativeMethods.cs.
1497
1498
1499
1500
1501
1502
1503







1504
1505
1506
1507
1508
1509
1510
#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_db_handle(IntPtr stmt);








#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_next_stmt(IntPtr db, IntPtr stmt);








>
>
>
>
>
>
>







1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_db_handle(IntPtr stmt);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_db_filename(IntPtr db, IntPtr dbName);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern IntPtr sqlite3_next_stmt(IntPtr db, IntPtr stmt);

Changes to Tests/vtab.eagle.
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46


















47
48
49
50
51
52
53
  set sql { \
    CREATE VIRTUAL TABLE t${id} USING vt${id}; \
  }

  unset -nocomplain results errors

  set code [compileCSharpWith [subst {

    using System.Data.SQLite;

    namespace _Dynamic${id}
    {
      public sealed class SQLiteModuleTest${id} : SQLiteModuleNoop
      {
        public SQLiteModuleTest${id}(string name)
          : base(name)
        {
          // do nothing.
        }


















      }

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

      public static class Test${id}
      {
        public static void Main()







>











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







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  set sql { \
    CREATE VIRTUAL TABLE t${id} USING vt${id}; \
  }

  unset -nocomplain results errors

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

    namespace _Dynamic${id}
    {
      public sealed class SQLiteModuleTest${id} : SQLiteModuleNoop
      {
        public SQLiteModuleTest${id}(string name)
          : base(name)
        {
          // do nothing.
        }

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

        public override SQLiteErrorCode Create(
          SQLiteConnection connection,
          IntPtr pClientData,
          string\[\] argv,
          ref string error
          )
        {
          SQLiteErrorCode rc = DeclareTable(
            connection, "CREATE TABLE ignored(x);", ref error);

          if (rc != SQLiteErrorCode.Ok)
            return rc;

          return base.Create(connection, pClientData, argv, ref error);
        }
      }

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

      public static class Test${id}
      {
        public static void Main()
76
77
78
79
80
81
82
83


84
85
86
87
88
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} Main
      } result] : [set result ""]}] $result
} -cleanup {
  cleanupDb $fileName

  unset -nocomplain result code results errors sql dataSource id db fileName
} -constraints [fixConstraints {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite}] -match regexp -result {^Ok System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}}



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

runSQLiteTestEpilogue
runTestEpilogue







|
>
>





95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
      [expr {$code eq "Ok" ? [catch {
        object invoke _Dynamic${id}.Test${id} Main
      } result] : [set result ""]}] $result
} -cleanup {
  cleanupDb $fileName

  unset -nocomplain result code results errors sql dataSource id db fileName
} -constraints [fixConstraints {eagle monoBug28 command.sql compile.DATA SQLite\
System.Data.SQLite}] -match regexp -result {^Ok\
System#CodeDom#Compiler#CompilerResults#\d+ \{\} 0 \{\}$}}

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

runSQLiteTestEpilogue
runTestEpilogue