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 | using u32 = System.UInt32; |
||
7 | namespace Community.CsharpSqlite |
||
8 | { |
||
9 | public partial class Sqlite3 |
||
10 | { |
||
11 | /* |
||
12 | ** |
||
13 | ** The author disclaims copyright to this source code. In place of |
||
14 | ** a legal notice, here is a blessing: |
||
15 | ** |
||
16 | ** May you do good and not evil. |
||
17 | ** May you find forgiveness for yourself and forgive others. |
||
18 | ** May you share freely, never taking more than you give. |
||
19 | ** |
||
20 | ************************************************************************* |
||
21 | ** This file contains the implementation for TRIGGERs |
||
22 | ************************************************************************* |
||
23 | ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
||
24 | ** C#-SQLite is an independent reimplementation of the SQLite software library |
||
25 | ** |
||
26 | ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2 |
||
27 | ** |
||
28 | ************************************************************************* |
||
29 | */ |
||
30 | //#include "sqliteInt.h" |
||
31 | |||
32 | #if !SQLITE_OMIT_TRIGGER |
||
33 | /* |
||
34 | ** Delete a linked list of TriggerStep structures. |
||
35 | */ |
||
36 | static void sqlite3DeleteTriggerStep( sqlite3 db, ref TriggerStep pTriggerStep ) |
||
37 | { |
||
38 | while ( pTriggerStep != null ) |
||
39 | { |
||
40 | TriggerStep pTmp = pTriggerStep; |
||
41 | pTriggerStep = pTriggerStep.pNext; |
||
42 | |||
43 | sqlite3ExprDelete( db, ref pTmp.pWhere ); |
||
44 | sqlite3ExprListDelete( db, ref pTmp.pExprList ); |
||
45 | sqlite3SelectDelete( db, ref pTmp.pSelect ); |
||
46 | sqlite3IdListDelete( db, ref pTmp.pIdList ); |
||
47 | |||
48 | pTriggerStep = null; |
||
49 | sqlite3DbFree( db, ref pTmp ); |
||
50 | } |
||
51 | } |
||
52 | |||
53 | /* |
||
54 | ** Given table pTab, return a list of all the triggers attached to |
||
55 | ** the table. The list is connected by Trigger.pNext pointers. |
||
56 | ** |
||
57 | ** All of the triggers on pTab that are in the same database as pTab |
||
58 | ** are already attached to pTab.pTrigger. But there might be additional |
||
59 | ** triggers on pTab in the TEMP schema. This routine prepends all |
||
60 | ** TEMP triggers on pTab to the beginning of the pTab.pTrigger list |
||
61 | ** and returns the combined list. |
||
62 | ** |
||
63 | ** To state it another way: This routine returns a list of all triggers |
||
64 | ** that fire off of pTab. The list will include any TEMP triggers on |
||
65 | ** pTab as well as the triggers lised in pTab.pTrigger. |
||
66 | */ |
||
67 | static Trigger sqlite3TriggerList( Parse pParse, Table pTab ) |
||
68 | { |
||
69 | Schema pTmpSchema = pParse.db.aDb[1].pSchema; |
||
70 | Trigger pList = null; /* List of triggers to return */ |
||
71 | |||
72 | if ( pParse.disableTriggers != 0 ) |
||
73 | { |
||
74 | return null; |
||
75 | } |
||
76 | |||
77 | if ( pTmpSchema != pTab.pSchema ) |
||
78 | { |
||
79 | HashElem p; |
||
80 | Debug.Assert( sqlite3SchemaMutexHeld( pParse.db, 0, pTmpSchema ) ); |
||
81 | for ( p = sqliteHashFirst( pTmpSchema.trigHash ); p != null; p = sqliteHashNext( p ) ) |
||
82 | { |
||
83 | Trigger pTrig = (Trigger)sqliteHashData( p ); |
||
84 | if ( pTrig.pTabSchema == pTab.pSchema |
||
85 | && pTrig.table.Equals( pTab.zName, StringComparison.OrdinalIgnoreCase ) ) |
||
86 | { |
||
87 | pTrig.pNext = ( pList != null ? pList : pTab.pTrigger ); |
||
88 | pList = pTrig; |
||
89 | } |
||
90 | } |
||
91 | } |
||
92 | |||
93 | return ( pList != null ? pList : pTab.pTrigger ); |
||
94 | } |
||
95 | |||
96 | /* |
||
97 | ** This is called by the parser when it sees a CREATE TRIGGER statement |
||
98 | ** up to the point of the BEGIN before the trigger actions. A Trigger |
||
99 | ** structure is generated based on the information available and stored |
||
100 | ** in pParse.pNewTrigger. After the trigger actions have been parsed, the |
||
101 | ** sqlite3FinishTrigger() function is called to complete the trigger |
||
102 | ** construction process. |
||
103 | */ |
||
104 | static void sqlite3BeginTrigger( |
||
105 | Parse pParse, /* The parse context of the CREATE TRIGGER statement */ |
||
106 | Token pName1, /* The name of the trigger */ |
||
107 | Token pName2, /* The name of the trigger */ |
||
108 | int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ |
||
109 | int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ |
||
110 | IdList pColumns, /* column list if this is an UPDATE OF trigger */ |
||
111 | SrcList pTableName,/* The name of the table/view the trigger applies to */ |
||
112 | Expr pWhen, /* WHEN clause */ |
||
113 | int isTemp, /* True if the TEMPORARY keyword is present */ |
||
114 | int noErr /* Suppress errors if the trigger already exists */ |
||
115 | ) |
||
116 | { |
||
117 | Trigger pTrigger = null; /* The new trigger */ |
||
118 | Table pTab; /* Table that the trigger fires off of */ |
||
119 | string zName = null; /* Name of the trigger */ |
||
120 | sqlite3 db = pParse.db; /* The database connection */ |
||
121 | int iDb; /* The database to store the trigger in */ |
||
122 | Token pName = null; /* The unqualified db name */ |
||
123 | DbFixer sFix = new DbFixer(); /* State vector for the DB fixer */ |
||
124 | |||
125 | Debug.Assert( pName1 != null ); /* pName1.z might be NULL, but not pName1 itself */ |
||
126 | Debug.Assert( pName2 != null ); |
||
127 | Debug.Assert( op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE ); |
||
128 | Debug.Assert( op > 0 && op < 0xff ); |
||
129 | if ( isTemp != 0 ) |
||
130 | { |
||
131 | /* If TEMP was specified, then the trigger name may not be qualified. */ |
||
132 | if ( pName2.n > 0 ) |
||
133 | { |
||
134 | sqlite3ErrorMsg( pParse, "temporary trigger may not have qualified name" ); |
||
135 | goto trigger_cleanup; |
||
136 | } |
||
137 | iDb = 1; |
||
138 | pName = pName1; |
||
139 | } |
||
140 | else |
||
141 | { |
||
142 | /* Figure out the db that the the trigger will be created in */ |
||
143 | iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pName ); |
||
144 | if ( iDb < 0 ) |
||
145 | { |
||
146 | goto trigger_cleanup; |
||
147 | } |
||
148 | } |
||
149 | if ( null == pTableName ) //|| db.mallocFailed |
||
150 | { |
||
151 | goto trigger_cleanup; |
||
152 | } |
||
153 | |||
154 | /* A long-standing parser bug is that this syntax was allowed: |
||
155 | ** |
||
156 | ** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... |
||
157 | ** ^^^^^^^^ |
||
158 | ** |
||
159 | ** To maintain backwards compatibility, ignore the database |
||
160 | ** name on pTableName if we are reparsing our of SQLITE_MASTER. |
||
161 | */ |
||
162 | if ( db.init.busy != 0 && iDb != 1 ) |
||
163 | { |
||
164 | //sqlite3DbFree( db, pTableName.a[0].zDatabase ); |
||
165 | pTableName.a[0].zDatabase = null; |
||
166 | } |
||
167 | |||
168 | /* If the trigger name was unqualified, and the table is a temp table, |
||
169 | ** then set iDb to 1 to create the trigger in the temporary database. |
||
170 | ** If sqlite3SrcListLookup() returns 0, indicating the table does not |
||
171 | ** exist, the error is caught by the block below. |
||
172 | */ |
||
173 | if ( pTableName == null /*|| db.mallocFailed != 0 */ ) |
||
174 | { |
||
175 | goto trigger_cleanup; |
||
176 | } |
||
177 | pTab = sqlite3SrcListLookup( pParse, pTableName ); |
||
178 | if ( db.init.busy == 0 && pName2.n == 0 && pTab != null |
||
179 | && pTab.pSchema == db.aDb[1].pSchema ) |
||
180 | { |
||
181 | iDb = 1; |
||
182 | } |
||
183 | |||
184 | /* Ensure the table name matches database name and that the table exists */ |
||
185 | // if ( db.mallocFailed != 0 ) goto trigger_cleanup; |
||
186 | Debug.Assert( pTableName.nSrc == 1 ); |
||
187 | if ( sqlite3FixInit( sFix, pParse, iDb, "trigger", pName ) != 0 && |
||
188 | sqlite3FixSrcList( sFix, pTableName ) != 0 ) |
||
189 | { |
||
190 | goto trigger_cleanup; |
||
191 | } |
||
192 | pTab = sqlite3SrcListLookup( pParse, pTableName ); |
||
193 | if ( pTab == null ) |
||
194 | { |
||
195 | /* The table does not exist. */ |
||
196 | if ( db.init.iDb == 1 ) |
||
197 | { |
||
198 | /* Ticket #3810. |
||
199 | ** Normally, whenever a table is dropped, all associated triggers are |
||
200 | ** dropped too. But if a TEMP trigger is created on a non-TEMP table |
||
201 | ** and the table is dropped by a different database connection, the |
||
202 | ** trigger is not visible to the database connection that does the |
||
203 | ** drop so the trigger cannot be dropped. This results in an |
||
204 | ** "orphaned trigger" - a trigger whose associated table is missing. |
||
205 | */ |
||
206 | db.init.orphanTrigger = 1; |
||
207 | } |
||
208 | goto trigger_cleanup; |
||
209 | } |
||
210 | if ( IsVirtual( pTab ) ) |
||
211 | { |
||
212 | sqlite3ErrorMsg( pParse, "cannot create triggers on virtual tables" ); |
||
213 | goto trigger_cleanup; |
||
214 | } |
||
215 | |||
216 | /* Check that the trigger name is not reserved and that no trigger of the |
||
217 | ** specified name exists */ |
||
218 | zName = sqlite3NameFromToken( db, pName ); |
||
219 | if ( zName == null || SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) ) |
||
220 | { |
||
221 | goto trigger_cleanup; |
||
222 | } |
||
223 | Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); |
||
224 | if ( sqlite3HashFind( ( db.aDb[iDb].pSchema.trigHash ), |
||
225 | zName, sqlite3Strlen30( zName ), (Trigger)null ) != null ) |
||
226 | { |
||
227 | if ( noErr == 0 ) |
||
228 | { |
||
229 | sqlite3ErrorMsg( pParse, "trigger %T already exists", pName ); |
||
230 | } |
||
231 | else |
||
232 | { |
||
233 | Debug.Assert( 0==db.init.busy ); |
||
234 | sqlite3CodeVerifySchema( pParse, iDb ); |
||
235 | } |
||
236 | goto trigger_cleanup; |
||
237 | } |
||
238 | |||
239 | /* Do not create a trigger on a system table */ |
||
240 | if ( pTab.zName.StartsWith( "sqlite_", System.StringComparison.OrdinalIgnoreCase ) ) |
||
241 | { |
||
242 | sqlite3ErrorMsg( pParse, "cannot create trigger on system table" ); |
||
243 | pParse.nErr++; |
||
244 | goto trigger_cleanup; |
||
245 | } |
||
246 | |||
247 | /* INSTEAD of triggers are only for views and views only support INSTEAD |
||
248 | ** of triggers. |
||
249 | */ |
||
250 | if ( pTab.pSelect != null && tr_tm != TK_INSTEAD ) |
||
251 | { |
||
252 | sqlite3ErrorMsg( pParse, "cannot create %s trigger on view: %S", |
||
253 | ( tr_tm == TK_BEFORE ) ? "BEFORE" : "AFTER", pTableName, 0 ); |
||
254 | goto trigger_cleanup; |
||
255 | } |
||
256 | if ( pTab.pSelect == null && tr_tm == TK_INSTEAD ) |
||
257 | { |
||
258 | sqlite3ErrorMsg( pParse, "cannot create INSTEAD OF" + |
||
259 | " trigger on table: %S", pTableName, 0 ); |
||
260 | goto trigger_cleanup; |
||
261 | } |
||
262 | |||
263 | #if !SQLITE_OMIT_AUTHORIZATION |
||
264 | { |
||
265 | int iTabDb = sqlite3SchemaToIndex( db, pTab.pSchema ); |
||
266 | int code = SQLITE_CREATE_TRIGGER; |
||
267 | string zDb = db.aDb[iTabDb].zName; |
||
268 | string zDbTrig = isTemp ? db.aDb[1].zName : zDb; |
||
269 | if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; |
||
270 | if( sqlite3AuthCheck(pParse, code, zName, pTab.zName, zDbTrig) ){ |
||
271 | goto trigger_cleanup; |
||
272 | } |
||
273 | if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb),0,zDb)){ |
||
274 | goto trigger_cleanup; |
||
275 | } |
||
276 | } |
||
277 | #endif |
||
278 | |||
279 | /* INSTEAD OF triggers can only appear on views and BEFORE triggers |
||
280 | ** cannot appear on views. So we might as well translate every |
||
281 | ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code |
||
282 | ** elsewhere. |
||
283 | */ |
||
284 | if ( tr_tm == TK_INSTEAD ) |
||
285 | { |
||
286 | tr_tm = TK_BEFORE; |
||
287 | } |
||
288 | |||
289 | /* Build the Trigger object */ |
||
290 | pTrigger = new Trigger();// (Trigger*)sqlite3DbMallocZero( db, sizeof(Trigger )) |
||
291 | if ( pTrigger == null ) |
||
292 | goto trigger_cleanup; |
||
293 | pTrigger.zName = zName; |
||
294 | pTrigger.table = pTableName.a[0].zName;// sqlite3DbStrDup( db, pTableName.a[0].zName ); |
||
295 | pTrigger.pSchema = db.aDb[iDb].pSchema; |
||
296 | pTrigger.pTabSchema = pTab.pSchema; |
||
297 | pTrigger.op = (u8)op; |
||
298 | pTrigger.tr_tm = tr_tm == TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; |
||
299 | pTrigger.pWhen = sqlite3ExprDup( db, pWhen, EXPRDUP_REDUCE ); |
||
300 | pTrigger.pColumns = sqlite3IdListDup( db, pColumns ); |
||
301 | Debug.Assert( pParse.pNewTrigger == null ); |
||
302 | pParse.pNewTrigger = pTrigger; |
||
303 | |||
304 | trigger_cleanup: |
||
305 | sqlite3DbFree( db, ref zName ); |
||
306 | sqlite3SrcListDelete( db, ref pTableName ); |
||
307 | sqlite3IdListDelete( db, ref pColumns ); |
||
308 | sqlite3ExprDelete( db, ref pWhen ); |
||
309 | if ( pParse.pNewTrigger == null ) |
||
310 | { |
||
311 | sqlite3DeleteTrigger( db, ref pTrigger ); |
||
312 | } |
||
313 | else |
||
314 | { |
||
315 | Debug.Assert( pParse.pNewTrigger == pTrigger ); |
||
316 | } |
||
317 | } |
||
318 | |||
319 | /* |
||
320 | ** This routine is called after all of the trigger actions have been parsed |
||
321 | ** in order to complete the process of building the trigger. |
||
322 | */ |
||
323 | static void sqlite3FinishTrigger( |
||
324 | Parse pParse, /* Parser context */ |
||
325 | TriggerStep pStepList, /* The triggered program */ |
||
326 | Token pAll /* Token that describes the complete CREATE TRIGGER */ |
||
327 | ) |
||
328 | { |
||
329 | Trigger pTrig = pParse.pNewTrigger; /* Trigger being finished */ |
||
330 | string zName; /* Name of trigger */ |
||
331 | |||
332 | sqlite3 db = pParse.db; /* The database */ |
||
333 | DbFixer sFix = new DbFixer(); /* Fixer object */ |
||
334 | int iDb; /* Database containing the trigger */ |
||
335 | Token nameToken = new Token(); /* Trigger name for error reporting */ |
||
336 | |||
337 | pParse.pNewTrigger = null; |
||
338 | if ( NEVER( pParse.nErr != 0 ) || pTrig == null ) |
||
339 | goto triggerfinish_cleanup; |
||
340 | zName = pTrig.zName; |
||
341 | iDb = sqlite3SchemaToIndex( pParse.db, pTrig.pSchema ); |
||
342 | pTrig.step_list = pStepList; |
||
343 | while ( pStepList != null ) |
||
344 | { |
||
345 | pStepList.pTrig = pTrig; |
||
346 | pStepList = pStepList.pNext; |
||
347 | } |
||
348 | nameToken.z = pTrig.zName; |
||
349 | nameToken.n = sqlite3Strlen30( nameToken.z ); |
||
350 | if ( sqlite3FixInit( sFix, pParse, iDb, "trigger", nameToken ) != 0 |
||
351 | && sqlite3FixTriggerStep( sFix, pTrig.step_list ) != 0 ) |
||
352 | { |
||
353 | goto triggerfinish_cleanup; |
||
354 | } |
||
355 | |||
356 | /* if we are not initializing, |
||
357 | ** build the sqlite_master entry |
||
358 | */ |
||
359 | if ( 0 == db.init.busy ) |
||
360 | { |
||
361 | Vdbe v; |
||
362 | string z; |
||
363 | |||
364 | /* Make an entry in the sqlite_master table */ |
||
365 | v = sqlite3GetVdbe( pParse ); |
||
366 | if ( v == null ) |
||
367 | goto triggerfinish_cleanup; |
||
368 | sqlite3BeginWriteOperation( pParse, 0, iDb ); |
||
369 | z = pAll.z.Substring( 0, pAll.n );//sqlite3DbStrNDup( db, (char*)pAll.z, pAll.n ); |
||
370 | sqlite3NestedParse( pParse, |
||
371 | "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", |
||
372 | db.aDb[iDb].zName, SCHEMA_TABLE( iDb ), zName, |
||
373 | pTrig.table, z ); |
||
374 | sqlite3DbFree( db, ref z ); |
||
375 | sqlite3ChangeCookie( pParse, iDb ); |
||
376 | sqlite3VdbeAddParseSchemaOp( v, iDb, |
||
377 | sqlite3MPrintf( db, "type='trigger' AND name='%q'", zName ) ); |
||
378 | } |
||
379 | |||
380 | if ( db.init.busy != 0 ) |
||
381 | { |
||
382 | Trigger pLink = pTrig; |
||
383 | Hash pHash = db.aDb[iDb].pSchema.trigHash; |
||
384 | Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); |
||
385 | pTrig = sqlite3HashInsert( ref pHash, zName, sqlite3Strlen30( zName ), pTrig ); |
||
386 | if ( pTrig != null ) |
||
387 | { |
||
388 | //db.mallocFailed = 1; |
||
389 | } |
||
390 | else if ( pLink.pSchema == pLink.pTabSchema ) |
||
391 | { |
||
392 | Table pTab; |
||
393 | int n = sqlite3Strlen30( pLink.table ); |
||
394 | pTab = sqlite3HashFind( pLink.pTabSchema.tblHash, pLink.table, n, (Table)null ); |
||
395 | Debug.Assert( pTab != null ); |
||
396 | pLink.pNext = pTab.pTrigger; |
||
397 | pTab.pTrigger = pLink; |
||
398 | } |
||
399 | } |
||
400 | |||
401 | triggerfinish_cleanup: |
||
402 | sqlite3DeleteTrigger( db, ref pTrig ); |
||
403 | Debug.Assert( pParse.pNewTrigger == null ); |
||
404 | sqlite3DeleteTriggerStep( db, ref pStepList ); |
||
405 | } |
||
406 | |||
407 | /* |
||
408 | ** Turn a SELECT statement (that the pSelect parameter points to) into |
||
409 | ** a trigger step. Return a pointer to a TriggerStep structure. |
||
410 | ** |
||
411 | ** The parser calls this routine when it finds a SELECT statement in |
||
412 | ** body of a TRIGGER. |
||
413 | */ |
||
414 | static TriggerStep sqlite3TriggerSelectStep( sqlite3 db, Select pSelect ) |
||
415 | { |
||
416 | TriggerStep pTriggerStep = new TriggerStep();// sqlite3DbMallocZero( db, sizeof(TriggerStep )) |
||
417 | if ( pTriggerStep == null ) |
||
418 | { |
||
419 | sqlite3SelectDelete( db, ref pSelect ); |
||
420 | return null; |
||
421 | } |
||
422 | |||
423 | pTriggerStep.op = TK_SELECT; |
||
424 | pTriggerStep.pSelect = pSelect; |
||
425 | pTriggerStep.orconf = OE_Default; |
||
426 | return pTriggerStep; |
||
427 | } |
||
428 | |||
429 | /* |
||
430 | ** Allocate space to hold a new trigger step. The allocated space |
||
431 | ** holds both the TriggerStep object and the TriggerStep.target.z string. |
||
432 | ** |
||
433 | ** If an OOM error occurs, NULL is returned and db.mallocFailed is set. |
||
434 | */ |
||
435 | static TriggerStep triggerStepAllocate( |
||
436 | sqlite3 db, /* Database connection */ |
||
437 | u8 op, /* Trigger opcode */ |
||
438 | Token pName /* The target name */ |
||
439 | ) |
||
440 | { |
||
441 | TriggerStep pTriggerStep; |
||
442 | |||
443 | pTriggerStep = new TriggerStep();// sqlite3DbMallocZero( db, sizeof( TriggerStep ) + pName.n ); |
||
444 | //if ( pTriggerStep != null ) |
||
445 | //{ |
||
446 | string z;// = (char*)&pTriggerStep[1]; |
||
447 | z = pName.z;// memcpy( z, pName.z, pName.n ); |
||
448 | pTriggerStep.target.z = z; |
||
449 | pTriggerStep.target.n = pName.n; |
||
450 | pTriggerStep.op = op; |
||
451 | //} |
||
452 | return pTriggerStep; |
||
453 | } |
||
454 | |||
455 | /* |
||
456 | ** Build a trigger step out of an INSERT statement. Return a pointer |
||
457 | ** to the new trigger step. |
||
458 | ** |
||
459 | ** The parser calls this routine when it sees an INSERT inside the |
||
460 | ** body of a trigger. |
||
461 | */ |
||
462 | // OVERLOADS, so I don't need to rewrite parse.c |
||
463 | static TriggerStep sqlite3TriggerInsertStep( sqlite3 db, Token pTableName, IdList pColumn, int null_4, int null_5, u8 orconf ) |
||
464 | { |
||
465 | return sqlite3TriggerInsertStep( db, pTableName, pColumn, null, null, orconf ); |
||
466 | } |
||
467 | static TriggerStep sqlite3TriggerInsertStep( sqlite3 db, Token pTableName, IdList pColumn, ExprList pEList, int null_5, u8 orconf ) |
||
468 | { |
||
469 | return sqlite3TriggerInsertStep( db, pTableName, pColumn, pEList, null, orconf ); |
||
470 | } |
||
471 | static TriggerStep sqlite3TriggerInsertStep( sqlite3 db, Token pTableName, IdList pColumn, int null_4, Select pSelect, u8 orconf ) |
||
472 | { |
||
473 | return sqlite3TriggerInsertStep( db, pTableName, pColumn, null, pSelect, orconf ); |
||
474 | } |
||
475 | static TriggerStep sqlite3TriggerInsertStep( |
||
476 | sqlite3 db, /* The database connection */ |
||
477 | Token pTableName, /* Name of the table into which we insert */ |
||
478 | IdList pColumn, /* List of columns in pTableName to insert into */ |
||
479 | ExprList pEList, /* The VALUE clause: a list of values to be inserted */ |
||
480 | Select pSelect, /* A SELECT statement that supplies values */ |
||
481 | u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ |
||
482 | ) |
||
483 | { |
||
484 | TriggerStep pTriggerStep; |
||
485 | |||
486 | Debug.Assert( pEList == null || pSelect == null ); |
||
487 | Debug.Assert( pEList != null || pSelect != null /*|| db.mallocFailed != 0 */ ); |
||
488 | |||
489 | pTriggerStep = triggerStepAllocate( db, TK_INSERT, pTableName ); |
||
490 | //if ( pTriggerStep != null ) |
||
491 | //{ |
||
492 | pTriggerStep.pSelect = sqlite3SelectDup( db, pSelect, EXPRDUP_REDUCE ); |
||
493 | pTriggerStep.pIdList = pColumn; |
||
494 | pTriggerStep.pExprList = sqlite3ExprListDup( db, pEList, EXPRDUP_REDUCE ); |
||
495 | pTriggerStep.orconf = orconf; |
||
496 | //} |
||
497 | //else |
||
498 | //{ |
||
499 | // sqlite3IdListDelete( db, ref pColumn ); |
||
500 | //} |
||
501 | sqlite3ExprListDelete( db, ref pEList ); |
||
502 | sqlite3SelectDelete( db, ref pSelect ); |
||
503 | |||
504 | return pTriggerStep; |
||
505 | } |
||
506 | |||
507 | /* |
||
508 | ** Construct a trigger step that implements an UPDATE statement and return |
||
509 | ** a pointer to that trigger step. The parser calls this routine when it |
||
510 | ** sees an UPDATE statement inside the body of a CREATE TRIGGER. |
||
511 | */ |
||
512 | static TriggerStep sqlite3TriggerUpdateStep( |
||
513 | sqlite3 db, /* The database connection */ |
||
514 | Token pTableName, /* Name of the table to be updated */ |
||
515 | ExprList pEList, /* The SET clause: list of column and new values */ |
||
516 | Expr pWhere, /* The WHERE clause */ |
||
517 | u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ |
||
518 | ) |
||
519 | { |
||
520 | TriggerStep pTriggerStep; |
||
521 | |||
522 | pTriggerStep = triggerStepAllocate( db, TK_UPDATE, pTableName ); |
||
523 | //if ( pTriggerStep != null ) |
||
524 | //{ |
||
525 | pTriggerStep.pExprList = sqlite3ExprListDup( db, pEList, EXPRDUP_REDUCE ); |
||
526 | pTriggerStep.pWhere = sqlite3ExprDup( db, pWhere, EXPRDUP_REDUCE ); |
||
527 | pTriggerStep.orconf = orconf; |
||
528 | //} |
||
529 | sqlite3ExprListDelete( db, ref pEList ); |
||
530 | sqlite3ExprDelete( db, ref pWhere ); |
||
531 | return pTriggerStep; |
||
532 | } |
||
533 | |||
534 | /* |
||
535 | ** Construct a trigger step that implements a DELETE statement and return |
||
536 | ** a pointer to that trigger step. The parser calls this routine when it |
||
537 | ** sees a DELETE statement inside the body of a CREATE TRIGGER. |
||
538 | */ |
||
539 | static TriggerStep sqlite3TriggerDeleteStep( |
||
540 | sqlite3 db, /* Database connection */ |
||
541 | Token pTableName, /* The table from which rows are deleted */ |
||
542 | Expr pWhere /* The WHERE clause */ |
||
543 | ) |
||
544 | { |
||
545 | TriggerStep pTriggerStep; |
||
546 | |||
547 | pTriggerStep = triggerStepAllocate( db, TK_DELETE, pTableName ); |
||
548 | //if ( pTriggerStep != null ) |
||
549 | //{ |
||
550 | pTriggerStep.pWhere = sqlite3ExprDup( db, pWhere, EXPRDUP_REDUCE ); |
||
551 | pTriggerStep.orconf = OE_Default; |
||
552 | //} |
||
553 | sqlite3ExprDelete( db, ref pWhere ); |
||
554 | return pTriggerStep; |
||
555 | } |
||
556 | |||
557 | |||
558 | |||
559 | /* |
||
560 | ** Recursively delete a Trigger structure |
||
561 | */ |
||
562 | static void sqlite3DeleteTrigger( sqlite3 db, ref Trigger pTrigger ) |
||
563 | { |
||
564 | if ( pTrigger == null ) |
||
565 | return; |
||
566 | sqlite3DeleteTriggerStep( db, ref pTrigger.step_list ); |
||
567 | sqlite3DbFree( db, ref pTrigger.zName ); |
||
568 | sqlite3DbFree( db, ref pTrigger.table ); |
||
569 | sqlite3ExprDelete( db, ref pTrigger.pWhen ); |
||
570 | sqlite3IdListDelete( db, ref pTrigger.pColumns ); |
||
571 | pTrigger = null; |
||
572 | sqlite3DbFree( db, ref pTrigger ); |
||
573 | } |
||
574 | |||
575 | /* |
||
576 | ** This function is called to drop a trigger from the database schema. |
||
577 | ** |
||
578 | ** This may be called directly from the parser and therefore identifies |
||
579 | ** the trigger by name. The sqlite3DropTriggerPtr() routine does the |
||
580 | ** same job as this routine except it takes a pointer to the trigger |
||
581 | ** instead of the trigger name. |
||
582 | **/ |
||
583 | static void sqlite3DropTrigger( Parse pParse, SrcList pName, int noErr ) |
||
584 | { |
||
585 | Trigger pTrigger = null; |
||
586 | int i; |
||
587 | string zDb; |
||
588 | string zName; |
||
589 | int nName; |
||
590 | sqlite3 db = pParse.db; |
||
591 | |||
592 | // if ( db.mallocFailed != 0 ) goto drop_trigger_cleanup; |
||
593 | if ( SQLITE_OK != sqlite3ReadSchema( pParse ) ) |
||
594 | { |
||
595 | goto drop_trigger_cleanup; |
||
596 | } |
||
597 | |||
598 | Debug.Assert( pName.nSrc == 1 ); |
||
599 | zDb = pName.a[0].zDatabase; |
||
600 | zName = pName.a[0].zName; |
||
601 | nName = sqlite3Strlen30( zName ); |
||
602 | Debug.Assert( zDb != null || sqlite3BtreeHoldsAllMutexes( db ) ); |
||
603 | for ( i = OMIT_TEMPDB; i < db.nDb; i++ ) |
||
604 | { |
||
605 | int j = ( i < 2 ) ? i ^ 1 : i; /* Search TEMP before MAIN */ |
||
606 | if ( zDb != null && !db.aDb[j].zName.Equals( zDb ,StringComparison.OrdinalIgnoreCase ) ) |
||
607 | continue; |
||
608 | Debug.Assert( sqlite3SchemaMutexHeld( db, j, null ) ); |
||
609 | pTrigger = sqlite3HashFind( ( db.aDb[j].pSchema.trigHash ), zName, nName, (Trigger)null ); |
||
610 | if ( pTrigger != null ) |
||
611 | break; |
||
612 | } |
||
613 | if ( pTrigger == null ) |
||
614 | { |
||
615 | if ( noErr == 0 ) |
||
616 | { |
||
617 | sqlite3ErrorMsg( pParse, "no such trigger: %S", pName, 0 ); |
||
618 | } |
||
619 | else |
||
620 | { |
||
621 | sqlite3CodeVerifyNamedSchema( pParse, zDb ); |
||
622 | } |
||
623 | pParse.checkSchema = 1; |
||
624 | goto drop_trigger_cleanup; |
||
625 | } |
||
626 | sqlite3DropTriggerPtr( pParse, pTrigger ); |
||
627 | |||
628 | drop_trigger_cleanup: |
||
629 | sqlite3SrcListDelete( db, ref pName ); |
||
630 | } |
||
631 | |||
632 | /* |
||
633 | ** Return a pointer to the Table structure for the table that a trigger |
||
634 | ** is set on. |
||
635 | */ |
||
636 | static Table tableOfTrigger( Trigger pTrigger ) |
||
637 | { |
||
638 | int n = sqlite3Strlen30( pTrigger.table ); |
||
639 | return sqlite3HashFind( pTrigger.pTabSchema.tblHash, pTrigger.table, n, (Table)null ); |
||
640 | } |
||
641 | |||
642 | |||
643 | /* |
||
644 | ** Drop a trigger given a pointer to that trigger. |
||
645 | */ |
||
646 | static void sqlite3DropTriggerPtr( Parse pParse, Trigger pTrigger ) |
||
647 | { |
||
648 | Table pTable; |
||
649 | Vdbe v; |
||
650 | sqlite3 db = pParse.db; |
||
651 | int iDb; |
||
652 | |||
653 | iDb = sqlite3SchemaToIndex( pParse.db, pTrigger.pSchema ); |
||
654 | Debug.Assert( iDb >= 0 && iDb < db.nDb ); |
||
655 | pTable = tableOfTrigger( pTrigger ); |
||
656 | Debug.Assert( pTable != null ); |
||
657 | Debug.Assert( pTable.pSchema == pTrigger.pSchema || iDb == 1 ); |
||
658 | #if !SQLITE_OMIT_AUTHORIZATION |
||
659 | { |
||
660 | int code = SQLITE_DROP_TRIGGER; |
||
661 | string zDb = db.aDb[iDb].zName; |
||
662 | string zTab = SCHEMA_TABLE(iDb); |
||
663 | if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER; |
||
664 | if( sqlite3AuthCheck(pParse, code, pTrigger.name, pTable.zName, zDb) || |
||
665 | sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ |
||
666 | return; |
||
667 | } |
||
668 | } |
||
669 | #endif |
||
670 | |||
671 | /* Generate code to destroy the database record of the trigger. |
||
672 | */ |
||
673 | Debug.Assert( pTable != null ); |
||
674 | if ( ( v = sqlite3GetVdbe( pParse ) ) != null ) |
||
675 | { |
||
676 | int _base; |
||
677 | VdbeOpList[] dropTrigger = new VdbeOpList[] { |
||
678 | new VdbeOpList( OP_Rewind, 0, ADDR(9), 0), |
||
679 | new VdbeOpList( OP_String8, 0, 1, 0), /* 1 */ |
||
680 | new VdbeOpList( OP_Column, 0, 1, 2), |
||
681 | new VdbeOpList( OP_Ne, 2, ADDR(8), 1), |
||
682 | new VdbeOpList( OP_String8, 0, 1, 0), /* 4: "trigger" */ |
||
683 | new VdbeOpList( OP_Column, 0, 0, 2), |
||
684 | new VdbeOpList( OP_Ne, 2, ADDR(8), 1), |
||
685 | new VdbeOpList( OP_Delete, 0, 0, 0), |
||
686 | new VdbeOpList( OP_Next, 0, ADDR(1), 0), /* 8 */ |
||
687 | }; |
||
688 | |||
689 | sqlite3BeginWriteOperation( pParse, 0, iDb ); |
||
690 | sqlite3OpenMasterTable( pParse, iDb ); |
||
691 | _base = sqlite3VdbeAddOpList( v, dropTrigger.Length, dropTrigger ); |
||
692 | sqlite3VdbeChangeP4( v, _base + 1, pTrigger.zName, P4_TRANSIENT ); |
||
693 | sqlite3VdbeChangeP4( v, _base + 4, "trigger", P4_STATIC ); |
||
694 | sqlite3ChangeCookie( pParse, iDb ); |
||
695 | sqlite3VdbeAddOp2( v, OP_Close, 0, 0 ); |
||
696 | sqlite3VdbeAddOp4( v, OP_DropTrigger, iDb, 0, 0, pTrigger.zName, 0 ); |
||
697 | if ( pParse.nMem < 3 ) |
||
698 | { |
||
699 | pParse.nMem = 3; |
||
700 | } |
||
701 | } |
||
702 | } |
||
703 | |||
704 | /* |
||
705 | ** Remove a trigger from the hash tables of the sqlite* pointer. |
||
706 | */ |
||
707 | static void sqlite3UnlinkAndDeleteTrigger( sqlite3 db, int iDb, string zName ) |
||
708 | { |
||
709 | Trigger pTrigger; |
||
710 | Hash pHash; |
||
711 | |||
712 | Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); |
||
713 | pHash = ( db.aDb[iDb].pSchema.trigHash ); |
||
714 | pTrigger = sqlite3HashInsert( ref pHash, zName, sqlite3Strlen30( zName ), (Trigger)null ); |
||
715 | if ( ALWAYS( pTrigger != null ) ) |
||
716 | { |
||
717 | if ( pTrigger.pSchema == pTrigger.pTabSchema ) |
||
718 | { |
||
719 | Table pTab = tableOfTrigger( pTrigger ); |
||
720 | //Trigger** pp; |
||
721 | //for ( pp = &pTab.pTrigger ; *pp != pTrigger ; pp = &( (*pp).pNext ) ) ; |
||
722 | //*pp = (*pp).pNext; |
||
723 | if ( pTab.pTrigger == pTrigger ) |
||
724 | { |
||
725 | pTab.pTrigger = pTrigger.pNext; |
||
726 | } |
||
727 | else |
||
728 | { |
||
729 | Trigger cc = pTab.pTrigger; |
||
730 | while ( cc != null ) |
||
731 | { |
||
732 | if ( cc.pNext == pTrigger ) |
||
733 | { |
||
734 | cc.pNext = cc.pNext.pNext; |
||
735 | break; |
||
736 | } |
||
737 | cc = cc.pNext; |
||
738 | } |
||
739 | Debug.Assert( cc != null ); |
||
740 | } |
||
741 | } |
||
742 | sqlite3DeleteTrigger( db, ref pTrigger ); |
||
743 | db.flags |= SQLITE_InternChanges; |
||
744 | } |
||
745 | } |
||
746 | |||
747 | /* |
||
748 | ** pEList is the SET clause of an UPDATE statement. Each entry |
||
749 | ** in pEList is of the format <id>=<expr>. If any of the entries |
||
750 | ** in pEList have an <id> which matches an identifier in pIdList, |
||
751 | ** then return TRUE. If pIdList==NULL, then it is considered a |
||
752 | ** wildcard that matches anything. Likewise if pEList==NULL then |
||
753 | ** it matches anything so always return true. Return false only |
||
754 | ** if there is no match. |
||
755 | */ |
||
756 | static int checkColumnOverlap( IdList pIdList, ExprList pEList ) |
||
757 | { |
||
758 | int e; |
||
759 | if ( pIdList == null || NEVER( pEList == null ) ) |
||
760 | return 1; |
||
761 | for ( e = 0; e < pEList.nExpr; e++ ) |
||
762 | { |
||
763 | if ( sqlite3IdListIndex( pIdList, pEList.a[e].zName ) >= 0 ) |
||
764 | return 1; |
||
765 | } |
||
766 | return 0; |
||
767 | } |
||
768 | |||
769 | /* |
||
770 | ** Return a list of all triggers on table pTab if there exists at least |
||
771 | ** one trigger that must be fired when an operation of type 'op' is |
||
772 | ** performed on the table, and, if that operation is an UPDATE, if at |
||
773 | ** least one of the columns in pChanges is being modified. |
||
774 | */ |
||
775 | static Trigger sqlite3TriggersExist( |
||
776 | Parse pParse, /* Parse context */ |
||
777 | Table pTab, /* The table the contains the triggers */ |
||
778 | int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ |
||
779 | ExprList pChanges, /* Columns that change in an UPDATE statement */ |
||
780 | out int pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ |
||
781 | ) |
||
782 | { |
||
783 | int mask = 0; |
||
784 | Trigger pList = null; |
||
785 | Trigger p; |
||
786 | |||
787 | if ( ( pParse.db.flags & SQLITE_EnableTrigger ) != 0 ) |
||
788 | { |
||
789 | pList = sqlite3TriggerList( pParse, pTab ); |
||
790 | } |
||
791 | Debug.Assert( pList == null || IsVirtual( pTab ) == false ); |
||
792 | for ( p = pList; p != null; p = p.pNext ) |
||
793 | { |
||
794 | if ( p.op == op && checkColumnOverlap( p.pColumns, pChanges ) != 0 ) |
||
795 | { |
||
796 | mask |= p.tr_tm; |
||
797 | } |
||
798 | } |
||
799 | //if ( pMask != 0 ) |
||
800 | { |
||
801 | pMask = mask; |
||
802 | } |
||
803 | return ( mask != 0 ? pList : null ); |
||
804 | } |
||
805 | |||
806 | |||
807 | /* |
||
808 | ** Convert the pStep.target token into a SrcList and return a pointer |
||
809 | ** to that SrcList. |
||
810 | ** |
||
811 | ** This routine adds a specific database name, if needed, to the target when |
||
812 | ** forming the SrcList. This prevents a trigger in one database from |
||
813 | ** referring to a target in another database. An exception is when the |
||
814 | ** trigger is in TEMP in which case it can refer to any other database it |
||
815 | ** wants. |
||
816 | */ |
||
817 | static SrcList targetSrcList( |
||
818 | Parse pParse, /* The parsing context */ |
||
819 | TriggerStep pStep /* The trigger containing the target token */ |
||
820 | ) |
||
821 | { |
||
822 | int iDb; /* Index of the database to use */ |
||
823 | SrcList pSrc; /* SrcList to be returned */ |
||
824 | |||
825 | pSrc = sqlite3SrcListAppend( pParse.db, 0, pStep.target, 0 ); |
||
826 | //if ( pSrc != null ) |
||
827 | //{ |
||
828 | Debug.Assert( pSrc.nSrc > 0 ); |
||
829 | Debug.Assert( pSrc.a != null ); |
||
830 | iDb = sqlite3SchemaToIndex( pParse.db, pStep.pTrig.pSchema ); |
||
831 | if ( iDb == 0 || iDb >= 2 ) |
||
832 | { |
||
833 | sqlite3 db = pParse.db; |
||
834 | Debug.Assert( iDb < pParse.db.nDb ); |
||
835 | pSrc.a[pSrc.nSrc - 1].zDatabase = db.aDb[iDb].zName;// sqlite3DbStrDup( db, db.aDb[iDb].zName ); |
||
836 | } |
||
837 | //} |
||
838 | return pSrc; |
||
839 | } |
||
840 | |||
841 | /* |
||
842 | ** Generate VDBE code for the statements inside the body of a single |
||
843 | ** trigger. |
||
844 | */ |
||
845 | static int codeTriggerProgram( |
||
846 | Parse pParse, /* The parser context */ |
||
847 | TriggerStep pStepList, /* List of statements inside the trigger body */ |
||
848 | int orconf /* Conflict algorithm. (OE_Abort, etc) */ |
||
849 | ) |
||
850 | { |
||
851 | TriggerStep pStep; |
||
852 | Vdbe v = pParse.pVdbe; |
||
853 | sqlite3 db = pParse.db; |
||
854 | |||
855 | Debug.Assert( pParse.pTriggerTab != null && pParse.pToplevel != null ); |
||
856 | Debug.Assert( pStepList != null ); |
||
857 | Debug.Assert( v != null ); |
||
858 | for ( pStep = pStepList; pStep != null; pStep = pStep.pNext ) |
||
859 | { |
||
860 | /* Figure out the ON CONFLICT policy that will be used for this step |
||
861 | ** of the trigger program. If the statement that caused this trigger |
||
862 | ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use |
||
863 | ** the ON CONFLICT policy that was specified as part of the trigger |
||
864 | ** step statement. Example: |
||
865 | ** |
||
866 | ** CREATE TRIGGER AFTER INSERT ON t1 BEGIN; |
||
867 | ** INSERT OR REPLACE INTO t2 VALUES(new.a, new.b); |
||
868 | ** END; |
||
869 | ** |
||
870 | ** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy |
||
871 | ** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy |
||
872 | */ |
||
873 | pParse.eOrconf = ( orconf == OE_Default ) ? pStep.orconf : (u8)orconf; |
||
874 | |||
875 | switch ( pStep.op ) |
||
876 | { |
||
877 | case TK_UPDATE: |
||
878 | { |
||
879 | sqlite3Update( pParse, |
||
880 | targetSrcList( pParse, pStep ), |
||
881 | sqlite3ExprListDup( db, pStep.pExprList, 0 ), |
||
882 | sqlite3ExprDup( db, pStep.pWhere, 0 ), |
||
883 | pParse.eOrconf |
||
884 | ); |
||
885 | break; |
||
886 | } |
||
887 | case TK_INSERT: |
||
888 | { |
||
889 | sqlite3Insert( pParse, |
||
890 | targetSrcList( pParse, pStep ), |
||
891 | sqlite3ExprListDup( db, pStep.pExprList, 0 ), |
||
892 | sqlite3SelectDup( db, pStep.pSelect, 0 ), |
||
893 | sqlite3IdListDup( db, pStep.pIdList ), |
||
894 | pParse.eOrconf |
||
895 | ); |
||
896 | break; |
||
897 | } |
||
898 | case TK_DELETE: |
||
899 | { |
||
900 | sqlite3DeleteFrom( pParse, |
||
901 | targetSrcList( pParse, pStep ), |
||
902 | sqlite3ExprDup( db, pStep.pWhere, 0 ) |
||
903 | ); |
||
904 | break; |
||
905 | } |
||
906 | default: |
||
907 | Debug.Assert( pStep.op == TK_SELECT ); |
||
908 | { |
||
909 | SelectDest sDest = new SelectDest(); |
||
910 | Select pSelect = sqlite3SelectDup( db, pStep.pSelect, 0 ); |
||
911 | sqlite3SelectDestInit( sDest, SRT_Discard, 0 ); |
||
912 | sqlite3Select( pParse, pSelect, ref sDest ); |
||
913 | sqlite3SelectDelete( db, ref pSelect ); |
||
914 | break; |
||
915 | } |
||
916 | } |
||
917 | if ( pStep.op != TK_SELECT ) |
||
918 | { |
||
919 | sqlite3VdbeAddOp0( v, OP_ResetCount ); |
||
920 | } |
||
921 | } |
||
922 | |||
923 | return 0; |
||
924 | } |
||
925 | |||
926 | #if SQLITE_DEBUG |
||
927 | /* |
||
928 | ** This function is used to add VdbeComment() annotations to a VDBE |
||
929 | ** program. It is not used in production code, only for debugging. |
||
930 | */ |
||
931 | static string onErrorText( int onError ) |
||
932 | { |
||
933 | switch ( onError ) |
||
934 | { |
||
935 | case OE_Abort: |
||
936 | return "abort"; |
||
937 | case OE_Rollback: |
||
938 | return "rollback"; |
||
939 | case OE_Fail: |
||
940 | return "fail"; |
||
941 | case OE_Replace: |
||
942 | return "replace"; |
||
943 | case OE_Ignore: |
||
944 | return "ignore"; |
||
945 | case OE_Default: |
||
946 | return "default"; |
||
947 | } |
||
948 | return "n/a"; |
||
949 | } |
||
950 | #endif |
||
951 | |||
952 | /* |
||
953 | ** Parse context structure pFrom has just been used to create a sub-vdbe |
||
954 | ** (trigger program). If an error has occurred, transfer error information |
||
955 | ** from pFrom to pTo. |
||
956 | */ |
||
957 | static void transferParseError( Parse pTo, Parse pFrom ) |
||
958 | { |
||
959 | Debug.Assert( string.IsNullOrEmpty( pFrom.zErrMsg ) || pFrom.nErr != 0 ); |
||
960 | Debug.Assert( string.IsNullOrEmpty( pTo.zErrMsg ) || pTo.nErr != 0 ); |
||
961 | if ( pTo.nErr == 0 ) |
||
962 | { |
||
963 | pTo.zErrMsg = pFrom.zErrMsg; |
||
964 | pTo.nErr = pFrom.nErr; |
||
965 | } |
||
966 | else |
||
967 | { |
||
968 | sqlite3DbFree( pFrom.db, ref pFrom.zErrMsg ); |
||
969 | } |
||
970 | } |
||
971 | |||
972 | /* |
||
973 | ** Create and populate a new TriggerPrg object with a sub-program |
||
974 | ** implementing trigger pTrigger with ON CONFLICT policy orconf. |
||
975 | */ |
||
976 | static TriggerPrg codeRowTrigger( |
||
977 | Parse pParse, /* Current parse context */ |
||
978 | Trigger pTrigger, /* Trigger to code */ |
||
979 | Table pTab, /* The table pTrigger is attached to */ |
||
980 | int orconf /* ON CONFLICT policy to code trigger program with */ |
||
981 | ) |
||
982 | { |
||
983 | Parse pTop = sqlite3ParseToplevel( pParse ); |
||
984 | sqlite3 db = pParse.db; /* Database handle */ |
||
985 | TriggerPrg pPrg; /* Value to return */ |
||
986 | Expr pWhen = null; /* Duplicate of trigger WHEN expression */ |
||
987 | Vdbe v; /* Temporary VM */ |
||
988 | NameContext sNC; /* Name context for sub-vdbe */ |
||
989 | SubProgram pProgram = null; /* Sub-vdbe for trigger program */ |
||
990 | Parse pSubParse; /* Parse context for sub-vdbe */ |
||
991 | int iEndTrigger = 0; /* Label to jump to if WHEN is false */ |
||
992 | |||
993 | Debug.Assert( pTrigger.zName == null || pTab == tableOfTrigger( pTrigger ) ); |
||
994 | Debug.Assert( pTop.pVdbe != null ); |
||
995 | |||
996 | /* Allocate the TriggerPrg and SubProgram objects. To ensure that they |
||
997 | ** are freed if an error occurs, link them into the Parse.pTriggerPrg |
||
998 | ** list of the top-level Parse object sooner rather than later. */ |
||
999 | pPrg = new TriggerPrg();// sqlite3DbMallocZero( db, sizeof( TriggerPrg ) ); |
||
1000 | //if ( null == pPrg ) return 0; |
||
1001 | pPrg.pNext = pTop.pTriggerPrg; |
||
1002 | pTop.pTriggerPrg = pPrg; |
||
1003 | pPrg.pProgram = pProgram = new SubProgram();// sqlite3DbMallocZero( db, sizeof( SubProgram ) ); |
||
1004 | //if( null==pProgram ) return 0; |
||
1005 | sqlite3VdbeLinkSubProgram( pTop.pVdbe, pProgram ); |
||
1006 | pPrg.pTrigger = pTrigger; |
||
1007 | pPrg.orconf = orconf; |
||
1008 | pPrg.aColmask[0] = 0xffffffff; |
||
1009 | pPrg.aColmask[1] = 0xffffffff; |
||
1010 | |||
1011 | |||
1012 | /* Allocate and populate a new Parse context to use for coding the |
||
1013 | ** trigger sub-program. */ |
||
1014 | pSubParse = new Parse();// sqlite3StackAllocZero( db, sizeof( Parse ) ); |
||
1015 | //if ( null == pSubParse ) return null; |
||
1016 | sNC = new NameContext();// memset( &sNC, 0, sizeof( sNC ) ); |
||
1017 | sNC.pParse = pSubParse; |
||
1018 | pSubParse.db = db; |
||
1019 | pSubParse.pTriggerTab = pTab; |
||
1020 | pSubParse.pToplevel = pTop; |
||
1021 | pSubParse.zAuthContext = pTrigger.zName; |
||
1022 | pSubParse.eTriggerOp = pTrigger.op; |
||
1023 | pSubParse.nQueryLoop = pParse.nQueryLoop; |
||
1024 | |||
1025 | v = sqlite3GetVdbe( pSubParse ); |
||
1026 | if ( v != null ) |
||
1027 | { |
||
1028 | #if SQLITE_DEBUG |
||
1029 | VdbeComment( v, "Start: %s.%s (%s %s%s%s ON %s)", |
||
1030 | pTrigger.zName ?? string.Empty, onErrorText( orconf ), |
||
1031 | ( pTrigger.tr_tm == TRIGGER_BEFORE ? "BEFORE" : "AFTER" ), |
||
1032 | ( pTrigger.op == TK_UPDATE ? "UPDATE" : string.Empty ), |
||
1033 | ( pTrigger.op == TK_INSERT ? "INSERT" : string.Empty ), |
||
1034 | ( pTrigger.op == TK_DELETE ? "DELETE" : string.Empty ), |
||
1035 | pTab.zName |
||
1036 | ); |
||
1037 | #endif |
||
1038 | #if !SQLITE_OMIT_TRACE |
||
1039 | sqlite3VdbeChangeP4( v, -1, |
||
1040 | sqlite3MPrintf( db, "-- TRIGGER %s", pTrigger.zName ), P4_DYNAMIC |
||
1041 | ); |
||
1042 | #endif |
||
1043 | |||
1044 | /* If one was specified, code the WHEN clause. If it evaluates to false |
||
1045 | ** (or NULL) the sub-vdbe is immediately halted by jumping to the |
||
1046 | ** OP_Halt inserted at the end of the program. */ |
||
1047 | if ( pTrigger.pWhen != null ) |
||
1048 | { |
||
1049 | pWhen = sqlite3ExprDup( db, pTrigger.pWhen, 0 ); |
||
1050 | if ( SQLITE_OK == sqlite3ResolveExprNames( sNC, ref pWhen ) |
||
1051 | //&& db.mallocFailed==0 |
||
1052 | ) |
||
1053 | { |
||
1054 | iEndTrigger = sqlite3VdbeMakeLabel( v ); |
||
1055 | sqlite3ExprIfFalse( pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL ); |
||
1056 | } |
||
1057 | sqlite3ExprDelete( db, ref pWhen ); |
||
1058 | } |
||
1059 | |||
1060 | /* Code the trigger program into the sub-vdbe. */ |
||
1061 | codeTriggerProgram( pSubParse, pTrigger.step_list, orconf ); |
||
1062 | |||
1063 | /* Insert an OP_Halt at the end of the sub-program. */ |
||
1064 | if ( iEndTrigger != 0 ) |
||
1065 | { |
||
1066 | sqlite3VdbeResolveLabel( v, iEndTrigger ); |
||
1067 | } |
||
1068 | sqlite3VdbeAddOp0( v, OP_Halt ); |
||
1069 | #if SQLITE_DEBUG |
||
1070 | VdbeComment( v, "End: %s.%s", pTrigger.zName, onErrorText( orconf ) ); |
||
1071 | #endif |
||
1072 | transferParseError( pParse, pSubParse ); |
||
1073 | //if( db.mallocFailed==0 ){ |
||
1074 | pProgram.aOp = sqlite3VdbeTakeOpArray( v, ref pProgram.nOp, ref pTop.nMaxArg ); |
||
1075 | //} |
||
1076 | pProgram.nMem = pSubParse.nMem; |
||
1077 | pProgram.nCsr = pSubParse.nTab; |
||
1078 | pProgram.token = pTrigger.GetHashCode(); |
||
1079 | pPrg.aColmask[0] = pSubParse.oldmask; |
||
1080 | pPrg.aColmask[1] = pSubParse.newmask; |
||
1081 | sqlite3VdbeDelete( ref v ); |
||
1082 | } |
||
1083 | |||
1084 | Debug.Assert( null == pSubParse.pAinc && null == pSubParse.pZombieTab ); |
||
1085 | Debug.Assert( null == pSubParse.pTriggerPrg && 0 == pSubParse.nMaxArg ); |
||
1086 | //sqlite3StackFree(db, pSubParse); |
||
1087 | |||
1088 | return pPrg; |
||
1089 | } |
||
1090 | |||
1091 | /* |
||
1092 | ** Return a pointer to a TriggerPrg object containing the sub-program for |
||
1093 | ** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such |
||
1094 | ** TriggerPrg object exists, a new object is allocated and populated before |
||
1095 | ** being returned. |
||
1096 | */ |
||
1097 | static TriggerPrg getRowTrigger( |
||
1098 | Parse pParse, /* Current parse context */ |
||
1099 | Trigger pTrigger, /* Trigger to code */ |
||
1100 | Table pTab, /* The table trigger pTrigger is attached to */ |
||
1101 | int orconf /* ON CONFLICT algorithm. */ |
||
1102 | ) |
||
1103 | { |
||
1104 | Parse pRoot = sqlite3ParseToplevel( pParse ); |
||
1105 | TriggerPrg pPrg; |
||
1106 | |||
1107 | Debug.Assert( pTrigger.zName == null || pTab == tableOfTrigger( pTrigger ) ); |
||
1108 | |||
1109 | /* It may be that this trigger has already been coded (or is in the |
||
1110 | ** process of being coded). If this is the case, then an entry with |
||
1111 | ** a matching TriggerPrg.pTrigger field will be present somewhere |
||
1112 | ** in the Parse.pTriggerPrg list. Search for such an entry. */ |
||
1113 | for ( pPrg = pRoot.pTriggerPrg; |
||
1114 | pPrg != null && ( pPrg.pTrigger != pTrigger || pPrg.orconf != orconf ); |
||
1115 | pPrg = pPrg.pNext |
||
1116 | ) |
||
1117 | ; |
||
1118 | |||
1119 | /* If an existing TriggerPrg could not be located, create a new one. */ |
||
1120 | if ( null == pPrg ) |
||
1121 | { |
||
1122 | pPrg = codeRowTrigger( pParse, pTrigger, pTab, orconf ); |
||
1123 | } |
||
1124 | |||
1125 | return pPrg; |
||
1126 | } |
||
1127 | |||
1128 | /* |
||
1129 | ** Generate code for the trigger program associated with trigger p on |
||
1130 | ** table pTab. The reg, orconf and ignoreJump parameters passed to this |
||
1131 | ** function are the same as those described in the header function for |
||
1132 | ** sqlite3CodeRowTrigger() |
||
1133 | */ |
||
1134 | static void sqlite3CodeRowTriggerDirect( |
||
1135 | Parse pParse, /* Parse context */ |
||
1136 | Trigger p, /* Trigger to code */ |
||
1137 | Table pTab, /* The table to code triggers from */ |
||
1138 | int reg, /* Reg array containing OLD.* and NEW.* values */ |
||
1139 | int orconf, /* ON CONFLICT policy */ |
||
1140 | int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ |
||
1141 | ) |
||
1142 | { |
||
1143 | Vdbe v = sqlite3GetVdbe( pParse ); /* Main VM */ |
||
1144 | TriggerPrg pPrg; |
||
1145 | pPrg = getRowTrigger( pParse, p, pTab, orconf ); |
||
1146 | Debug.Assert( pPrg != null || pParse.nErr != 0 );//|| pParse.db.mallocFailed ); |
||
1147 | |||
1148 | /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program |
||
1149 | ** is a pointer to the sub-vdbe containing the trigger program. */ |
||
1150 | if ( pPrg != null ) |
||
1151 | { |
||
1152 | bool bRecursive = ( !string.IsNullOrEmpty( p.zName ) && 0 == ( pParse.db.flags & SQLITE_RecTriggers ) ); |
||
1153 | sqlite3VdbeAddOp3( v, OP_Program, reg, ignoreJump, ++pParse.nMem ); |
||
1154 | sqlite3VdbeChangeP4( v, -1, pPrg.pProgram, P4_SUBPROGRAM ); |
||
1155 | #if SQLITE_DEBUG |
||
1156 | VdbeComment |
||
1157 | ( v, "Call: %s.%s", ( !string.IsNullOrEmpty( p.zName ) ? p.zName : "fkey" ), onErrorText( orconf ) ); |
||
1158 | #endif |
||
1159 | |||
1160 | /* Set the P5 operand of the OP_Program instruction to non-zero if |
||
1161 | ** recursive invocation of this trigger program is disallowed. Recursive |
||
1162 | ** invocation is disallowed if (a) the sub-program is really a trigger, |
||
1163 | ** not a foreign key action, and (b) the flag to enable recursive triggers |
||
1164 | ** is clear. */ |
||
1165 | sqlite3VdbeChangeP5( v, (u8)( bRecursive ? 1 : 0 ) ); |
||
1166 | } |
||
1167 | } |
||
1168 | |||
1169 | /* |
||
1170 | ** This is called to code the required FOR EACH ROW triggers for an operation |
||
1171 | ** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE) |
||
1172 | ** is given by the op paramater. The tr_tm parameter determines whether the |
||
1173 | ** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then |
||
1174 | ** parameter pChanges is passed the list of columns being modified. |
||
1175 | ** |
||
1176 | ** If there are no triggers that fire at the specified time for the specified |
||
1177 | ** operation on pTab, this function is a no-op. |
||
1178 | ** |
||
1179 | ** The reg argument is the address of the first in an array of registers |
||
1180 | ** that contain the values substituted for the new.* and old.* references |
||
1181 | ** in the trigger program. If N is the number of columns in table pTab |
||
1182 | ** (a copy of pTab.nCol), then registers are populated as follows: |
||
1183 | ** |
||
1184 | ** Register Contains |
||
1185 | ** ------------------------------------------------------ |
||
1186 | ** reg+0 OLD.rowid |
||
1187 | ** reg+1 OLD.* value of left-most column of pTab |
||
1188 | ** ... ... |
||
1189 | ** reg+N OLD.* value of right-most column of pTab |
||
1190 | ** reg+N+1 NEW.rowid |
||
1191 | ** reg+N+2 OLD.* value of left-most column of pTab |
||
1192 | ** ... ... |
||
1193 | ** reg+N+N+1 NEW.* value of right-most column of pTab |
||
1194 | ** |
||
1195 | ** For ON DELETE triggers, the registers containing the NEW.* values will |
||
1196 | ** never be accessed by the trigger program, so they are not allocated or |
||
1197 | ** populated by the caller (there is no data to populate them with anyway). |
||
1198 | ** Similarly, for ON INSERT triggers the values stored in the OLD.* registers |
||
1199 | ** are never accessed, and so are not allocated by the caller. So, for an |
||
1200 | ** ON INSERT trigger, the value passed to this function as parameter reg |
||
1201 | ** is not a readable register, although registers (reg+N) through |
||
1202 | ** (reg+N+N+1) are. |
||
1203 | ** |
||
1204 | ** Parameter orconf is the default conflict resolution algorithm for the |
||
1205 | ** trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump |
||
1206 | ** is the instruction that control should jump to if a trigger program |
||
1207 | ** raises an IGNORE exception. |
||
1208 | */ |
||
1209 | static void sqlite3CodeRowTrigger( |
||
1210 | Parse pParse, /* Parse context */ |
||
1211 | Trigger pTrigger, /* List of triggers on table pTab */ |
||
1212 | int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ |
||
1213 | ExprList pChanges, /* Changes list for any UPDATE OF triggers */ |
||
1214 | int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ |
||
1215 | Table pTab, /* The table to code triggers from */ |
||
1216 | int reg, /* The first in an array of registers (see above) */ |
||
1217 | int orconf, /* ON CONFLICT policy */ |
||
1218 | int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ |
||
1219 | ) |
||
1220 | { |
||
1221 | Trigger p; /* Used to iterate through pTrigger list */ |
||
1222 | |||
1223 | Debug.Assert( op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE ); |
||
1224 | Debug.Assert( tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); |
||
1225 | Debug.Assert( ( op == TK_UPDATE ) == ( pChanges != null ) ); |
||
1226 | |||
1227 | for ( p = pTrigger; p != null; p = p.pNext ) |
||
1228 | { |
||
1229 | |||
1230 | /* Sanity checking: The schema for the trigger and for the table are |
||
1231 | ** always defined. The trigger must be in the same schema as the table |
||
1232 | ** or else it must be a TEMP trigger. */ |
||
1233 | Debug.Assert( p.pSchema != null ); |
||
1234 | Debug.Assert( p.pTabSchema != null ); |
||
1235 | Debug.Assert( p.pSchema == p.pTabSchema |
||
1236 | || p.pSchema == pParse.db.aDb[1].pSchema ); |
||
1237 | |||
1238 | /* Determine whether we should code this trigger */ |
||
1239 | if ( p.op == op |
||
1240 | && p.tr_tm == tr_tm |
||
1241 | && checkColumnOverlap( p.pColumns, pChanges ) != 0 |
||
1242 | ) |
||
1243 | { |
||
1244 | sqlite3CodeRowTriggerDirect( pParse, p, pTab, reg, orconf, ignoreJump ); |
||
1245 | } |
||
1246 | } |
||
1247 | } |
||
1248 | |||
1249 | /* |
||
1250 | ** Triggers may access values stored in the old.* or new.* pseudo-table. |
||
1251 | ** This function returns a 32-bit bitmask indicating which columns of the |
||
1252 | ** old.* or new.* tables actually are used by triggers. This information |
||
1253 | ** may be used by the caller, for example, to avoid having to load the entire |
||
1254 | ** old.* record into memory when executing an UPDATE or DELETE command. |
||
1255 | ** |
||
1256 | ** Bit 0 of the returned mask is set if the left-most column of the |
||
1257 | ** table may be accessed using an [old|new].<col> reference. Bit 1 is set if |
||
1258 | ** the second leftmost column value is required, and so on. If there |
||
1259 | ** are more than 32 columns in the table, and at least one of the columns |
||
1260 | ** with an index greater than 32 may be accessed, 0xffffffff is returned. |
||
1261 | ** |
||
1262 | ** It is not possible to determine if the old.rowid or new.rowid column is |
||
1263 | ** accessed by triggers. The caller must always assume that it is. |
||
1264 | ** |
||
1265 | ** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned |
||
1266 | ** applies to the old.* table. If 1, the new.* table. |
||
1267 | ** |
||
1268 | ** Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE |
||
1269 | ** and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only |
||
1270 | ** included in the returned mask if the TRIGGER_BEFORE bit is set in the |
||
1271 | ** tr_tm parameter. Similarly, values accessed by AFTER triggers are only |
||
1272 | ** included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm. |
||
1273 | */ |
||
1274 | static u32 sqlite3TriggerColmask( |
||
1275 | Parse pParse, /* Parse context */ |
||
1276 | Trigger pTrigger, /* List of triggers on table pTab */ |
||
1277 | ExprList pChanges, /* Changes list for any UPDATE OF triggers */ |
||
1278 | int isNew, /* 1 for new.* ref mask, 0 for old.* ref mask */ |
||
1279 | int tr_tm, /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ |
||
1280 | Table pTab, /* The table to code triggers from */ |
||
1281 | int orconf /* Default ON CONFLICT policy for trigger steps */ |
||
1282 | ) |
||
1283 | { |
||
1284 | int op = pChanges != null ? TK_UPDATE : TK_DELETE; |
||
1285 | u32 mask = 0; |
||
1286 | Trigger p; |
||
1287 | |||
1288 | Debug.Assert( isNew == 1 || isNew == 0 ); |
||
1289 | for ( p = pTrigger; p != null; p = p.pNext ) |
||
1290 | { |
||
1291 | if ( p.op == op && ( tr_tm & p.tr_tm ) != 0 |
||
1292 | && checkColumnOverlap( p.pColumns, pChanges ) != 0 |
||
1293 | ) |
||
1294 | { |
||
1295 | TriggerPrg pPrg; |
||
1296 | pPrg = getRowTrigger( pParse, p, pTab, orconf ); |
||
1297 | if ( pPrg != null ) |
||
1298 | { |
||
1299 | mask |= pPrg.aColmask[isNew]; |
||
1300 | } |
||
1301 | } |
||
1302 | } |
||
1303 | |||
1304 | return mask; |
||
1305 | } |
||
1306 | #endif // * !SQLITE_OMIT_TRIGGER) */ |
||
1307 | |||
1308 | } |
||
1309 | } |