System.Data.SQLite

Check-in [3e1da60858]
Login

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

Overview
Comment:Allow virtual tables implemented in managed code to implement functions.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3e1da60858edc922f4d69435e2eefa3680c5f0e0
User & Date: mistachkin 2013-07-01 10:16:18.452
Context
2013-07-01
18:22
In the SQLiteFunction class, check if the database connection is open prior to attempting to call the Cancel method. Add NoFunctions connection flag to skip binding functions registered in the application domain. Fixes and adjustments to comments. Add several data-types for compatibility purposes. Fix for [fe50b8c2e8]. check-in: dff9a878dd user: mistachkin tags: trunk
10:16
Allow virtual tables implemented in managed code to implement functions. check-in: 3e1da60858 user: mistachkin tags: trunk
2013-06-28
06:47
When reading a DateTime value, avoid unnecessary string conversions. Fix for [4d87fbc742]. check-in: e1b4194a30 user: mistachkin tags: trunk
Changes
Unified Diff Show Whitespace Changes Patch
Changes to System.Data.SQLite/SQLite3.cs.
1731
1732
1733
1734
1735
1736
1737
1738

1739
1740
1741
1742
1743
1744
1745
    {
      return UnsafeNativeMethods.sqlite3_aggregate_context(context, 1);
    }

#if INTEROP_VIRTUAL_TABLE
    /// <summary>
    /// Calls the native SQLite core library in order to declare a virtual table
    /// in response to a call into the xCreate or xConnect virtual table methods.

    /// </summary>
    /// <param name="module">
    /// The virtual table module that is to be responsible for the virtual table
    /// being declared.
    /// </param>
    /// <param name="strSql">
    /// The string containing the SQL statement describing the virtual table to







|
>







1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
    {
      return UnsafeNativeMethods.sqlite3_aggregate_context(context, 1);
    }

#if INTEROP_VIRTUAL_TABLE
    /// <summary>
    /// Calls the native SQLite core library in order to declare a virtual table
    /// in response to a call into the <see cref="ISQLiteNativeModule.xCreate" />
    /// or <see cref="ISQLiteNativeModule.xConnect" /> virtual table methods.
    /// </summary>
    /// <param name="module">
    /// The virtual table module that is to be responsible for the virtual table
    /// being declared.
    /// </param>
    /// <param name="strSql">
    /// The string containing the SQL statement describing the virtual table to
1785
1786
1787
1788
1789
1790
1791


























































1792
1793
1794
1795
1796
1797
1798
            if (pSql != IntPtr.Zero)
            {
                SQLiteMemory.Free(pSql);
                pSql = IntPtr.Zero;
            }
        }
    }


























































#endif

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







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







1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
            if (pSql != IntPtr.Zero)
            {
                SQLiteMemory.Free(pSql);
                pSql = IntPtr.Zero;
            }
        }
    }

    /// <summary>
    /// Calls the native SQLite core library in order to declare a virtual table
    /// function in response to a call into the <see cref="ISQLiteNativeModule.xCreate" />
    /// or <see cref="ISQLiteNativeModule.xConnect" /> virtual table methods.
    /// </summary>
    /// <param name="module">
    /// The virtual table module that is to be responsible for the virtual table
    /// function being declared.
    /// </param>
    /// <param name="argumentCount">
    /// The number of arguments to the function being declared.
    /// </param>
    /// <param name="name">
    /// The name of the function being declared.
    /// </param>
    /// <param name="error">
    /// Upon success, the contents of this parameter are undefined.  Upon failure,
    /// it should contain an appropriate error message.
    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal override SQLiteErrorCode DeclareVirtualFunction(
        SQLiteModule module,
        int argumentCount,
        string name,
        ref string error
        )
    {
        if (_sql == null)
        {
            error = "connection has an invalid handle";
            return SQLiteErrorCode.Error;
        }

        IntPtr pName = IntPtr.Zero;

        try
        {
            pName = SQLiteString.Utf8IntPtrFromString(name);

            SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_overload_function(
                _sql, pName, argumentCount);

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

            return n;
        }
        finally
        {
            if (pName != IntPtr.Zero)
            {
                SQLiteMemory.Free(pName);
                pName = IntPtr.Zero;
            }
        }
    }
