wasCSharpSQLite – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Diagnostics; |
||
3 | using System.Text; |
||
4 | |||
5 | using u8 = System.Byte; |
||
6 | |||
7 | namespace Community.CsharpSqlite |
||
8 | { |
||
9 | public partial class Sqlite3 |
||
10 | { |
||
11 | /* |
||
12 | ** 2006 June 10 |
||
13 | ** |
||
14 | ** The author disclaims copyright to this source code. In place of |
||
15 | ** a legal notice, here is a blessing: |
||
16 | ** |
||
17 | ** May you do good and not evil. |
||
18 | ** May you find forgiveness for yourself and forgive others. |
||
19 | ** May you share freely, never taking more than you give. |
||
20 | ** |
||
21 | ************************************************************************* |
||
22 | ** This file contains code used to help implement virtual tables. |
||
23 | ************************************************************************* |
||
24 | ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
||
25 | ** C#-SQLite is an independent reimplementation of the SQLite software library |
||
26 | ** |
||
27 | ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2 |
||
28 | ** |
||
29 | ************************************************************************* |
||
30 | */ |
||
31 | #if !SQLITE_OMIT_VIRTUALTABLE |
||
32 | //#include "sqliteInt.h" |
||
33 | |||
34 | /* |
||
35 | ** Before a virtual table xCreate() or xConnect() method is invoked, the |
||
36 | ** sqlite3.pVtabCtx member variable is set to point to an instance of |
||
37 | ** this struct allocated on the stack. It is used by the implementation of |
||
38 | ** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which |
||
39 | ** are invoked only from within xCreate and xConnect methods. |
||
40 | */ |
||
41 | public class VtabCtx |
||
42 | { |
||
43 | public Table pTab; |
||
44 | public VTable pVTable; |
||
45 | }; |
||
46 | |||
47 | /* |
||
48 | ** The actual function that does the work of creating a new module. |
||
49 | ** This function implements the sqlite3_create_module() and |
||
50 | ** sqlite3_create_module_v2() interfaces. |
||
51 | */ |
||
52 | static int createModule( |
||
53 | sqlite3 db, /* Database in which module is registered */ |
||
54 | string zName, /* Name assigned to this module */ |
||
55 | sqlite3_module pModule, /* The definition of the module */ |
||
56 | object pAux, /* Context pointer for xCreate/xConnect */ |
||
57 | smdxDestroy xDestroy /* Module destructor function */ |
||
58 | ) |
||
59 | { |
||
60 | int rc, nName; |
||
61 | Module pMod; |
||
62 | |||
63 | sqlite3_mutex_enter( db.mutex ); |
||
64 | nName = sqlite3Strlen30( zName ); |
||
65 | pMod = new Module();// (Module)sqlite3DbMallocRaw( db, sizeof( Module ) + nName + 1 ); |
||
66 | if ( pMod != null ) |
||
67 | { |
||
68 | Module pDel; |
||
69 | string zCopy;// = (char )(&pMod[1]); |
||
70 | zCopy = zName;//memcpy(zCopy, zName, nName+1); |
||
71 | pMod.zName = zCopy; |
||
72 | pMod.pModule = pModule; |
||
73 | pMod.pAux = pAux; |
||
74 | pMod.xDestroy = xDestroy; |
||
75 | pDel = (Module)sqlite3HashInsert( ref db.aModule, zCopy, nName, pMod ); |
||
76 | if ( pDel != null && pDel.xDestroy != null ) |
||
77 | { |
||
78 | sqlite3ResetInternalSchema( db, -1 ); |
||
79 | pDel.xDestroy( ref pDel.pAux ); |
||
80 | } |
||
81 | sqlite3DbFree( db, ref pDel ); |
||
82 | //if( pDel==pMod ){ |
||
83 | // db.mallocFailed = 1; |
||
84 | //} |
||
85 | } |
||
86 | else if ( xDestroy != null ) |
||
87 | { |
||
88 | xDestroy( ref pAux ); |
||
89 | } |
||
90 | rc = sqlite3ApiExit( db, SQLITE_OK ); |
||
91 | sqlite3_mutex_leave( db.mutex ); |
||
92 | return rc; |
||
93 | } |
||
94 | |||
95 | |||
96 | /* |
||
97 | ** External API function used to create a new virtual-table module. |
||
98 | */ |
||
99 | static int sqlite3_create_module( |
||
100 | sqlite3 db, /* Database in which module is registered */ |
||
101 | string zName, /* Name assigned to this module */ |
||
102 | sqlite3_module pModule, /* The definition of the module */ |
||
103 | object pAux /* Context pointer for xCreate/xConnect */ |
||
104 | ) |
||
105 | { |
||
106 | return createModule( db, zName, pModule, pAux, null ); |
||
107 | } |
||
108 | |||
109 | /* |
||
110 | ** External API function used to create a new virtual-table module. |
||
111 | */ |
||
112 | static int sqlite3_create_module_v2( |
||
113 | sqlite3 db, /* Database in which module is registered */ |
||
114 | string zName, /* Name assigned to this module */ |
||
115 | sqlite3_module pModule, /* The definition of the module */ |
||
116 | sqlite3_vtab pAux, /* Context pointer for xCreate/xConnect */ |
||
117 | smdxDestroy xDestroy /* Module destructor function */ |
||
118 | ) |
||
119 | { |
||
120 | return createModule( db, zName, pModule, pAux, xDestroy ); |
||
121 | } |
||
122 | |||
123 | /* |
||
124 | ** Lock the virtual table so that it cannot be disconnected. |
||
125 | ** Locks nest. Every lock should have a corresponding unlock. |
||
126 | ** If an unlock is omitted, resources leaks will occur. |
||
127 | ** |
||
128 | ** If a disconnect is attempted while a virtual table is locked, |
||
129 | ** the disconnect is deferred until all locks have been removed. |
||
130 | */ |
||
131 | static void sqlite3VtabLock( VTable pVTab ) |
||
132 | { |
||
133 | pVTab.nRef++; |
||
134 | } |
||
135 | |||
136 | |||
137 | /* |
||
138 | ** pTab is a pointer to a Table structure representing a virtual-table. |
||
139 | ** Return a pointer to the VTable object used by connection db to access |
||
140 | ** this virtual-table, if one has been created, or NULL otherwise. |
||
141 | */ |
||
142 | static VTable sqlite3GetVTable( sqlite3 db, Table pTab ) |
||
143 | { |
||
144 | VTable pVtab; |
||
145 | Debug.Assert( IsVirtual( pTab ) ); |
||
146 | for ( pVtab = pTab.pVTable; pVtab != null && pVtab.db != db; pVtab = pVtab.pNext ) |
||
147 | ; |
||
148 | return pVtab; |
||
149 | } |
||
150 | |||
151 | /* |
||
152 | ** Decrement the ref-count on a virtual table object. When the ref-count |
||
153 | ** reaches zero, call the xDisconnect() method to delete the object. |
||
154 | */ |
||
155 | static void sqlite3VtabUnlock( VTable pVTab ) |
||
156 | { |
||
157 | sqlite3 db = pVTab.db; |
||
158 | |||
159 | Debug.Assert( db != null); |
||
160 | Debug.Assert( pVTab.nRef > 0 ); |
||
161 | Debug.Assert( sqlite3SafetyCheckOk( db ) ); |
||
162 | |||
163 | pVTab.nRef--; |
||
164 | if ( pVTab.nRef == 0 ) |
||
165 | { |
||
166 | object p = pVTab.pVtab; |
||
167 | if ( p != null ) |
||
168 | { |
||
169 | ((sqlite3_vtab)p).pModule.xDisconnect( ref p ); |
||
170 | } |
||
171 | sqlite3DbFree( db, ref pVTab ); |
||
172 | } |
||
173 | } |
||
174 | |||
175 | /* |
||
176 | ** Table p is a virtual table. This function moves all elements in the |
||
177 | ** p.pVTable list to the sqlite3.pDisconnect lists of their associated |
||
178 | ** database connections to be disconnected at the next opportunity. |
||
179 | ** Except, if argument db is not NULL, then the entry associated with |
||
180 | ** connection db is left in the p.pVTable list. |
||
181 | */ |
||
182 | static VTable vtabDisconnectAll( sqlite3 db, Table p ) |
||
183 | { |
||
184 | VTable pRet = null; |
||
185 | VTable pVTable = p.pVTable; |
||
186 | p.pVTable = null; |
||
187 | |||
188 | /* Assert that the mutex (if any) associated with the BtShared database |
||
189 | ** that contains table p is held by the caller. See header comments |
||
190 | ** above function sqlite3VtabUnlockList() for an explanation of why |
||
191 | ** this makes it safe to access the sqlite3.pDisconnect list of any |
||
192 | ** database connection that may have an entry in the p.pVTable list. |
||
193 | */ |
||
194 | Debug.Assert( db == null || sqlite3SchemaMutexHeld( db, 0, p.pSchema ) ); |
||
195 | |||
196 | while ( pVTable != null ) |
||
197 | { |
||
198 | sqlite3 db2 = pVTable.db; |
||
199 | VTable pNext = pVTable.pNext; |
||
200 | Debug.Assert( db2 != null ); |
||
201 | if ( db2 == db ) |
||
202 | { |
||
203 | pRet = pVTable; |
||
204 | p.pVTable = pRet; |
||
205 | pRet.pNext = null; |
||
206 | } |
||
207 | else |
||
208 | { |
||
209 | pVTable.pNext = db2.pDisconnect; |
||
210 | db2.pDisconnect = pVTable; |
||
211 | } |
||
212 | pVTable = pNext; |
||
213 | } |
||
214 | |||
215 | Debug.Assert( null == db || pRet != null ); |
||
216 | return pRet; |
||
217 | } |
||
218 | |||
219 | |||
220 | /* |
||
221 | ** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. |
||
222 | ** |
||
223 | ** This function may only be called when the mutexes associated with all |
||
224 | ** shared b-tree databases opened using connection db are held by the |
||
225 | ** caller. This is done to protect the sqlite3.pDisconnect list. The |
||
226 | ** sqlite3.pDisconnect list is accessed only as follows: |
||
227 | ** |
||
228 | ** 1) By this function. In this case, all BtShared mutexes and the mutex |
||
229 | ** associated with the database handle itself must be held. |
||
230 | ** |
||
231 | ** 2) By function vtabDisconnectAll(), when it adds a VTable entry to |
||
232 | ** the sqlite3.pDisconnect list. In this case either the BtShared mutex |
||
233 | ** associated with the database the virtual table is stored in is held |
||
234 | ** or, if the virtual table is stored in a non-sharable database, then |
||
235 | ** the database handle mutex is held. |
||
236 | ** |
||
237 | ** As a result, a sqlite3.pDisconnect cannot be accessed simultaneously |
||
238 | ** by multiple threads. It is thread-safe. |
||
239 | */ |
||
240 | static void sqlite3VtabUnlockList( sqlite3 db ) |
||
241 | { |
||
242 | VTable p = db.pDisconnect; |
||
243 | db.pDisconnect = null; |
||
244 | |||
245 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) ); |
||
246 | Debug.Assert( sqlite3_mutex_held( db.mutex ) ); |
||
247 | |||
248 | if ( p != null ) |
||
249 | { |
||
250 | sqlite3ExpirePreparedStatements( db ); |
||
251 | do |
||
252 | { |
||
253 | VTable pNext = p.pNext; |
||
254 | sqlite3VtabUnlock( p ); |
||
255 | p = pNext; |
||
256 | } while ( p != null ); |
||
257 | } |
||
258 | } |
||
259 | |||
260 | /* |
||
261 | ** Clear any and all virtual-table information from the Table record. |
||
262 | ** This routine is called, for example, just before deleting the Table |
||
263 | ** record. |
||
264 | ** |
||
265 | ** Since it is a virtual-table, the Table structure contains a pointer |
||
266 | ** to the head of a linked list of VTable structures. Each VTable |
||
267 | ** structure is associated with a single sqlite3* user of the schema. |
||
268 | ** The reference count of the VTable structure associated with database |
||
269 | ** connection db is decremented immediately (which may lead to the |
||
270 | ** structure being xDisconnected and free). Any other VTable structures |
||
271 | ** in the list are moved to the sqlite3.pDisconnect list of the associated |
||
272 | ** database connection. |
||
273 | */ |
||
274 | static void sqlite3VtabClear( sqlite3 db, Table p ) |
||
275 | { |
||
276 | if ( null == db || db.pnBytesFreed == 0 ) |
||
277 | vtabDisconnectAll( null, p ); |
||
278 | if ( p.azModuleArg != null ) |
||
279 | { |
||
280 | int i; |
||
281 | for ( i = 0; i < p.nModuleArg; i++ ) |
||
282 | { |
||
283 | sqlite3DbFree( db, ref p.azModuleArg[i] ); |
||
284 | } |
||
285 | sqlite3DbFree( db, ref p.azModuleArg ); |
||
286 | } |
||
287 | } |
||
288 | |||
289 | /* |
||
290 | ** Add a new module argument to pTable.azModuleArg[]. |
||
291 | ** The string is not copied - the pointer is stored. The |
||
292 | ** string will be freed automatically when the table is |
||
293 | ** deleted. |
||
294 | */ |
||
295 | static void addModuleArgument( sqlite3 db, Table pTable, string zArg ) |
||
296 | { |
||
297 | int i = pTable.nModuleArg++; |
||
298 | //int nBytes = sizeof(char )*(1+pTable.nModuleArg); |
||
299 | //string[] azModuleArg; |
||
300 | //sqlite3DbRealloc( db, pTable.azModuleArg, nBytes ); |
||
301 | if ( pTable.azModuleArg == null || pTable.azModuleArg.Length < pTable.nModuleArg ) |
||
302 | Array.Resize( ref pTable.azModuleArg, 3 + pTable.nModuleArg ); |
||
303 | //if ( azModuleArg == null ) |
||
304 | //{ |
||
305 | // int j; |
||
306 | // for ( j = 0; j < i; j++ ) |
||
307 | // { |
||
308 | // sqlite3DbFree( db, ref pTable.azModuleArg[j] ); |
||
309 | // } |
||
310 | // sqlite3DbFree( db, ref zArg ); |
||
311 | // sqlite3DbFree( db, ref pTable.azModuleArg ); |
||
312 | // pTable.nModuleArg = 0; |
||
313 | //} |
||
314 | //else |
||
315 | { |
||
316 | pTable.azModuleArg[i] = zArg; |
||
317 | //pTable.azModuleArg[i + 1] = null; |
||
318 | //azModuleArg[i+1] = 0; |
||
319 | } |
||
320 | //pTable.azModuleArg = azModuleArg; |
||
321 | } |
||
322 | |||
323 | /* |
||
324 | ** The parser calls this routine when it first sees a CREATE VIRTUAL TABLE |
||
325 | ** statement. The module name has been parsed, but the optional list |
||
326 | ** of parameters that follow the module name are still pending. |
||
327 | */ |
||
328 | static void sqlite3VtabBeginParse( |
||
329 | Parse pParse, /* Parsing context */ |
||
330 | Token pName1, /* Name of new table, or database name */ |
||
331 | Token pName2, /* Name of new table or NULL */ |
||
332 | Token pModuleName /* Name of the module for the virtual table */ |
||
333 | ) |
||
334 | { |
||
335 | int iDb; /* The database the table is being created in */ |
||
336 | Table pTable; /* The new virtual table */ |
||
337 | sqlite3 db; /* Database connection */ |
||
338 | |||
339 | sqlite3StartTable( pParse, pName1, pName2, 0, 0, 1, 0 ); |
||
340 | pTable = pParse.pNewTable; |
||
341 | if ( pTable == null ) |
||
342 | return; |
||
343 | Debug.Assert( null == pTable.pIndex ); |
||
344 | |||
345 | db = pParse.db; |
||
346 | iDb = sqlite3SchemaToIndex( db, pTable.pSchema ); |
||
347 | Debug.Assert( iDb >= 0 ); |
||
348 | |||
349 | pTable.tabFlags |= TF_Virtual; |
||
350 | pTable.nModuleArg = 0; |
||
351 | addModuleArgument( db, pTable, sqlite3NameFromToken( db, pModuleName ) ); |
||
352 | addModuleArgument( db, pTable, db.aDb[iDb].zName);//sqlite3DbStrDup( db, db.aDb[iDb].zName ) ); |
||
353 | addModuleArgument( db, pTable, pTable.zName );//sqlite3DbStrDup( db, pTable.zName ) ); |
||
354 | pParse.sNameToken.n = pParse.sNameToken.z.Length;// (int)[pModuleName.n] - pName1.z ); |
||
355 | |||
356 | #if !SQLITE_OMIT_AUTHORIZATION |
||
357 | /* Creating a virtual table invokes the authorization callback twice. |
||
358 | ** The first invocation, to obtain permission to INSERT a row into the |
||
359 | ** sqlite_master table, has already been made by sqlite3StartTable(). |
||
360 | ** The second call, to obtain permission to create the table, is made now. |
||
361 | */ |
||
362 | if( pTable->azModuleArg ){ |
||
363 | sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, |
||
364 | pTable->azModuleArg[0], pParse->db->aDb[iDb].zName); |
||
365 | } |
||
366 | #endif |
||
367 | } |
||
368 | |||
369 | /* |
||
370 | ** This routine takes the module argument that has been accumulating |
||
371 | ** in pParse.zArg[] and appends it to the list of arguments on the |
||
372 | ** virtual table currently under construction in pParse.pTable. |
||
373 | */ |
||
374 | static void addArgumentToVtab( Parse pParse ) |
||
375 | { |
||
376 | if ( pParse.sArg.z != null && ALWAYS( pParse.pNewTable ) ) |
||
377 | { |
||
378 | string z = pParse.sArg.z.Substring( 0, pParse.sArg.n ); |
||
379 | ////int n = pParse.sArg.n; |
||
380 | sqlite3 db = pParse.db; |
||
381 | addModuleArgument( db, pParse.pNewTable, z );////sqlite3DbStrNDup( db, z, n ) ); |
||
382 | } |
||
383 | } |
||
384 | |||
385 | /* |
||
386 | ** The parser calls this routine after the CREATE VIRTUAL TABLE statement |
||
387 | ** has been completely parsed. |
||
388 | */ |
||
389 | static void sqlite3VtabFinishParse( Parse pParse, Token pEnd ) |
||
390 | { |
||
391 | Table pTab = pParse.pNewTable; /* The table being constructed */ |
||
392 | sqlite3 db = pParse.db; /* The database connection */ |
||
393 | |||
394 | if ( pTab == null ) |
||
395 | return; |
||
396 | addArgumentToVtab( pParse ); |
||
397 | pParse.sArg.z = string.Empty; |
||
398 | if ( pTab.nModuleArg < 1 ) |
||
399 | return; |
||
400 | |||
401 | /* If the CREATE VIRTUAL TABLE statement is being entered for the |
||
402 | ** first time (in other words if the virtual table is actually being |
||
403 | ** created now instead of just being read out of sqlite_master) then |
||
404 | ** do additional initialization work and store the statement text |
||
405 | ** in the sqlite_master table. |
||
406 | */ |
||
407 | if ( 0 == db.init.busy ) |
||
408 | { |
||
409 | string zStmt; |
||
410 | string zWhere; |
||
411 | int iDb; |
||
412 | Vdbe v; |
||
413 | |||
414 | /* Compute the complete text of the CREATE VIRTUAL TABLE statement */ |
||
415 | if ( pEnd != null ) |
||
416 | { |
||
417 | pParse.sNameToken.n = pParse.sNameToken.z.Length;//(int)( pEnd.z - pParse.sNameToken.z ) + pEnd.n; |
||
418 | } |
||
419 | zStmt = sqlite3MPrintf( db, "CREATE VIRTUAL TABLE %T", pParse.sNameToken.z.Substring(0,pParse.sNameToken.n) ); |
||
420 | |||
421 | /* A slot for the record has already been allocated in the |
||
422 | ** SQLITE_MASTER table. We just need to update that slot with all |
||
423 | ** the information we've collected. |
||
424 | ** |
||
425 | ** The VM register number pParse.regRowid holds the rowid of an |
||
426 | ** entry in the sqlite_master table tht was created for this vtab |
||
427 | ** by sqlite3StartTable(). |
||
428 | */ |
||
429 | iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); |
||
430 | sqlite3NestedParse( pParse, |
||
431 | "UPDATE %Q.%s " + |
||
432 | "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " + |
||
433 | "WHERE rowid=#%d", |
||
434 | db.aDb[iDb].zName, SCHEMA_TABLE( iDb ), |
||
435 | pTab.zName, |
||
436 | pTab.zName, |
||
437 | zStmt, |
||
438 | pParse.regRowid |
||
439 | ); |
||
440 | sqlite3DbFree( db, ref zStmt ); |
||
441 | v = sqlite3GetVdbe( pParse ); |
||
442 | sqlite3ChangeCookie( pParse, iDb ); |
||
443 | |||
444 | sqlite3VdbeAddOp2( v, OP_Expire, 0, 0 ); |
||
445 | zWhere = sqlite3MPrintf( db, "name='%q' AND type='table'", pTab.zName ); |
||
446 | sqlite3VdbeAddParseSchemaOp( v, iDb, zWhere ); |
||
447 | sqlite3VdbeAddOp4( v, OP_VCreate, iDb, 0, 0, |
||
448 | pTab.zName, sqlite3Strlen30( pTab.zName ) + 1 ); |
||
449 | } |
||
450 | |||
451 | /* If we are rereading the sqlite_master table create the in-memory |
||
452 | ** record of the table. The xConnect() method is not called until |
||
453 | ** the first time the virtual table is used in an SQL statement. This |
||
454 | ** allows a schema that contains virtual tables to be loaded before |
||
455 | ** the required virtual table implementations are registered. */ |
||
456 | else |
||
457 | { |
||
458 | Table pOld; |
||
459 | Schema pSchema = pTab.pSchema; |
||
460 | string zName = pTab.zName; |
||
461 | int nName = sqlite3Strlen30( zName ); |
||
462 | Debug.Assert( sqlite3SchemaMutexHeld( db, 0, pSchema ) ); |
||
463 | pOld = sqlite3HashInsert( ref pSchema.tblHash, zName, nName, pTab ); |
||
464 | if ( pOld != null ) |
||
465 | { |
||
466 | //db.mallocFailed = 1; |
||
467 | Debug.Assert( pTab == pOld ); /* Malloc must have failed inside HashInsert() */ |
||
468 | return; |
||
469 | } |
||
470 | pParse.pNewTable = null; |
||
471 | } |
||
472 | } |
||
473 | |||
474 | /* |
||
475 | ** The parser calls this routine when it sees the first token |
||
476 | ** of an argument to the module name in a CREATE VIRTUAL TABLE statement. |
||
477 | */ |
||
478 | static void sqlite3VtabArgInit( Parse pParse ) |
||
479 | { |
||
480 | addArgumentToVtab( pParse ); |
||
481 | pParse.sArg.z = null; |
||
482 | pParse.sArg.n = 0; |
||
483 | } |
||
484 | |||
485 | /* |
||
486 | ** The parser calls this routine for each token after the first token |
||
487 | ** in an argument to the module name in a CREATE VIRTUAL TABLE statement. |
||
488 | */ |
||
489 | static void sqlite3VtabArgExtend( Parse pParse, Token p ) |
||
490 | { |
||
491 | Token pArg = pParse.sArg; |
||
492 | if ( pArg.z == null ) |
||
493 | { |
||
494 | pArg.z = p.z; |
||
495 | pArg.n = p.n; |
||
496 | } |
||
497 | else |
||
498 | { |
||
499 | //Debug.Assert( pArg.z< p.z ); |
||
500 | pArg.n += p.n+1;//(int)( p.z[p.n] - pArg.z ); |
||
501 | } |
||
502 | } |
||
503 | |||
504 | /* |
||
505 | ** Invoke a virtual table constructor (either xCreate or xConnect). The |
||
506 | ** pointer to the function to invoke is passed as the fourth parameter |
||
507 | ** to this procedure. |
||
508 | */ |
||
509 | static int vtabCallConstructor( |
||
510 | sqlite3 db, |
||
511 | Table pTab, |
||
512 | Module pMod, |
||
513 | smdxCreateConnect xConstruct, |
||
514 | ref string pzErr |
||
515 | ) |
||
516 | { |
||
517 | VtabCtx sCtx = new VtabCtx(); |
||
518 | VTable pVTable; |
||
519 | int rc; |
||
520 | string[] azArg = pTab.azModuleArg; |
||
521 | int nArg = pTab.nModuleArg; |
||
522 | string zErr = null; |
||
523 | string zModuleName = sqlite3MPrintf( db, "%s", pTab.zName ); |
||
524 | |||
525 | //if ( string.IsNullOrEmpty( zModuleName ) ) |
||
526 | //{ |
||
527 | // return SQLITE_NOMEM; |
||
528 | //} |
||
529 | |||
530 | pVTable = new VTable();//sqlite3DbMallocZero( db, sizeof( VTable ) ); |
||
531 | //if ( null == pVTable ) |
||
532 | //{ |
||
533 | // sqlite3DbFree( db, ref zModuleName ); |
||
534 | // return SQLITE_NOMEM; |
||
535 | //} |
||
536 | pVTable.db = db; |
||
537 | pVTable.pMod = pMod; |
||
538 | |||
539 | /* Invoke the virtual table constructor */ |
||
540 | //assert( &db->pVtabCtx ); |
||
541 | Debug.Assert( xConstruct != null ); |
||
542 | sCtx.pTab = pTab; |
||
543 | sCtx.pVTable = pVTable; |
||
544 | db.pVtabCtx = sCtx; |
||
545 | rc = xConstruct( db, pMod.pAux, nArg, azArg, out pVTable.pVtab, out zErr ); |
||
546 | db.pVtabCtx = null; |
||
547 | //if ( rc == SQLITE_NOMEM ) |
||
548 | // db.mallocFailed = 1; |
||
549 | |||
550 | if ( SQLITE_OK != rc ) |
||
551 | { |
||
552 | if ( zErr.Length == 0 ) |
||
553 | { |
||
554 | pzErr = sqlite3MPrintf( db, "vtable constructor failed: %s", zModuleName ); |
||
555 | } |
||
556 | else |
||
557 | { |
||
558 | pzErr = sqlite3MPrintf( db, "%s", zErr ); |
||
559 | zErr = null;//sqlite3_free( zErr ); |
||
560 | } |
||
561 | sqlite3DbFree( db, ref pVTable ); |
||
562 | } |
||
563 | else if ( ALWAYS( pVTable.pVtab ) ) |
||
564 | { |
||
565 | /* Justification of ALWAYS(): A correct vtab constructor must allocate |
||
566 | ** the sqlite3_vtab object if successful. */ |
||
567 | pVTable.pVtab.pModule = pMod.pModule; |
||
568 | pVTable.nRef = 1; |
||
569 | if ( sCtx.pTab != null ) |
||
570 | { |
||
571 | string zFormat = "vtable constructor did not declare schema: %s"; |
||
572 | pzErr = sqlite3MPrintf( db, zFormat, pTab.zName ); |
||
573 | sqlite3VtabUnlock( pVTable ); |
||
574 | rc = SQLITE_ERROR; |
||
575 | } |
||
576 | else |
||
577 | { |
||
578 | int iCol; |
||
579 | /* If everything went according to plan, link the new VTable structure |
||
580 | ** into the linked list headed by pTab->pVTable. Then loop through the |
||
581 | ** columns of the table to see if any of them contain the token "hidden". |
||
582 | ** If so, set the Column.isHidden flag and remove the token from |
||
583 | ** the type string. */ |
||
584 | pVTable.pNext = pTab.pVTable; |
||
585 | pTab.pVTable = pVTable; |
||
586 | |||
587 | for ( iCol = 0; iCol < pTab.nCol; iCol++ ) |
||
588 | { |
||
589 | if ( string.IsNullOrEmpty( pTab.aCol[iCol].zType ) ) |
||
590 | continue; |
||
591 | StringBuilder zType = new StringBuilder( pTab.aCol[iCol].zType); |
||
592 | int nType; |
||
593 | int i = 0; |
||
594 | //if ( zType ) |
||
595 | // continue; |
||
596 | nType = sqlite3Strlen30( zType ); |
||
597 | if ( sqlite3StrNICmp( "hidden", 0, zType.ToString(), 6 ) != 0 || ( zType.Length > 6 && zType[6] != ' ' ) ) |
||
598 | { |
||
599 | for ( i = 0; i < nType; i++ ) |
||
600 | { |
||
601 | if ( ( 0 == sqlite3StrNICmp( " hidden", zType.ToString().Substring( i ), 7 ) ) |
||
602 | && ( i+7 == zType.Length || (zType[i + 7] == '\0' || zType[i + 7] == ' ' )) |
||
603 | ) |
||
604 | { |
||
605 | i++; |
||
606 | break; |
||
607 | } |
||
608 | } |
||
609 | } |
||
610 | if ( i < nType ) |
||
611 | { |
||
612 | int j; |
||
613 | int nDel = 6 + ( zType.Length > i + 6 ? 1 : 0 ); |
||
614 | for ( j = i; ( j + nDel ) < nType; j++ ) |
||
615 | { |
||
616 | zType[j] = zType[j + nDel]; |
||
617 | } |
||
618 | if ( zType[i] == '\0' && i > 0 ) |
||
619 | { |
||
620 | Debug.Assert( zType[i - 1] == ' ' ); |
||
621 | zType.Length = i;//[i - 1] = '\0'; |
||
622 | } |
||
623 | pTab.aCol[iCol].isHidden = 1; |
||
624 | pTab.aCol[iCol].zType = zType.ToString().Substring(0,j); |
||
625 | } |
||
626 | } |
||
627 | } |
||
628 | } |
||
629 | |||
630 | sqlite3DbFree( db, ref zModuleName ); |
||
631 | return rc; |
||
632 | } |
||
633 | |||
634 | /* |
||
635 | ** This function is invoked by the parser to call the xConnect() method |
||
636 | ** of the virtual table pTab. If an error occurs, an error code is returned |
||
637 | ** and an error left in pParse. |
||
638 | ** |
||
639 | ** This call is a no-op if table pTab is not a virtual table. |
||
640 | */ |
||
641 | static int sqlite3VtabCallConnect( Parse pParse, Table pTab ) |
||
642 | { |
||
643 | sqlite3 db = pParse.db; |
||
644 | string zMod; |
||
645 | Module pMod; |
||
646 | int rc; |
||
647 | |||
648 | Debug.Assert( pTab != null); |
||
649 | if ( ( pTab.tabFlags & TF_Virtual ) == 0 || sqlite3GetVTable( db, pTab )!= null ) |
||
650 | { |
||
651 | return SQLITE_OK; |
||
652 | } |
||
653 | |||
654 | /* Locate the required virtual table module */ |
||
655 | zMod = pTab.azModuleArg[0]; |
||
656 | pMod = (Module)sqlite3HashFind( db.aModule, zMod, sqlite3Strlen30( zMod ), (Module)null ); |
||
657 | |||
658 | if ( null == pMod ) |
||
659 | { |
||
660 | string zModule = pTab.azModuleArg[0]; |
||
661 | sqlite3ErrorMsg( pParse, "no such module: %s", zModule ); |
||
662 | rc = SQLITE_ERROR; |
||
663 | } |
||
664 | else |
||
665 | { |
||
666 | string zErr = null; |
||
667 | rc = vtabCallConstructor( db, pTab, pMod, pMod.pModule.xConnect, ref zErr ); |
||
668 | if ( rc != SQLITE_OK ) |
||
669 | { |
||
670 | sqlite3ErrorMsg( pParse, "%s", zErr ); |
||
671 | } |
||
672 | zErr = null;//sqlite3DbFree( db, zErr ); |
||
673 | } |
||
674 | |||
675 | return rc; |
||
676 | } |
||
677 | /* |
||
678 | ** Grow the db.aVTrans[] array so that there is room for at least one |
||
679 | ** more v-table. Return SQLITE_NOMEM if a malloc fails, or SQLITE_OK otherwise. |
||
680 | */ |
||
681 | static int growVTrans( sqlite3 db ) |
||
682 | { |
||
683 | const int ARRAY_INCR = 5; |
||
684 | |||
685 | /* Grow the sqlite3.aVTrans array if required */ |
||
686 | if ( ( db.nVTrans % ARRAY_INCR ) == 0 ) |
||
687 | { |
||
688 | //VTable** aVTrans; |
||
689 | //int nBytes = sizeof( sqlite3_vtab* ) * ( db.nVTrans + ARRAY_INCR ); |
||
690 | //aVTrans = sqlite3DbRealloc( db, (void)db.aVTrans, nBytes ); |
||
691 | //if ( !aVTrans ) |
||
692 | //{ |
||
693 | // return SQLITE_NOMEM; |
||
694 | //} |
||
695 | //memset( &aVTrans[db.nVTrans], 0, sizeof( sqlite3_vtab* ) * ARRAY_INCR ); |
||
696 | Array.Resize( ref db.aVTrans, db.nVTrans + ARRAY_INCR ); |
||
697 | } |
||
698 | |||
699 | return SQLITE_OK; |
||
700 | } |
||
701 | |||
702 | /* |
||
703 | ** Add the virtual table pVTab to the array sqlite3.aVTrans[]. Space should |
||
704 | ** have already been reserved using growVTrans(). |
||
705 | */ |
||
706 | static void addToVTrans( sqlite3 db, VTable pVTab ) |
||
707 | { |
||
708 | /* Add pVtab to the end of sqlite3.aVTrans */ |
||
709 | db.aVTrans[db.nVTrans++] = pVTab; |
||
710 | sqlite3VtabLock( pVTab ); |
||
711 | } |
||
712 | |||
713 | /* |
||
714 | ** This function is invoked by the vdbe to call the xCreate method |
||
715 | ** of the virtual table named zTab in database iDb. |
||
716 | ** |
||
717 | ** If an error occurs, *pzErr is set to point an an English language |
||
718 | ** description of the error and an SQLITE_XXX error code is returned. |
||
719 | ** In this case the caller must call sqlite3DbFree(db, ) on *pzErr. |
||
720 | */ |
||
721 | static int sqlite3VtabCallCreate( sqlite3 db, int iDb, string zTab, ref string pzErr ) |
||
722 | { |
||
723 | int rc = SQLITE_OK; |
||
724 | Table pTab; |
||
725 | Module pMod; |
||
726 | string zMod; |
||
727 | |||
728 | pTab = sqlite3FindTable( db, zTab, db.aDb[iDb].zName ); |
||
729 | Debug.Assert( pTab != null && ( pTab.tabFlags & TF_Virtual ) != 0 && null == pTab.pVTable ); |
||
730 | |||
731 | /* Locate the required virtual table module */ |
||
732 | zMod = pTab.azModuleArg[0]; |
||
733 | pMod = (Module)sqlite3HashFind( db.aModule, zMod, sqlite3Strlen30( zMod ), (Module)null ); |
||
734 | |||
735 | /* If the module has been registered and includes a Create method, |
||
736 | ** invoke it now. If the module has not been registered, return an |
||
737 | ** error. Otherwise, do nothing. |
||
738 | */ |
||
739 | if ( null == pMod ) |
||
740 | { |
||
741 | pzErr = sqlite3MPrintf( db, "no such module: %s", zMod ); |
||
742 | rc = SQLITE_ERROR; |
||
743 | } |
||
744 | else |
||
745 | { |
||
746 | rc = vtabCallConstructor( db, pTab, pMod, pMod.pModule.xCreate, ref pzErr ); |
||
747 | } |
||
748 | |||
749 | /* Justification of ALWAYS(): The xConstructor method is required to |
||
750 | ** create a valid sqlite3_vtab if it returns SQLITE_OK. */ |
||
751 | if ( rc == SQLITE_OK && ALWAYS( sqlite3GetVTable( db, pTab ) ) ) |
||
752 | { |
||
753 | rc = growVTrans( db ); |
||
754 | if ( rc == SQLITE_OK ) |
||
755 | { |
||
756 | addToVTrans( db, sqlite3GetVTable( db, pTab ) ); |
||
757 | } |
||
758 | } |
||
759 | |||
760 | return rc; |
||
761 | } |
||
762 | |||
763 | /* |
||
764 | ** This function is used to set the schema of a virtual table. It is only |
||
765 | ** valid to call this function from within the xCreate() or xConnect() of a |
||
766 | ** virtual table module. |
||
767 | */ |
||
768 | static int sqlite3_declare_vtab( sqlite3 db, string zCreateTable ) |
||
769 | { |
||
770 | Parse pParse; |
||
771 | |||
772 | int rc = SQLITE_OK; |
||
773 | Table pTab; |
||
774 | string zErr = string.Empty; |
||
775 | |||
776 | sqlite3_mutex_enter( db.mutex ); |
||
777 | if ( null == db.pVtabCtx || null == ( pTab = db.pVtabCtx.pTab ) ) |
||
778 | { |
||
779 | sqlite3Error( db, SQLITE_MISUSE, 0 ); |
||
780 | sqlite3_mutex_leave( db.mutex ); |
||
781 | return SQLITE_MISUSE_BKPT(); |
||
782 | } |
||
783 | Debug.Assert( ( pTab.tabFlags & TF_Virtual ) != 0 ); |
||
784 | |||
785 | pParse = new Parse();//sqlite3StackAllocZero(db, sizeof(*pParse)); |
||
786 | //if ( pParse == null ) |
||
787 | //{ |
||
788 | // rc = SQLITE_NOMEM; |
||
789 | //} |
||
790 | //else |
||
791 | { |
||
792 | pParse.declareVtab = 1; |
||
793 | pParse.db = db; |
||
794 | pParse.nQueryLoop = 1; |
||
795 | |||
796 | if ( SQLITE_OK == sqlite3RunParser( pParse, zCreateTable, ref zErr ) |
||
797 | && pParse.pNewTable != null |
||
798 | //&& !db.mallocFailed |
||
799 | && null==pParse.pNewTable.pSelect |
||
800 | && ( pParse.pNewTable.tabFlags & TF_Virtual ) == 0 |
||
801 | ) |
||
802 | { |
||
803 | if ( null==pTab.aCol ) |
||
804 | { |
||
805 | pTab.aCol = pParse.pNewTable.aCol; |
||
806 | pTab.nCol = pParse.pNewTable.nCol; |
||
807 | pParse.pNewTable.nCol = 0; |
||
808 | pParse.pNewTable.aCol = null; |
||
809 | } |
||
810 | db.pVtabCtx.pTab = null; |
||
811 | } |
||
812 | else |
||
813 | { |
||
814 | sqlite3Error( db, SQLITE_ERROR, ( zErr != null ? "%s" : null ), zErr ); |
||
815 | zErr = null;//sqlite3DbFree( db, zErr ); |
||
816 | rc = SQLITE_ERROR; |
||
817 | } |
||
818 | pParse.declareVtab = 0; |
||
819 | |||
820 | if ( pParse.pVdbe !=null) |
||
821 | { |
||
822 | sqlite3VdbeFinalize( ref pParse.pVdbe ); |
||
823 | } |
||
824 | sqlite3DeleteTable( db, ref pParse.pNewTable ); |
||
825 | //sqlite3StackFree( db, pParse ); |
||
826 | } |
||
827 | |||
828 | Debug.Assert( ( rc & 0xff ) == rc ); |
||
829 | rc = sqlite3ApiExit( db, rc ); |
||
830 | sqlite3_mutex_leave( db.mutex ); |
||
831 | return rc; |
||
832 | } |
||
833 | |||
834 | /* |
||
835 | ** This function is invoked by the vdbe to call the xDestroy method |
||
836 | ** of the virtual table named zTab in database iDb. This occurs |
||
837 | ** when a DROP TABLE is mentioned. |
||
838 | ** |
||
839 | ** This call is a no-op if zTab is not a virtual table. |
||
840 | */ |
||
841 | static int sqlite3VtabCallDestroy( sqlite3 db, int iDb, string zTab ) |
||
842 | { |
||
843 | int rc = SQLITE_OK; |
||
844 | Table pTab; |
||
845 | |||
846 | pTab = sqlite3FindTable( db, zTab, db.aDb[iDb].zName ); |
||
847 | if ( ALWAYS( pTab != null && pTab.pVTable != null ) ) |
||
848 | { |
||
849 | VTable p = vtabDisconnectAll( db, pTab ); |
||
850 | |||
851 | Debug.Assert( rc == SQLITE_OK ); |
||
852 | object obj = p.pVtab; |
||
853 | rc = p.pMod.pModule.xDestroy( ref obj ); |
||
854 | p.pVtab = null; |
||
855 | |||
856 | /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ |
||
857 | if ( rc == SQLITE_OK ) |
||
858 | { |
||
859 | Debug.Assert( pTab.pVTable == p && p.pNext == null ); |
||
860 | p.pVtab = null; |
||
861 | pTab.pVTable = null; |
||
862 | sqlite3VtabUnlock( p ); |
||
863 | } |
||
864 | } |
||
865 | |||
866 | return rc; |
||
867 | } |
||
868 | |||
869 | /* |
||
870 | ** This function invokes either the xRollback or xCommit method |
||
871 | ** of each of the virtual tables in the sqlite3.aVTrans array. The method |
||
872 | ** called is identified by the second argument, "offset", which is |
||
873 | ** the offset of the method to call in the sqlite3_module structure. |
||
874 | ** |
||
875 | ** The array is cleared after invoking the callbacks. |
||
876 | */ |
||
877 | static void callFinaliser( sqlite3 db, int offset ) |
||
878 | { |
||
879 | int i; |
||
880 | if ( db.aVTrans != null ) |
||
881 | { |
||
882 | for ( i = 0; i < db.nVTrans; i++ ) |
||
883 | { |
||
884 | VTable pVTab = db.aVTrans[i]; |
||
885 | sqlite3_vtab p = pVTab.pVtab; |
||
886 | if ( p != null ) |
||
887 | { |
||
888 | //int (*x)(sqlite3_vtab ); |
||
889 | //x = *(int (*)(sqlite3_vtab ))((char )p.pModule + offset); |
||
890 | //if( x ) x(p); |
||
891 | if ( offset == 0 ) |
||
892 | { |
||
893 | if ( p.pModule.xCommit != null ) |
||
894 | p.pModule.xCommit( p ); |
||
895 | } |
||
896 | else |
||
897 | { |
||
898 | if ( p.pModule.xRollback != null ) |
||
899 | p.pModule.xRollback( p ); |
||
900 | } |
||
901 | } |
||
902 | pVTab.iSavepoint = 0; |
||
903 | sqlite3VtabUnlock( pVTab ); |
||
904 | } |
||
905 | sqlite3DbFree( db, ref db.aVTrans ); |
||
906 | db.nVTrans = 0; |
||
907 | db.aVTrans = null; |
||
908 | } |
||
909 | } |
||
910 | |||
911 | /* |
||
912 | ** Invoke the xSync method of all virtual tables in the sqlite3.aVTrans |
||
913 | ** array. Return the error code for the first error that occurs, or |
||
914 | ** SQLITE_OK if all xSync operations are successful. |
||
915 | ** |
||
916 | ** Set *pzErrmsg to point to a buffer that should be released using |
||
917 | ** sqlite3DbFree() containing an error message, if one is available. |
||
918 | */ |
||
919 | static int sqlite3VtabSync( sqlite3 db, ref string pzErrmsg ) |
||
920 | { |
||
921 | int i; |
||
922 | int rc = SQLITE_OK; |
||
923 | VTable[] aVTrans = db.aVTrans; |
||
924 | |||
925 | db.aVTrans = null; |
||
926 | for ( i = 0; rc == SQLITE_OK && i < db.nVTrans; i++ ) |
||
927 | { |
||
928 | smdxFunction x;//int (*x)(sqlite3_vtab ); |
||
929 | sqlite3_vtab pVtab = aVTrans[i].pVtab; |
||
930 | if ( pVtab != null && ( x = pVtab.pModule.xSync ) != null ) |
||
931 | { |
||
932 | rc = x( pVtab ); |
||
933 | //sqlite3DbFree(db, ref pzErrmsg); |
||
934 | pzErrmsg = pVtab.zErrMsg;// sqlite3DbStrDup( db, pVtab.zErrMsg ); |
||
935 | pVtab.zErrMsg = null;//sqlite3_free( ref pVtab.zErrMsg ); |
||
936 | } |
||
937 | } |
||
938 | db.aVTrans = aVTrans; |
||
939 | return rc; |
||
940 | } |
||
941 | |||
942 | /* |
||
943 | ** Invoke the xRollback method of all virtual tables in the |
||
944 | ** sqlite3.aVTrans array. Then clear the array itself. |
||
945 | */ |
||
946 | static int sqlite3VtabRollback( sqlite3 db ) |
||
947 | { |
||
948 | callFinaliser( db, 1 );//offsetof( sqlite3_module, xRollback ) ); |
||
949 | return SQLITE_OK; |
||
950 | } |
||
951 | |||
952 | /* |
||
953 | ** Invoke the xCommit method of all virtual tables in the |
||
954 | ** sqlite3.aVTrans array. Then clear the array itself. |
||
955 | */ |
||
956 | static int sqlite3VtabCommit( sqlite3 db ) |
||
957 | { |
||
958 | callFinaliser( db, 0 );//offsetof( sqlite3_module, xCommit ) ); |
||
959 | return SQLITE_OK; |
||
960 | } |
||
961 | |||
962 | /* |
||
963 | ** If the virtual table pVtab supports the transaction interface |
||
964 | ** (xBegin/xRollback/xCommit and optionally xSync) and a transaction is |
||
965 | ** not currently open, invoke the xBegin method now. |
||
966 | ** |
||
967 | ** If the xBegin call is successful, place the sqlite3_vtab pointer |
||
968 | ** in the sqlite3.aVTrans array. |
||
969 | */ |
||
970 | static int sqlite3VtabBegin( sqlite3 db, VTable pVTab ) |
||
971 | { |
||
972 | int rc = SQLITE_OK; |
||
973 | sqlite3_module pModule; |
||
974 | |||
975 | /* Special case: If db.aVTrans is NULL and db.nVTrans is greater |
||
976 | ** than zero, then this function is being called from within a |
||
977 | ** virtual module xSync() callback. It is illegal to write to |
||
978 | ** virtual module tables in this case, so return SQLITE_LOCKED. |
||
979 | */ |
||
980 | if ( sqlite3VtabInSync( db ) ) |
||
981 | { |
||
982 | return SQLITE_LOCKED; |
||
983 | } |
||
984 | if ( null == pVTab ) |
||
985 | { |
||
986 | return SQLITE_OK; |
||
987 | } |
||
988 | pModule = pVTab.pVtab.pModule; |
||
989 | |||
990 | if ( pModule.xBegin != null ) |
||
991 | { |
||
992 | int i; |
||
993 | |||
994 | /* If pVtab is already in the aVTrans array, return early */ |
||
995 | for ( i = 0; i < db.nVTrans; i++ ) |
||
996 | { |
||
997 | if ( db.aVTrans[i] == pVTab ) |
||
998 | { |
||
999 | return SQLITE_OK; |
||
1000 | } |
||
1001 | } |
||
1002 | |||
1003 | /* Invoke the xBegin method. If successful, add the vtab to the |
||
1004 | ** sqlite3.aVTrans[] array. */ |
||
1005 | rc = growVTrans( db ); |
||
1006 | if ( rc == SQLITE_OK ) |
||
1007 | { |
||
1008 | rc = pModule.xBegin( pVTab.pVtab ); |
||
1009 | if ( rc == SQLITE_OK ) |
||
1010 | { |
||
1011 | addToVTrans( db, pVTab ); |
||
1012 | } |
||
1013 | } |
||
1014 | } |
||
1015 | return rc; |
||
1016 | } |
||
1017 | |||
1018 | /* |
||
1019 | ** Invoke either the xSavepoint, xRollbackTo or xRelease method of all |
||
1020 | ** virtual tables that currently have an open transaction. Pass iSavepoint |
||
1021 | ** as the second argument to the virtual table method invoked. |
||
1022 | ** |
||
1023 | ** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is |
||
1024 | ** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is |
||
1025 | ** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with |
||
1026 | ** an open transaction is invoked. |
||
1027 | ** |
||
1028 | ** If any virtual table method returns an error code other than SQLITE_OK, |
||
1029 | ** processing is abandoned and the error returned to the caller of this |
||
1030 | ** function immediately. If all calls to virtual table methods are successful, |
||
1031 | ** SQLITE_OK is returned. |
||
1032 | */ |
||
1033 | static int sqlite3VtabSavepoint( sqlite3 db, int op, int iSavepoint ) |
||
1034 | { |
||
1035 | int rc = SQLITE_OK; |
||
1036 | |||
1037 | Debug.Assert( op == SAVEPOINT_RELEASE || op == SAVEPOINT_ROLLBACK || op == SAVEPOINT_BEGIN ); |
||
1038 | Debug.Assert( iSavepoint >= 0 ); |
||
1039 | if ( db.aVTrans != null ) |
||
1040 | { |
||
1041 | int i; |
||
1042 | for ( i = 0; rc == SQLITE_OK && i < db.nVTrans; i++ ) |
||
1043 | { |
||
1044 | VTable pVTab = db.aVTrans[i]; |
||
1045 | sqlite3_module pMod = pVTab.pMod.pModule; |
||
1046 | if ( pMod.iVersion >= 2 ) |
||
1047 | { |
||
1048 | smdxFunctionArg xMethod = null; //int (*xMethod)(sqlite3_vtab *, int); |
||
1049 | switch ( op ) |
||
1050 | { |
||
1051 | case SAVEPOINT_BEGIN: |
||
1052 | xMethod = pMod.xSavepoint; |
||
1053 | pVTab.iSavepoint = iSavepoint + 1; |
||
1054 | break; |
||
1055 | case SAVEPOINT_ROLLBACK: |
||
1056 | xMethod = pMod.xRollbackTo; |
||
1057 | break; |
||
1058 | default: |
||
1059 | xMethod = pMod.xRelease; |
||
1060 | break; |
||
1061 | } |
||
1062 | if ( xMethod != null && pVTab.iSavepoint > iSavepoint ) |
||
1063 | { |
||
1064 | rc = xMethod( db.aVTrans[i].pVtab, iSavepoint ); |
||
1065 | } |
||
1066 | } |
||
1067 | } |
||
1068 | } |
||
1069 | return rc; |
||
1070 | } |
||
1071 | |||
1072 | /* |
||
1073 | ** The first parameter (pDef) is a function implementation. The |
||
1074 | ** second parameter (pExpr) is the first argument to this function. |
||
1075 | ** If pExpr is a column in a virtual table, then let the virtual |
||
1076 | ** table implementation have an opportunity to overload the function. |
||
1077 | ** |
||
1078 | ** This routine is used to allow virtual table implementations to |
||
1079 | ** overload MATCH, LIKE, GLOB, and REGEXP operators. |
||
1080 | ** |
||
1081 | ** Return either the pDef argument (indicating no change) or a |
||
1082 | ** new FuncDef structure that is marked as ephemeral using the |
||
1083 | ** SQLITE_FUNC_EPHEM flag. |
||
1084 | */ |
||
1085 | static FuncDef sqlite3VtabOverloadFunction( |
||
1086 | sqlite3 db, /* Database connection for reporting malloc problems */ |
||
1087 | FuncDef pDef, /* Function to possibly overload */ |
||
1088 | int nArg, /* Number of arguments to the function */ |
||
1089 | Expr pExpr /* First argument to the function */ |
||
1090 | ) |
||
1091 | { |
||
1092 | Table pTab; |
||
1093 | sqlite3_vtab pVtab; |
||
1094 | sqlite3_module pMod; |
||
1095 | dxFunc xFunc = null;//void (*xFunc)(sqlite3_context*,int,sqlite3_value*) = 0; |
||
1096 | object pArg = null; |
||
1097 | FuncDef pNew; |
||
1098 | int rc = 0; |
||
1099 | string zLowerName; |
||
1100 | string z; |
||
1101 | |||
1102 | /* Check to see the left operand is a column in a virtual table */ |
||
1103 | if ( NEVER( pExpr == null ) ) |
||
1104 | return pDef; |
||
1105 | if ( pExpr.op != TK_COLUMN ) |
||
1106 | return pDef; |
||
1107 | pTab = pExpr.pTab; |
||
1108 | if ( NEVER( pTab == null ) ) |
||
1109 | return pDef; |
||
1110 | if ( ( pTab.tabFlags & TF_Virtual ) == 0 ) |
||
1111 | return pDef; |
||
1112 | pVtab = sqlite3GetVTable( db, pTab ).pVtab; |
||
1113 | Debug.Assert( pVtab != null ); |
||
1114 | Debug.Assert( pVtab.pModule != null ); |
||
1115 | pMod = (sqlite3_module)pVtab.pModule; |
||
1116 | if ( pMod.xFindFunction == null ) |
||
1117 | return pDef; |
||
1118 | |||
1119 | /* Call the xFindFunction method on the virtual table implementation |
||
1120 | ** to see if the implementation wants to overload this function |
||
1121 | */ |
||
1122 | zLowerName = pDef.zName;//sqlite3DbStrDup(db, pDef.zName); |
||
1123 | if ( zLowerName != null ) |
||
1124 | { |
||
1125 | //for(z=(unsigned char)zLowerName; *z; z++){ |
||
1126 | // *z = sqlite3UpperToLower[*z]; |
||
1127 | //} |
||
1128 | rc = pMod.xFindFunction( pVtab, nArg, zLowerName.ToLowerInvariant(), ref xFunc, ref pArg ); |
||
1129 | sqlite3DbFree( db, ref zLowerName ); |
||
1130 | } |
||
1131 | if ( rc == 0 ) |
||
1132 | { |
||
1133 | return pDef; |
||
1134 | } |
||
1135 | |||
1136 | /* Create a new ephemeral function definition for the overloaded |
||
1137 | ** function */ |
||
1138 | //sqlite3DbMallocZero(db, sizeof(*pNew) |
||
1139 | // + sqlite3Strlen30(pDef.zName) + 1); |
||
1140 | //if ( pNew == null ) |
||
1141 | //{ |
||
1142 | // return pDef; |
||
1143 | //} |
||
1144 | pNew = pDef.Copy(); |
||
1145 | pNew.zName = pDef.zName; |
||
1146 | //pNew.zName = (char )&pNew[1]; |
||
1147 | //memcpy(pNew.zName, pDef.zName, sqlite3Strlen30(pDef.zName)+1); |
||
1148 | pNew.xFunc = xFunc; |
||
1149 | pNew.pUserData = pArg; |
||
1150 | pNew.flags |= SQLITE_FUNC_EPHEM; |
||
1151 | return pNew; |
||
1152 | } |
||
1153 | |||
1154 | /* |
||
1155 | ** Make sure virtual table pTab is contained in the pParse.apVirtualLock[] |
||
1156 | ** array so that an OP_VBegin will get generated for it. Add pTab to the |
||
1157 | ** array if it is missing. If pTab is already in the array, this routine |
||
1158 | ** is a no-op. |
||
1159 | */ |
||
1160 | static void sqlite3VtabMakeWritable( Parse pParse, Table pTab ) |
||
1161 | { |
||
1162 | Parse pToplevel = sqlite3ParseToplevel( pParse ); |
||
1163 | int i, n; |
||
1164 | //Table[] apVtabLock = null; |
||
1165 | |||
1166 | Debug.Assert( IsVirtual( pTab ) ); |
||
1167 | for ( i = 0; i < pToplevel.nVtabLock; i++ ) |
||
1168 | { |
||
1169 | if ( pTab == pToplevel.apVtabLock[i] ) |
||
1170 | return; |
||
1171 | } |
||
1172 | n = pToplevel.apVtabLock == null ? 1 : pToplevel.apVtabLock.Length + 1;//(pToplevel.nVtabLock+1)*sizeof(pToplevel.apVtabLock[0]); |
||
1173 | //sqlite3_realloc( pToplevel.apVtabLock, n ); |
||
1174 | //if ( apVtabLock != null ) |
||
1175 | { |
||
1176 | Array.Resize( ref pToplevel.apVtabLock, n );// pToplevel.apVtabLock= apVtabLock; |
||
1177 | pToplevel.apVtabLock[pToplevel.nVtabLock++] = pTab; |
||
1178 | } |
||
1179 | //else |
||
1180 | //{ |
||
1181 | // pToplevel.db.mallocFailed = 1; |
||
1182 | //} |
||
1183 | } |
||
1184 | |||
1185 | static int[] aMap = new int[] { |
||
1186 | SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE |
||
1187 | }; |
||
1188 | /* |
||
1189 | ** Return the ON CONFLICT resolution mode in effect for the virtual |
||
1190 | ** table update operation currently in progress. |
||
1191 | ** |
||
1192 | ** The results of this routine are undefined unless it is called from |
||
1193 | ** within an xUpdate method. |
||
1194 | */ |
||
1195 | static int sqlite3_vtab_on_conflict( sqlite3 db ){ |
||
1196 | //static const unsigned char aMap[] = { |
||
1197 | // SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE |
||
1198 | //}; |
||
1199 | Debug.Assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 ); |
||
1200 | Debug.Assert( OE_Ignore==4 && OE_Replace==5 ); |
||
1201 | Debug.Assert( db.vtabOnConflict>=1 && db.vtabOnConflict<=5 ); |
||
1202 | return (int)aMap[db.vtabOnConflict-1]; |
||
1203 | } |
||
1204 | |||
1205 | /* |
||
1206 | ** Call from within the xCreate() or xConnect() methods to provide |
||
1207 | ** the SQLite core with additional information about the behavior |
||
1208 | ** of the virtual table being implemented. |
||
1209 | */ |
||
1210 | static int sqlite3_vtab_config( sqlite3 db, int op, params object[] ap ){ // TODO ...){ |
||
1211 | //va_list ap; |
||
1212 | int rc = SQLITE_OK; |
||
1213 | |||
1214 | sqlite3_mutex_enter(db.mutex); |
||
1215 | |||
1216 | va_start(ap, "op"); |
||
1217 | switch( op ){ |
||
1218 | case SQLITE_VTAB_CONSTRAINT_SUPPORT: { |
||
1219 | VtabCtx p = db.pVtabCtx; |
||
1220 | if( null == p ){ |
||
1221 | rc = SQLITE_MISUSE_BKPT(); |
||
1222 | }else{ |
||
1223 | Debug.Assert( p.pTab == null || ( p.pTab.tabFlags & TF_Virtual ) != 0 ); |
||
1224 | p.pVTable.bConstraint = (Byte)va_arg(ap, (Int32)0); |
||
1225 | } |
||
1226 | break; |
||
1227 | } |
||
1228 | default: |
||
1229 | rc = SQLITE_MISUSE_BKPT(); |
||
1230 | break; |
||
1231 | } |
||
1232 | va_end(ref ap); |
||
1233 | |||
1234 | if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0); |
||
1235 | sqlite3_mutex_leave(db.mutex); |
||
1236 | return rc; |
||
1237 | } |
||
1238 | |||
1239 | #endif //* SQLITE_OMIT_VIRTUALTABLE */ |
||
1240 | } |
||
1241 | } |