#endif

    /// <summary>
    /// Enables or disabled extension loading by SQLite.
    /// </summary>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.
Changes to System.Data.SQLite/SQLiteBase.cs.
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
























258
259
260
261
262
263
264
    /// The module object previously passed to the <see cref="CreateModule" />
    /// method.
    /// </param>
    internal abstract void DisposeModule(SQLiteModule module);

    /// <summary>
    /// Calls the native SQLite core library in order to declare a virtual table
    /// in response to a call into the xCreate or xConnect virtual table methods.

    /// </summary>
    /// <param name="module">
    /// The virtual table module that is to be responsible for the virtual table
    /// being declared.
    /// </param>
    /// <param name="strSql">
    /// The string containing the SQL statement describing the virtual table to
    /// be declared.
    /// </param>
    /// <param name="error">
    /// Upon success, the contents of this parameter are undefined.  Upon failure,
    /// it should contain an appropriate error message.
    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal abstract SQLiteErrorCode DeclareVirtualTable(SQLiteModule module, string strSql, ref string error);
























#endif

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







|
>

















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







233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
    /// The module object previously passed to the <see cref="CreateModule" />
    /// method.
    /// </param>
    internal abstract void DisposeModule(SQLiteModule module);

    /// <summary>
    /// Calls the native SQLite core library in order to declare a virtual table
    /// in response to a call into the <see cref="ISQLiteNativeModule.xCreate" />
    /// or <see cref="ISQLiteNativeModule.xConnect" /> virtual table methods.
    /// </summary>
    /// <param name="module">
    /// The virtual table module that is to be responsible for the virtual table
    /// being declared.
    /// </param>
    /// <param name="strSql">
    /// The string containing the SQL statement describing the virtual table to
    /// be declared.
    /// </param>
    /// <param name="error">
    /// Upon success, the contents of this parameter are undefined.  Upon failure,
    /// it should contain an appropriate error message.
    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal abstract SQLiteErrorCode DeclareVirtualTable(SQLiteModule module, string strSql, ref string error);

    /// <summary>
    /// Calls the native SQLite core library in order to declare a virtual table
    /// function in response to a call into the <see cref="ISQLiteNativeModule.xCreate" />
    /// or <see cref="ISQLiteNativeModule.xConnect" /> virtual table methods.
    /// </summary>
    /// <param name="module">
    /// The virtual table module that is to be responsible for the virtual table
    /// function being declared.
    /// </param>
    /// <param name="argumentCount">
    /// The number of arguments to the function being declared.
    /// </param>
    /// <param name="name">
    /// The name of the function being declared.
    /// </param>
    /// <param name="error">
    /// Upon success, the contents of this parameter are undefined.  Upon failure,
    /// it should contain an appropriate error message.
    /// </param>
    /// <returns>
    /// A standard SQLite return code.
    /// </returns>
    internal abstract SQLiteErrorCode DeclareVirtualFunction(SQLiteModule module, int argumentCount, string name, ref string error);
#endif

    /// <summary>
    /// Enables or disabled extension loading by SQLite.
    /// </summary>
    /// <param name="bOnOff">
    /// True to enable loading of extensions, false to disable.
Changes to System.Data.SQLite/SQLiteFunction.cs.
86
87
88
89
90
91
92


































93
94
95
96
97
98
99
    /// <summary>
    /// Internal constructor, initializes the function's internal variables.
    /// </summary>
    protected SQLiteFunction()
    {
      _contextDataList = new Dictionary<IntPtr, AggregateData>();
    }



































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

    #region IDisposable Members
    /// <summary>
    /// Disposes of any active contextData variables that were not automatically cleaned up.  Sometimes this can happen if
    /// someone closes the connection while a DataReader is open.







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







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    /// <summary>
    /// Internal constructor, initializes the function's internal variables.
    /// </summary>
    protected SQLiteFunction()
    {
      _contextDataList = new Dictionary<IntPtr, AggregateData>();
    }

    /// <summary>
    /// Constructs an instance of this class using the specified data-type
    /// conversion parameters.
    /// </summary>
    /// <param name="format">
    /// The DateTime format to be used when converting string values to a
    /// DateTime and binding DateTime parameters.
    /// </param>
    /// <param name="kind">
    /// The <see cref="DateTimeKind" /> to be used when creating DateTime
    /// values.
    /// </param>
    /// <param name="formatString">
    /// The format string to be used when parsing and formatting DateTime
    /// values.
    /// </param>
    /// <param name="utf16">
    /// Non-zero to create a UTF-16 data-type conversion context; otherwise,
    /// a UTF-8 data-type conversion context will be created.
    /// </param>
    protected SQLiteFunction(
        SQLiteDateFormats format,
        DateTimeKind kind,
        string formatString,
        bool utf16
        )
        : this()
    {
        if (utf16)
            _base = new SQLite3_UTF16(format, kind, formatString, IntPtr.Zero, null, false);
        else
            _base = new SQLite3(format, kind, formatString, IntPtr.Zero, null, false);
    }

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

    #region IDisposable Members
    /// <summary>
    /// Disposes of any active contextData variables that were not automatically cleaned up.  Sometimes this can happen if
    /// someone closes the connection while a DataReader is open.
Changes to System.Data.SQLite/SQLiteModule.cs.
5174
5175
5176
5177
5178
5179
5180










5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199

5200
5201
5202
5203
5204
5205
5206
        /// <summary>
        /// This field is used to store the virtual table cursor instances
        /// associated with this module.  The native pointer to the
        /// sqlite3_vtab_cursor derived structure is used to key into this
        /// collection.
        /// </summary>
        private Dictionary<IntPtr, SQLiteVirtualTableCursor> cursors;










        #endregion

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

        #region Public Constructors
        /// <summary>
        /// Constructs an instance of this class.
        /// </summary>
        /// <param name="name">
        /// The name of the module.  This parameter cannot be null.
        /// </param>
        public SQLiteModule(string name)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            this.name = name;
            this.tables = new Dictionary<IntPtr, SQLiteVirtualTable>();
            this.cursors = new Dictionary<IntPtr, SQLiteVirtualTableCursor>();

        }
        #endregion

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

        #region Internal Methods
        /// <summary>







>
>
>
>
>
>
>
>
>
>



















>







5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
        /// <summary>
        /// This field is used to store the virtual table cursor instances
        /// associated with this module.  The native pointer to the
        /// sqlite3_vtab_cursor derived structure is used to key into this
        /// collection.
        /// </summary>
        private Dictionary<IntPtr, SQLiteVirtualTableCursor> cursors;

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

        /// <summary>
        /// This field is used to store the virtual table function instances
        /// associated with this module.  The case-insensitive function name
        /// and the number of arguments (with -1 meaning "any") are used to
        /// construct the string that is used to key into this collection.
        /// </summary>
        private Dictionary<string, SQLiteFunction> functions;
        #endregion

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

        #region Public Constructors
        /// <summary>
        /// Constructs an instance of this class.
        /// </summary>
        /// <param name="name">
        /// The name of the module.  This parameter cannot be null.
        /// </param>
        public SQLiteModule(string name)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            this.name = name;
            this.tables = new Dictionary<IntPtr, SQLiteVirtualTable>();
            this.cursors = new Dictionary<IntPtr, SQLiteVirtualTableCursor>();
            this.functions = new Dictionary<string, SQLiteFunction>();
        }
        #endregion

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

        #region Internal Methods
        /// <summary>
5785
5786
5787
5788
5789
5790
5791

































5792
5793
5794
5795
5796
5797
5798

            return pCursor;
        }
        #endregion

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


































        #region Table Declaration Helper Methods
        /// <summary>
        /// Attempts to declare the schema for the virtual table using the
        /// specified database connection.
        /// </summary>
        /// <param name="connection">
        /// The <see cref="SQLiteConnection" /> object instance to use when







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







5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842

            return pCursor;
        }
        #endregion

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

        #region Function Lookup Methods
        /// <summary>
        /// Deterimines the key that should be used to identify and store the
        /// function instance for the virtual table (i.e. to be returned via
        /// the <see cref="ISQLiteNativeModule.xFindFunction" /> method).
        /// </summary>
        /// <param name="argumentCount">
        /// The number of arguments to the virtual table function.
        /// </param>
        /// <param name="name">
        /// The name of the virtual table function.
        /// </param>
        /// <param name="function">
        /// The <see cref="SQLiteFunction" /> object instance associated with
        /// this virtual table function.
        /// </param>
        /// <returns>
        /// The string that should be used to identify and store the virtual
        /// table function instance.  This method cannot return null.  If null
        /// is returned from this method, the behavior is undefined.
        /// </returns>
        protected virtual string GetFunctionKey(
            int argumentCount,
            string name,
            SQLiteFunction function
            )
        {
            return String.Format("{0}:{1}", argumentCount, name);
        }
        #endregion

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

        #region Table Declaration Helper Methods
        /// <summary>
        /// Attempts to declare the schema for the virtual table using the
        /// specified database connection.
        /// </summary>
        /// <param name="connection">
        /// The <see cref="SQLiteConnection" /> object instance to use when
5828
5829
5830
5831
5832
5833
5834





























5835
5836
5837
5838
5839
5840
5841
                error = "connection has invalid handle";
                return SQLiteErrorCode.Error;
            }

            return sqliteBase.DeclareVirtualTable(this, sql, ref error);
        }
        #endregion






























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

        #region Error Handling Helper Methods
        /// <summary>
        /// Arranges for the specified error message to be placed into the
        /// zErrMsg field of a sqlite3_vtab derived structure, freeing the







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







5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
                error = "connection has invalid handle";
                return SQLiteErrorCode.Error;
            }

            return sqliteBase.DeclareVirtualTable(this, sql, ref error);
        }
        #endregion

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

        #region Function Declaration Helper Methods
        protected virtual SQLiteErrorCode DeclareFunction(
            SQLiteConnection connection,
            int argumentCount,
            string name,
            ref string error
            )
        {
            if (connection == null)
            {
                error = "invalid connection";
                return SQLiteErrorCode.Error;
            }

            SQLiteBase sqliteBase = connection._sql;

            if (sqliteBase == null)
            {
                error = "connection has invalid handle";
                return SQLiteErrorCode.Error;
            }

            return sqliteBase.DeclareVirtualFunction(
                this, argumentCount, name, ref error);
        }
        #endregion

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

        #region Error Handling Helper Methods
        /// <summary>
        /// Arranges for the specified error message to be placed into the
        /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
7036
7037
7038
7039
7040
7041
7042

7043
7044
7045
7046
7047
7048
7049
7050
7051



7052

7053
7054
7055
7056
7057
7058
7059
        {
            try
            {
                SQLiteVirtualTable table = TableFromIntPtr(pVtab);

                if (table != null)
                {

                    SQLiteFunction function = null;

                    if (FindFunction(
                            table, nArg,
                            SQLiteString.StringFromUtf8IntPtr(zName),
                            ref function, ref pClientData))
                    {
                        if (function != null)
                        {



                            callback = function.ScalarCallback;

                            return 1;
                        }
                        else
                        {
                            SetTableError(pVtab, "no function was created");
                        }
                    }







>



<
<
|



>
>
>

>







7109
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119


7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
        {
            try
            {
                SQLiteVirtualTable table = TableFromIntPtr(pVtab);

                if (table != null)
                {
                    string name = SQLiteString.StringFromUtf8IntPtr(zName);
                    SQLiteFunction function = null;

                    if (FindFunction(


                            table, nArg, name, ref function, ref pClientData))
                    {
                        if (function != null)
                        {
                            string key = GetFunctionKey(nArg, name, function);

                            functions[key] = function;
                            callback = function.ScalarCallback;

                            return 1;
                        }
                        else
                        {
                            SetTableError(pVtab, "no function was created");
                        }
                    }
7790
7791
7792
7793
7794
7795
7796
7797
7798

7799
7800
7801
7802



7803
7804
7805
7806
7807
7808
7809
        /// <see cref="Dispose()" /> method.  Zero if this method is being
        /// called from the finalizer.
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                //if (disposing)
                //{

                //    ////////////////////////////////////
                //    // dispose managed resources here...
                //    ////////////////////////////////////
                //}




                //////////////////////////////////////
                // release unmanaged resources here...
                //////////////////////////////////////

                try
                {







|
<
>
|
|
|
|
>
>
>







7866
7867
7868
7869
7870
7871
7872
7873

7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
        /// <see cref="Dispose()" /> method.  Zero if this method is being
        /// called from the finalizer.
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)

                {
                    ////////////////////////////////////
                    // dispose managed resources here...
                    ////////////////////////////////////

                    if (functions != null)
                        functions.Clear();
                }

                //////////////////////////////////////
                // release unmanaged resources here...
                //////////////////////////////////////

                try
                {
Changes to System.Data.SQLite/UnsafeNativeMethods.cs.
986
987
988
989
990
991
992







993
994
995
996
997
998
999
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern SQLiteErrorCode sqlite3_load_extension(
        IntPtr db, byte[] fileName, byte[] procName, ref IntPtr pError);








#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
#else
    [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
#endif
    internal static extern SQLiteErrorCode sqlite3_win32_set_directory(uint type, string value);








>
>
>
>
>
>
>







986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern SQLiteErrorCode sqlite3_load_extension(
        IntPtr db, byte[] fileName, byte[] procName, ref IntPtr pError);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
    [DllImport(SQLITE_DLL)]
#endif
    internal static extern SQLiteErrorCode sqlite3_overload_function(IntPtr db, IntPtr zName, int nArgs);

#if !PLATFORM_COMPACTFRAMEWORK
    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
#else
    [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
#endif
    internal static extern SQLiteErrorCode sqlite3_win32_set_directory(uint type, string value);

Changes to Tests/vtab.eagle.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

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

runTest {test vtab-1.1 {virtual table support} -setup {
  setupDb [set fileName vtab-1.1.db]
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql { \
    CREATE VIRTUAL TABLE t${id} USING mod${id}; \







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

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

runTest {test vtab-1.1 {basic virtual table support} -setup {
  setupDb [set fileName vtab-1.1.db]
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql { \
    CREATE VIRTUAL TABLE t${id} USING mod${id}; \
316
317
318
319
320
321
322





























































































































































































































































323
324
325
326
327
  unset -nocomplain result code results errors sql dataSource id db fileName
} -constraints \
{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite\
defineConstant.System.Data.SQLite.INTEROP_VIRTUAL_TABLE} -match regexp -result \
[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
\{\} 0 \{1 2 3 4 5 Error \{SQL logic error or missing database
virtual table "t\d+" is read-only\}\}$}]}






























































































































































































































































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

runSQLiteTestEpilogue
runTestEpilogue







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





316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
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
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
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
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
  unset -nocomplain result code results errors sql dataSource id db fileName
} -constraints \
{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite\
defineConstant.System.Data.SQLite.INTEROP_VIRTUAL_TABLE} -match regexp -result \
[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
\{\} 0 \{1 2 3 4 5 Error \{SQL logic error or missing database
virtual table "t\d+" is read-only\}\}$}]}

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

runTest {test vtab-1.4 {virtual table function support} -setup {
  setupDb [set fileName vtab-1.4.db]
} -body {
  set id [object invoke Interpreter.GetActive NextId]
  set dataSource [file join [getDatabaseDirectory] $fileName]

  set sql(1) { \
    CREATE VIRTUAL TABLE t${id} USING mod${id}; \
  }

  set sql(2) { \
    SELECT Base64(x, CAST('one' AS BLOB)) FROM t${id}; \
  }

  set sql(3) { \
    SELECT Base64(x, CAST('one' AS BLOB), 'two') FROM t${id}; \
  }

  set sql(4) { \
    SELECT Base65(x, CAST('one' AS BLOB)) FROM t${id}; \
  }

  unset -nocomplain results errors

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

    namespace _Dynamic${id}
    {
      public class SQLiteFunction${id} : SQLiteFunction
      {
        public SQLiteFunction${id}()
          : base(SQLiteDateFormats.Default, DateTimeKind.Unspecified,
                 null, false)
        {
          // do nothing.
        }

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

        public override object Invoke(
          object\[\] args
          )
        {
          if (args == null)
            return null;

          if (args.Length != 2)
            return new ArgumentException(String.Format(
              "need exactly two arguments, got {0}", args.Length));

          object arg = args\[1\];

          if (arg == null)
            return String.Empty;

          Type type = arg.GetType();

          if (type == typeof(DBNull))
            return String.Empty;

          if (type != typeof(byte\[\]))
            return new ArgumentException(String.Format(
              "argument must be byte array, got {0}", type));

          return Convert.ToBase64String((byte\[\]) arg);
        }
      }

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

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

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

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

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

          rc = DeclareFunction(connection, -1, "Base64", ref error);

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

          table = new SQLiteVirtualTable(arguments);
          return SQLiteErrorCode.Ok;
        }

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

        public override SQLiteErrorCode Open(
          SQLiteVirtualTable table,
          ref SQLiteVirtualTableCursor cursor
          )
        {
          cursor = new SQLiteVirtualTableCursor(table);
          return SQLiteErrorCode.Ok;
        }

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

        public override bool FindFunction(
          SQLiteVirtualTable table,
          int argumentCount,
          string name,
          ref SQLiteFunction function,
          ref IntPtr pClientData
          )
        {
          if (argumentCount != 2)
          {
            SetTableError(table, String.Format(
              "no \\"{0}\\" functions accept {1} argument(s)",
              base.Name, argumentCount));

            return false;
          }

          if (!String.Equals(name, "Base64",
              StringComparison.OrdinalIgnoreCase))
          {
            SetTableError(table, String.Format(
              "no \\"{0}\\" functions are named \\"{1}\\"",
              base.Name, name));

            return false;
          }

          function = new SQLiteFunction${id}();
          return true;
        }
      }

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

      public static class Test${id}
      {
        public static StringList GetList()
        {
          StringList result = new StringList();

          using (SQLiteConnection connection = new SQLiteConnection(
              "Data Source=${dataSource};"))
          {
            connection.Open();
            connection.CreateModule(new SQLiteModuleTest${id}("mod${id}"));

            try
            {
              using (SQLiteCommand command = connection.CreateCommand())
              {
                command.CommandText = "[subst ${sql(1)}]";
                result.Add(String.Format("{0}", command.ExecuteScalar()));
              }
            }
            catch (Exception e)
            {
              result.Add(e.Message);
            }

            try
            {
              using (SQLiteCommand command = connection.CreateCommand())
              {
                command.CommandText = "[subst ${sql(2)}]";
                result.Add(String.Format("{0}", command.ExecuteScalar()));
              }
            }
            catch (Exception e)
            {
              result.Add(e.Message);
            }

            try
            {
              using (SQLiteCommand command = connection.CreateCommand())
              {
                command.CommandText = "[subst ${sql(3)}]";
                result.Add(String.Format("{0}", command.ExecuteScalar()));
              }
            }
            catch (Exception e)
            {
              result.Add(e.Message);
            }

            try
            {
              using (SQLiteCommand command = connection.CreateCommand())
              {
                command.CommandText = "[subst ${sql(4)}]";
                result.Add(String.Format("{0}", command.ExecuteScalar()));
              }
            }
            catch (Exception e)
            {
              result.Add(e.Message);
            }

            connection.Close();
          }

          return result;
        }

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

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

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

  unset -nocomplain result code results errors sql dataSource id db fileName
} -constraints \
{eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite\
defineConstant.System.Data.SQLite.INTEROP_VIRTUAL_TABLE} -match regexp -result \
[string map [list \n \r\n] {^Ok System#CodeDom#Compiler#CompilerResults#\d+\
\{\} 0 \{\{\} b25l \{SQL logic error or missing database
unable to use function Base64 in the requested context\} \{SQL logic error or\
missing database
no such function: Base65\}\}$}]}

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

runSQLiteTestEpilogue
runTestEpilogue