wasCSharpSQLite – Blame information for rev 3
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Diagnostics; |
||
3 | using System.Text; |
||
4 | |||
5 | namespace Community.CsharpSqlite |
||
6 | { |
||
7 | using sqlite3_value = Sqlite3.Mem; |
||
8 | |||
9 | public partial class Sqlite3 |
||
10 | { |
||
11 | /* |
||
12 | ** 2005 February 15 |
||
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 C code routines that used to generate VDBE code |
||
23 | ** that implements the ALTER TABLE command. |
||
24 | ************************************************************************* |
||
25 | ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
||
26 | ** C#-SQLite is an independent reimplementation of the SQLite software library |
||
27 | ** |
||
28 | ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2 |
||
29 | ** |
||
30 | ************************************************************************* |
||
31 | */ |
||
32 | //#include "sqliteInt.h" |
||
33 | |||
34 | /* |
||
35 | ** The code in this file only exists if we are not omitting the |
||
36 | ** ALTER TABLE logic from the build. |
||
37 | */ |
||
38 | #if !SQLITE_OMIT_ALTERTABLE |
||
39 | |||
40 | |||
41 | /* |
||
42 | ** This function is used by SQL generated to implement the |
||
43 | ** ALTER TABLE command. The first argument is the text of a CREATE TABLE or |
||
44 | ** CREATE INDEX command. The second is a table name. The table name in |
||
45 | ** the CREATE TABLE or CREATE INDEX statement is replaced with the third |
||
46 | ** argument and the result returned. Examples: |
||
47 | ** |
||
48 | ** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def') |
||
49 | ** . 'CREATE TABLE def(a, b, c)' |
||
50 | ** |
||
51 | ** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def') |
||
52 | ** . 'CREATE INDEX i ON def(a, b, c)' |
||
53 | */ |
||
54 | static void renameTableFunc( |
||
55 | sqlite3_context context, |
||
56 | int NotUsed, |
||
57 | sqlite3_value[] argv |
||
58 | ) |
||
59 | { |
||
60 | string bResult = sqlite3_value_text( argv[0] ); |
||
61 | string zSql = bResult ?? string.Empty; |
||
62 | string zTableName = sqlite3_value_text( argv[1] ); |
||
63 | |||
64 | int token = 0; |
||
65 | Token tname = new Token(); |
||
66 | int zCsr = 0; |
||
67 | int zLoc = 0; |
||
68 | int len = 0; |
||
69 | string zRet; |
||
70 | |||
71 | sqlite3 db = sqlite3_context_db_handle( context ); |
||
72 | |||
73 | UNUSED_PARAMETER( NotUsed ); |
||
74 | |||
75 | /* The principle used to locate the table name in the CREATE TABLE |
||
76 | ** statement is that the table name is the first non-space token that |
||
77 | ** is immediately followed by a TK_LP or TK_USING token. |
||
78 | */ |
||
79 | if ( zSql.Length > 0 ) |
||
80 | { |
||
81 | do |
||
82 | { |
||
83 | if ( zCsr == zSql.Length ) |
||
84 | { |
||
85 | /* Ran out of input before finding an opening bracket. Return NULL. */ |
||
86 | return; |
||
87 | } |
||
88 | |||
89 | /* Store the token that zCsr points to in tname. */ |
||
90 | zLoc = zCsr; |
||
91 | tname.z = zSql.Substring( zCsr );//(char*)zCsr; |
||
92 | tname.n = len; |
||
93 | |||
94 | /* Advance zCsr to the next token. Store that token type in 'token', |
||
95 | ** and its length in 'len' (to be used next iteration of this loop). |
||
96 | */ |
||
97 | do |
||
98 | { |
||
99 | zCsr += len; |
||
100 | len = ( zCsr == zSql.Length ) ? 1 : sqlite3GetToken( zSql, zCsr, ref token ); |
||
101 | } while ( token == TK_SPACE ); |
||
102 | Debug.Assert( len > 0 ); |
||
103 | } while ( token != TK_LP && token != TK_USING ); |
||
104 | |||
105 | zRet = sqlite3MPrintf( db, "%.*s\"%w\"%s", zLoc, zSql.Substring( 0, zLoc ), |
||
106 | zTableName, zSql.Substring( zLoc + tname.n ) ); |
||
107 | |||
108 | sqlite3_result_text( context, zRet, -1, SQLITE_DYNAMIC ); |
||
109 | } |
||
110 | } |
||
111 | |||
112 | /* |
||
113 | ** This C function implements an SQL user function that is used by SQL code |
||
114 | ** generated by the ALTER TABLE ... RENAME command to modify the definition |
||
115 | ** of any foreign key constraints that use the table being renamed as the |
||
116 | ** parent table. It is passed three arguments: |
||
117 | ** |
||
118 | ** 1) The complete text of the CREATE TABLE statement being modified, |
||
119 | ** 2) The old name of the table being renamed, and |
||
120 | ** 3) The new name of the table being renamed. |
||
121 | ** |
||
122 | ** It returns the new CREATE TABLE statement. For example: |
||
123 | ** |
||
124 | ** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3') |
||
125 | ** -> 'CREATE TABLE t1(a REFERENCES t3)' |
||
126 | */ |
||
127 | #if !SQLITE_OMIT_FOREIGN_KEY |
||
128 | static void renameParentFunc( |
||
129 | sqlite3_context context, |
||
130 | int NotUsed, |
||
131 | sqlite3_value[] argv |
||
132 | ) |
||
133 | { |
||
134 | sqlite3 db = sqlite3_context_db_handle( context ); |
||
135 | string zOutput = string.Empty; |
||
136 | string zResult; |
||
137 | string zInput = sqlite3_value_text( argv[0] ); |
||
138 | string zOld = sqlite3_value_text( argv[1] ); |
||
139 | string zNew = sqlite3_value_text( argv[2] ); |
||
140 | |||
141 | int zIdx; /* Pointer to token */ |
||
142 | int zLeft = 0; /* Pointer to remainder of String */ |
||
143 | int n = 0; /* Length of token z */ |
||
144 | int token = 0; /* Type of token */ |
||
145 | |||
146 | UNUSED_PARAMETER( NotUsed ); |
||
147 | for ( zIdx = 0; zIdx < zInput.Length; zIdx += n )//z=zInput; *z; z=z+n) |
||
148 | { |
||
149 | n = sqlite3GetToken( zInput, zIdx, ref token ); |
||
150 | if ( token == TK_REFERENCES ) |
||
151 | { |
||
152 | string zParent; |
||
153 | do |
||
154 | { |
||
155 | zIdx += n; |
||
156 | n = sqlite3GetToken( zInput, zIdx, ref token ); |
||
157 | } while ( token == TK_SPACE ); |
||
158 | |||
159 | zParent = zIdx + n < zInput.Length ? zInput.Substring( zIdx, n ) : string.Empty;//sqlite3DbStrNDup(db, zIdx, n); |
||
160 | if ( string.IsNullOrEmpty( zParent ) ) |
||
161 | break; |
||
162 | sqlite3Dequote( ref zParent ); |
||
163 | if ( zOld.Equals( zParent, StringComparison.OrdinalIgnoreCase ) ) |
||
164 | { |
||
165 | string zOut = sqlite3MPrintf( db, "%s%.*s\"%w\"", |
||
166 | zOutput, zIdx - zLeft, zInput.Substring( zLeft ), zNew |
||
167 | ); |
||
168 | sqlite3DbFree( db, ref zOutput ); |
||
169 | zOutput = zOut; |
||
170 | zIdx += n;// zInput = &z[n]; |
||
171 | zLeft = zIdx; |
||
172 | } |
||
173 | sqlite3DbFree( db, ref zParent ); |
||
174 | } |
||
175 | } |
||
176 | |||
177 | zResult = sqlite3MPrintf( db, "%s%s", zOutput, zInput.Substring( zLeft ) ); |
||
178 | sqlite3_result_text( context, zResult, -1, SQLITE_DYNAMIC ); |
||
179 | sqlite3DbFree( db, ref zOutput ); |
||
180 | } |
||
181 | #endif |
||
182 | |||
183 | #if !SQLITE_OMIT_TRIGGER |
||
184 | /* This function is used by SQL generated to implement the |
||
185 | ** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER |
||
186 | ** statement. The second is a table name. The table name in the CREATE |
||
187 | ** TRIGGER statement is replaced with the third argument and the result |
||
188 | ** returned. This is analagous to renameTableFunc() above, except for CREATE |
||
189 | ** TRIGGER, not CREATE INDEX and CREATE TABLE. |
||
190 | */ |
||
191 | static void renameTriggerFunc( |
||
192 | sqlite3_context context, |
||
193 | int NotUsed, |
||
194 | sqlite3_value[] argv |
||
195 | ) |
||
196 | { |
||
197 | string zSql = sqlite3_value_text( argv[0] ); |
||
198 | string zTableName = sqlite3_value_text( argv[1] ); |
||
199 | |||
200 | int token = 0; |
||
201 | Token tname = new Token(); |
||
202 | int dist = 3; |
||
203 | int zCsr = 0; |
||
204 | int zLoc = 0; |
||
205 | int len = 1; |
||
206 | string zRet; |
||
207 | |||
208 | sqlite3 db = sqlite3_context_db_handle( context ); |
||
209 | |||
210 | UNUSED_PARAMETER( NotUsed ); |
||
211 | |||
212 | /* The principle used to locate the table name in the CREATE TRIGGER |
||
213 | ** statement is that the table name is the first token that is immediatedly |
||
214 | ** preceded by either TK_ON or TK_DOT and immediatedly followed by one |
||
215 | ** of TK_WHEN, TK_BEGIN or TK_FOR. |
||
216 | */ |
||
217 | if ( zSql != null ) |
||
218 | { |
||
219 | do |
||
220 | { |
||
221 | |||
222 | if ( zCsr == zSql.Length ) |
||
223 | { |
||
224 | /* Ran out of input before finding the table name. Return NULL. */ |
||
225 | return; |
||
226 | } |
||
227 | |||
228 | /* Store the token that zCsr points to in tname. */ |
||
229 | zLoc = zCsr; |
||
230 | tname.z = zSql.Substring( zCsr, len );//(char*)zCsr; |
||
231 | tname.n = len; |
||
232 | |||
233 | /* Advance zCsr to the next token. Store that token type in 'token', |
||
234 | ** and its length in 'len' (to be used next iteration of this loop). |
||
235 | */ |
||
236 | do |
||
237 | { |
||
238 | zCsr += len; |
||
239 | len = ( zCsr == zSql.Length ) ? 1 : sqlite3GetToken( zSql, zCsr, ref token ); |
||
240 | } while ( token == TK_SPACE ); |
||
241 | Debug.Assert( len > 0 ); |
||
242 | |||
243 | /* Variable 'dist' stores the number of tokens read since the most |
||
244 | ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN |
||
245 | ** token is read and 'dist' equals 2, the condition stated above |
||
246 | ** to be met. |
||
247 | ** |
||
248 | ** Note that ON cannot be a database, table or column name, so |
||
249 | ** there is no need to worry about syntax like |
||
250 | ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc. |
||
251 | */ |
||
252 | dist++; |
||
253 | if ( token == TK_DOT || token == TK_ON ) |
||
254 | { |
||
255 | dist = 0; |
||
256 | } |
||
257 | } while ( dist != 2 || ( token != TK_WHEN && token != TK_FOR && token != TK_BEGIN ) ); |
||
258 | |||
259 | /* Variable tname now contains the token that is the old table-name |
||
260 | ** in the CREATE TRIGGER statement. |
||
261 | */ |
||
262 | zRet = sqlite3MPrintf( db, "%.*s\"%w\"%s", zLoc, zSql.Substring( 0, zLoc ), |
||
263 | zTableName, zSql.Substring( zLoc + tname.n ) ); |
||
264 | sqlite3_result_text( context, zRet, -1, SQLITE_DYNAMIC ); |
||
265 | } |
||
266 | } |
||
267 | #endif // * !SQLITE_OMIT_TRIGGER */ |
||
268 | |||
269 | /* |
||
270 | ** Register built-in functions used to help implement ALTER TABLE |
||
271 | */ |
||
272 | static FuncDef[] aAlterTableFuncs; |
||
273 | static void sqlite3AlterFunctions() |
||
274 | { |
||
275 | aAlterTableFuncs = new FuncDef[] { |
||
276 | FUNCTION("sqlite_rename_table", 2, 0, 0, renameTableFunc), |
||
277 | #if !SQLITE_OMIT_TRIGGER |
||
278 | FUNCTION("sqlite_rename_trigger", 2, 0, 0, renameTriggerFunc), |
||
279 | #endif |
||
280 | #if !SQLITE_OMIT_FOREIGN_KEY |
||
281 | FUNCTION("sqlite_rename_parent", 3, 0, 0, renameParentFunc), |
||
282 | #endif |
||
283 | }; |
||
284 | #if SQLITE_OMIT_WSD |
||
285 | FuncDefHash pHash = GLOBAL(FuncDefHash, sqlite3GlobalFunctions); |
||
286 | FuncDef[] aFunc = GLOBAL(FuncDef, aAlterTableFuncs); |
||
287 | #else |
||
288 | FuncDefHash pHash = sqlite3GlobalFunctions; |
||
289 | FuncDef[] aFunc = aAlterTableFuncs; |
||
290 | #endif |
||
291 | |||
292 | for (int i = 0; i < ArraySize( aAlterTableFuncs ); i++ ) |
||
293 | { |
||
294 | sqlite3FuncDefInsert( pHash, aFunc[i] ); |
||
295 | } |
||
296 | } |
||
297 | |||
298 | /* |
||
299 | ** This function is used to create the text of expressions of the form: |
||
300 | ** |
||
301 | ** name=<constant1> OR name=<constant2> OR ... |
||
302 | ** |
||
303 | ** If argument zWhere is NULL, then a pointer string containing the text |
||
304 | ** "name=<constant>" is returned, where <constant> is the quoted version |
||
305 | ** of the string passed as argument zConstant. The returned buffer is |
||
306 | ** allocated using sqlite3DbMalloc(). It is the responsibility of the |
||
307 | ** caller to ensure that it is eventually freed. |
||
308 | ** |
||
309 | ** If argument zWhere is not NULL, then the string returned is |
||
310 | ** "<where> OR name=<constant>", where <where> is the contents of zWhere. |
||
311 | ** In this case zWhere is passed to sqlite3DbFree() before returning. |
||
312 | ** |
||
313 | */ |
||
314 | static string whereOrName( sqlite3 db, string zWhere, string zConstant ) |
||
315 | { |
||
316 | string zNew; |
||
317 | if ( string.IsNullOrEmpty( zWhere ) ) |
||
318 | { |
||
319 | zNew = sqlite3MPrintf( db, "name=%Q", zConstant ); |
||
320 | } |
||
321 | else |
||
322 | { |
||
323 | zNew = sqlite3MPrintf( db, "%s OR name=%Q", zWhere, zConstant ); |
||
324 | sqlite3DbFree( db, ref zWhere ); |
||
325 | } |
||
326 | return zNew; |
||
327 | } |
||
328 | |||
329 | #if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER) |
||
330 | /* |
||
331 | ** Generate the text of a WHERE expression which can be used to select all |
||
332 | ** tables that have foreign key constraints that refer to table pTab (i.e. |
||
333 | ** constraints for which pTab is the parent table) from the sqlite_master |
||
334 | ** table. |
||
335 | */ |
||
336 | static string whereForeignKeys( Parse pParse, Table pTab ) |
||
337 | { |
||
338 | FKey p; |
||
339 | string zWhere = string.Empty; |
||
340 | for ( p = sqlite3FkReferences( pTab ); p != null; p = p.pNextTo ) |
||
341 | { |
||
342 | zWhere = whereOrName( pParse.db, zWhere, p.pFrom.zName ); |
||
343 | } |
||
344 | return zWhere; |
||
345 | } |
||
346 | #endif |
||
347 | |||
348 | /* |
||
349 | ** Generate the text of a WHERE expression which can be used to select all |
||
350 | ** temporary triggers on table pTab from the sqlite_temp_master table. If |
||
351 | ** table pTab has no temporary triggers, or is itself stored in the |
||
352 | ** temporary database, NULL is returned. |
||
353 | */ |
||
354 | static string whereTempTriggers( Parse pParse, Table pTab ) |
||
355 | { |
||
356 | Trigger pTrig; |
||
357 | string zWhere = string.Empty; |
||
358 | Schema pTempSchema = pParse.db.aDb[1].pSchema; /* Temp db schema */ |
||
359 | |||
360 | /* If the table is not located in the temp.db (in which case NULL is |
||
361 | ** returned, loop through the tables list of triggers. For each trigger |
||
362 | ** that is not part of the temp.db schema, add a clause to the WHERE |
||
363 | ** expression being built up in zWhere. |
||
364 | */ |
||
365 | if ( pTab.pSchema != pTempSchema ) |
||
366 | { |
||
367 | sqlite3 db = pParse.db; |
||
368 | for ( pTrig = sqlite3TriggerList( pParse, pTab ); pTrig != null; pTrig = pTrig.pNext ) |
||
369 | { |
||
370 | if ( pTrig.pSchema == pTempSchema ) |
||
371 | { |
||
372 | zWhere = whereOrName( db, zWhere, pTrig.zName ); |
||
373 | } |
||
374 | } |
||
375 | } |
||
376 | if ( !string.IsNullOrEmpty( zWhere ) ) |
||
377 | { |
||
378 | zWhere = sqlite3MPrintf( pParse.db, "type='trigger' AND (%s)", zWhere ); |
||
379 | //sqlite3DbFree( pParse.db, ref zWhere ); |
||
380 | //zWhere = zNew; |
||
381 | } |
||
382 | return zWhere; |
||
383 | } |
||
384 | |||
385 | /* |
||
386 | ** Generate code to drop and reload the internal representation of table |
||
387 | ** pTab from the database, including triggers and temporary triggers. |
||
388 | ** Argument zName is the name of the table in the database schema at |
||
389 | ** the time the generated code is executed. This can be different from |
||
390 | ** pTab.zName if this function is being called to code part of an |
||
391 | ** "ALTER TABLE RENAME TO" statement. |
||
392 | */ |
||
393 | static void reloadTableSchema( Parse pParse, Table pTab, string zName ) |
||
394 | { |
||
395 | Vdbe v; |
||
396 | string zWhere; |
||
397 | int iDb; /* Index of database containing pTab */ |
||
398 | #if !SQLITE_OMIT_TRIGGER |
||
399 | Trigger pTrig; |
||
400 | #endif |
||
401 | |||
402 | v = sqlite3GetVdbe( pParse ); |
||
403 | if ( NEVER( v == null ) ) |
||
404 | return; |
||
405 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) ); |
||
406 | iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema ); |
||
407 | Debug.Assert( iDb >= 0 ); |
||
408 | |||
409 | #if !SQLITE_OMIT_TRIGGER |
||
410 | /* Drop any table triggers from the internal schema. */ |
||
411 | for ( pTrig = sqlite3TriggerList( pParse, pTab ); pTrig != null; pTrig = pTrig.pNext ) |
||
412 | { |
||
413 | int iTrigDb = sqlite3SchemaToIndex( pParse.db, pTrig.pSchema ); |
||
414 | Debug.Assert( iTrigDb == iDb || iTrigDb == 1 ); |
||
415 | sqlite3VdbeAddOp4( v, OP_DropTrigger, iTrigDb, 0, 0, pTrig.zName, 0 ); |
||
416 | } |
||
417 | #endif |
||
418 | |||
419 | /* Drop the table and index from the internal schema. */ |
||
420 | sqlite3VdbeAddOp4( v, OP_DropTable, iDb, 0, 0, pTab.zName, 0 ); |
||
421 | |||
422 | /* Reload the table, index and permanent trigger schemas. */ |
||
423 | zWhere = sqlite3MPrintf( pParse.db, "tbl_name=%Q", zName ); |
||
424 | if ( zWhere == null ) |
||
425 | return; |
||
426 | sqlite3VdbeAddParseSchemaOp( v, iDb, zWhere ); |
||
427 | |||
428 | #if !SQLITE_OMIT_TRIGGER |
||
429 | /* Now, if the table is not stored in the temp database, reload any temp |
||
430 | ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. |
||
431 | */ |
||
432 | if ( ( zWhere = whereTempTriggers( pParse, pTab ) ).Length > 0 ) |
||
433 | { |
||
434 | sqlite3VdbeAddParseSchemaOp( v, 1, zWhere ); |
||
435 | } |
||
436 | #endif |
||
437 | } |
||
438 | |||
439 | /* |
||
440 | ** Parameter zName is the name of a table that is about to be altered |
||
441 | ** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). |
||
442 | ** If the table is a system table, this function leaves an error message |
||
443 | ** in pParse->zErr (system tables may not be altered) and returns non-zero. |
||
444 | ** |
||
445 | ** Or, if zName is not a system table, zero is returned. |
||
446 | */ |
||
447 | static int isSystemTable( Parse pParse, string zName ) |
||
448 | { |
||
449 | if ( zName.StartsWith( "sqlite_", System.StringComparison.OrdinalIgnoreCase ) ) |
||
450 | { |
||
451 | sqlite3ErrorMsg( pParse, "table %s may not be altered", zName ); |
||
452 | return 1; |
||
453 | } |
||
454 | return 0; |
||
455 | } |
||
456 | |||
457 | /* |
||
458 | ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" |
||
459 | ** command. |
||
460 | */ |
||
461 | static void sqlite3AlterRenameTable( |
||
462 | Parse pParse, /* Parser context. */ |
||
463 | SrcList pSrc, /* The table to rename. */ |
||
464 | Token pName /* The new table name. */ |
||
465 | ) |
||
466 | { |
||
467 | int iDb; /* Database that contains the table */ |
||
468 | string zDb; /* Name of database iDb */ |
||
469 | Table pTab; /* Table being renamed */ |
||
470 | string zName = null; /* NULL-terminated version of pName */ |
||
471 | sqlite3 db = pParse.db; /* Database connection */ |
||
472 | int nTabName; /* Number of UTF-8 characters in zTabName */ |
||
473 | string zTabName; /* Original name of the table */ |
||
474 | Vdbe v; |
||
475 | #if !SQLITE_OMIT_TRIGGER |
||
476 | string zWhere = string.Empty; /* Where clause to locate temp triggers */ |
||
477 | #endif |
||
478 | VTable pVTab = null; /* Non-zero if this is a v-tab with an xRename() */ |
||
479 | int savedDbFlags; /* Saved value of db->flags */ |
||
480 | |||
481 | savedDbFlags = db.flags; |
||
482 | |||
483 | //if ( NEVER( db.mallocFailed != 0 ) ) goto exit_rename_table; |
||
484 | Debug.Assert( pSrc.nSrc == 1 ); |
||
485 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) ); |
||
486 | pTab = sqlite3LocateTable( pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase ); |
||
487 | if ( pTab == null ) |
||
488 | goto exit_rename_table; |
||
489 | iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema ); |
||
490 | zDb = db.aDb[iDb].zName; |
||
491 | db.flags |= SQLITE_PreferBuiltin; |
||
492 | |||
493 | /* Get a NULL terminated version of the new table name. */ |
||
494 | zName = sqlite3NameFromToken( db, pName ); |
||
495 | if ( zName == null ) |
||
496 | goto exit_rename_table; |
||
497 | |||
498 | /* Check that a table or index named 'zName' does not already exist |
||
499 | ** in database iDb. If so, this is an error. |
||
500 | */ |
||
501 | if ( sqlite3FindTable( db, zName, zDb ) != null || sqlite3FindIndex( db, zName, zDb ) != null ) |
||
502 | { |
||
503 | sqlite3ErrorMsg( pParse, |
||
504 | "there is already another table or index with this name: %s", zName ); |
||
505 | goto exit_rename_table; |
||
506 | } |
||
507 | |||
508 | /* Make sure it is not a system table being altered, or a reserved name |
||
509 | ** that the table is being renamed to. |
||
510 | */ |
||
511 | if ( SQLITE_OK!=isSystemTable(pParse, pTab.zName) ) |
||
512 | { |
||
513 | goto exit_rename_table; |
||
514 | } |
||
515 | if ( SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) ) |
||
516 | { |
||
517 | goto exit_rename_table; |
||
518 | } |
||
519 | |||
520 | #if !SQLITE_OMIT_VIEW |
||
521 | if ( pTab.pSelect != null ) |
||
522 | { |
||
523 | sqlite3ErrorMsg( pParse, "view %s may not be altered", pTab.zName ); |
||
524 | goto exit_rename_table; |
||
525 | } |
||
526 | #endif |
||
527 | |||
528 | #if !SQLITE_OMIT_AUTHORIZATION |
||
529 | /* Invoke the authorization callback. */ |
||
530 | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab.zName, 0) ){ |
||
531 | goto exit_rename_table; |
||
532 | } |
||
533 | #endif |
||
534 | |||
535 | if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 ) |
||
536 | { |
||
537 | goto exit_rename_table; |
||
538 | } |
||
539 | #if !SQLITE_OMIT_VIRTUALTABLE |
||
540 | if ( IsVirtual( pTab ) ) |
||
541 | { |
||
542 | pVTab = sqlite3GetVTable( db, pTab ); |
||
543 | if ( pVTab.pVtab.pModule.xRename == null ) |
||
544 | { |
||
545 | pVTab = null; |
||
546 | } |
||
547 | } |
||
548 | #endif |
||
549 | /* Begin a transaction and code the VerifyCookie for database iDb. |
||
550 | ** Then modify the schema cookie (since the ALTER TABLE modifies the |
||
551 | ** schema). Open a statement transaction if the table is a virtual |
||
552 | ** table. |
||
553 | */ |
||
554 | v = sqlite3GetVdbe( pParse ); |
||
555 | if ( v == null ) |
||
556 | { |
||
557 | goto exit_rename_table; |
||
558 | } |
||
559 | sqlite3BeginWriteOperation( pParse, pVTab != null ? 1 : 0, iDb ); |
||
560 | sqlite3ChangeCookie( pParse, iDb ); |
||
561 | |||
562 | /* If this is a virtual table, invoke the xRename() function if |
||
563 | ** one is defined. The xRename() callback will modify the names |
||
564 | ** of any resources used by the v-table implementation (including other |
||
565 | ** SQLite tables) that are identified by the name of the virtual table. |
||
566 | */ |
||
567 | #if !SQLITE_OMIT_VIRTUALTABLE |
||
568 | if ( pVTab !=null) |
||
569 | { |
||
570 | int i = ++pParse.nMem; |
||
571 | sqlite3VdbeAddOp4( v, OP_String8, 0, i, 0, zName, 0 ); |
||
572 | sqlite3VdbeAddOp4( v, OP_VRename, i, 0, 0, pVTab, P4_VTAB ); |
||
573 | sqlite3MayAbort(pParse); |
||
574 | } |
||
575 | #endif |
||
576 | |||
577 | /* figure out how many UTF-8 characters are in zName */ |
||
578 | zTabName = pTab.zName; |
||
579 | nTabName = sqlite3Utf8CharLen( zTabName, -1 ); |
||
580 | |||
581 | #if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER) |
||
582 | if ( ( db.flags & SQLITE_ForeignKeys ) != 0 ) |
||
583 | { |
||
584 | /* If foreign-key support is enabled, rewrite the CREATE TABLE |
||
585 | ** statements corresponding to all child tables of foreign key constraints |
||
586 | ** for which the renamed table is the parent table. */ |
||
587 | if ( ( zWhere = whereForeignKeys( pParse, pTab ) ) != null ) |
||
588 | { |
||
589 | sqlite3NestedParse( pParse, |
||
590 | "UPDATE \"%w\".%s SET " + |
||
591 | "sql = sqlite_rename_parent(sql, %Q, %Q) " + |
||
592 | "WHERE %s;", zDb, SCHEMA_TABLE( iDb ), zTabName, zName, zWhere ); |
||
593 | sqlite3DbFree( db, ref zWhere ); |
||
594 | } |
||
595 | } |
||
596 | #endif |
||
597 | |||
598 | /* Modify the sqlite_master table to use the new table name. */ |
||
599 | sqlite3NestedParse( pParse, |
||
600 | "UPDATE %Q.%s SET " + |
||
601 | #if SQLITE_OMIT_TRIGGER |
||
602 | "sql = sqlite_rename_table(sql, %Q), " + |
||
603 | #else |
||
604 | "sql = CASE " + |
||
605 | "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" + |
||
606 | "ELSE sqlite_rename_table(sql, %Q) END, " + |
||
607 | #endif |
||
608 | "tbl_name = %Q, " + |
||
609 | "name = CASE " + |
||
610 | "WHEN type='table' THEN %Q " + |
||
611 | "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " + |
||
612 | "'sqlite_autoindex_' || %Q || substr(name,%d+18) " + |
||
613 | "ELSE name END " + |
||
614 | "WHERE tbl_name=%Q AND " + |
||
615 | "(type='table' OR type='index' OR type='trigger');", |
||
616 | zDb, SCHEMA_TABLE( iDb ), zName, zName, zName, |
||
617 | #if !SQLITE_OMIT_TRIGGER |
||
618 | zName, |
||
619 | #endif |
||
620 | zName, nTabName, zTabName |
||
621 | ); |
||
622 | |||
623 | #if !SQLITE_OMIT_AUTOINCREMENT |
||
624 | /* If the sqlite_sequence table exists in this database, then update |
||
625 | ** it with the new table name. |
||
626 | */ |
||
627 | if ( sqlite3FindTable( db, "sqlite_sequence", zDb ) != null ) |
||
628 | { |
||
629 | sqlite3NestedParse( pParse, |
||
630 | "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", |
||
631 | zDb, zName, pTab.zName |
||
632 | ); |
||
633 | } |
||
634 | #endif |
||
635 | |||
636 | #if !SQLITE_OMIT_TRIGGER |
||
637 | /* If there are TEMP triggers on this table, modify the sqlite_temp_master |
||
638 | ** table. Don't do this if the table being ALTERed is itself located in |
||
639 | ** the temp database. |
||
640 | */ |
||
641 | if ( ( zWhere = whereTempTriggers( pParse, pTab ) ).Length > 0 ) |
||
642 | { |
||
643 | sqlite3NestedParse( pParse, |
||
644 | "UPDATE sqlite_temp_master SET " + |
||
645 | "sql = sqlite_rename_trigger(sql, %Q), " + |
||
646 | "tbl_name = %Q " + |
||
647 | "WHERE %s;", zName, zName, zWhere ); |
||
648 | sqlite3DbFree( db, ref zWhere ); |
||
649 | } |
||
650 | #endif |
||
651 | |||
652 | #if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER) |
||
653 | if ( ( db.flags & SQLITE_ForeignKeys ) != 0 ) |
||
654 | { |
||
655 | FKey p; |
||
656 | for ( p = sqlite3FkReferences( pTab ); p != null; p = p.pNextTo ) |
||
657 | { |
||
658 | Table pFrom = p.pFrom; |
||
659 | if ( pFrom != pTab ) |
||
660 | { |
||
661 | reloadTableSchema( pParse, p.pFrom, pFrom.zName ); |
||
662 | } |
||
663 | } |
||
664 | } |
||
665 | #endif |
||
666 | |||
667 | /* Drop and reload the internal table schema. */ |
||
668 | reloadTableSchema( pParse, pTab, zName ); |
||
669 | |||
670 | exit_rename_table: |
||
671 | sqlite3SrcListDelete( db, ref pSrc ); |
||
672 | sqlite3DbFree( db, ref zName ); |
||
673 | db.flags = savedDbFlags; |
||
674 | } |
||
675 | |||
676 | /* |
||
677 | ** Generate code to make sure the file format number is at least minFormat. |
||
678 | ** The generated code will increase the file format number if necessary. |
||
679 | */ |
||
680 | static void sqlite3MinimumFileFormat( Parse pParse, int iDb, int minFormat ) |
||
681 | { |
||
682 | Vdbe v; |
||
683 | v = sqlite3GetVdbe( pParse ); |
||
684 | /* The VDBE should have been allocated before this routine is called. |
||
685 | ** If that allocation failed, we would have quit before reaching this |
||
686 | ** point */ |
||
687 | if ( ALWAYS( v ) ) |
||
688 | { |
||
689 | int r1 = sqlite3GetTempReg( pParse ); |
||
690 | int r2 = sqlite3GetTempReg( pParse ); |
||
691 | int j1; |
||
692 | sqlite3VdbeAddOp3( v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT ); |
||
693 | sqlite3VdbeUsesBtree( v, iDb ); |
||
694 | sqlite3VdbeAddOp2( v, OP_Integer, minFormat, r2 ); |
||
695 | j1 = sqlite3VdbeAddOp3( v, OP_Ge, r2, 0, r1 ); |
||
696 | sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2 ); |
||
697 | sqlite3VdbeJumpHere( v, j1 ); |
||
698 | sqlite3ReleaseTempReg( pParse, r1 ); |
||
699 | sqlite3ReleaseTempReg( pParse, r2 ); |
||
700 | } |
||
701 | } |
||
702 | |||
703 | /* |
||
704 | ** This function is called after an "ALTER TABLE ... ADD" statement |
||
705 | ** has been parsed. Argument pColDef contains the text of the new |
||
706 | ** column definition. |
||
707 | ** |
||
708 | ** The Table structure pParse.pNewTable was extended to include |
||
709 | ** the new column during parsing. |
||
710 | */ |
||
711 | static void sqlite3AlterFinishAddColumn( Parse pParse, Token pColDef ) |
||
712 | { |
||
713 | Table pNew; /* Copy of pParse.pNewTable */ |
||
714 | Table pTab; /* Table being altered */ |
||
715 | int iDb; /* Database number */ |
||
716 | string zDb; /* Database name */ |
||
717 | string zTab; /* Table name */ |
||
718 | string zCol; /* Null-terminated column definition */ |
||
719 | Column pCol; /* The new column */ |
||
720 | Expr pDflt; /* Default value for the new column */ |
||
721 | sqlite3 db; /* The database connection; */ |
||
722 | |||
723 | db = pParse.db; |
||
724 | if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) |
||
725 | return; |
||
726 | pNew = pParse.pNewTable; |
||
727 | Debug.Assert( pNew != null ); |
||
728 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) ); |
||
729 | iDb = sqlite3SchemaToIndex( db, pNew.pSchema ); |
||
730 | zDb = db.aDb[iDb].zName; |
||
731 | zTab = pNew.zName.Substring( 16 );// zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */ |
||
732 | pCol = pNew.aCol[pNew.nCol - 1]; |
||
733 | pDflt = pCol.pDflt; |
||
734 | pTab = sqlite3FindTable( db, zTab, zDb ); |
||
735 | Debug.Assert( pTab != null ); |
||
736 | |||
737 | #if !SQLITE_OMIT_AUTHORIZATION |
||
738 | /* Invoke the authorization callback. */ |
||
739 | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab.zName, 0) ){ |
||
740 | return; |
||
741 | } |
||
742 | #endif |
||
743 | |||
744 | /* If the default value for the new column was specified with a |
||
745 | ** literal NULL, then set pDflt to 0. This simplifies checking |
||
746 | ** for an SQL NULL default below. |
||
747 | */ |
||
748 | if ( pDflt != null && pDflt.op == TK_NULL ) |
||
749 | { |
||
750 | pDflt = null; |
||
751 | } |
||
752 | |||
753 | /* Check that the new column is not specified as PRIMARY KEY or UNIQUE. |
||
754 | ** If there is a NOT NULL constraint, then the default value for the |
||
755 | ** column must not be NULL. |
||
756 | */ |
||
757 | if ( pCol.isPrimKey != 0 ) |
||
758 | { |
||
759 | sqlite3ErrorMsg( pParse, "Cannot add a PRIMARY KEY column" ); |
||
760 | return; |
||
761 | } |
||
762 | if ( pNew.pIndex != null ) |
||
763 | { |
||
764 | sqlite3ErrorMsg( pParse, "Cannot add a UNIQUE column" ); |
||
765 | return; |
||
766 | } |
||
767 | if ( ( db.flags & SQLITE_ForeignKeys ) != 0 && pNew.pFKey != null && pDflt != null ) |
||
768 | { |
||
769 | sqlite3ErrorMsg( pParse, |
||
770 | "Cannot add a REFERENCES column with non-NULL default value" ); |
||
771 | return; |
||
772 | } |
||
773 | if ( pCol.notNull != 0 && pDflt == null ) |
||
774 | { |
||
775 | sqlite3ErrorMsg( pParse, |
||
776 | "Cannot add a NOT NULL column with default value NULL" ); |
||
777 | return; |
||
778 | } |
||
779 | |||
780 | /* Ensure the default expression is something that sqlite3ValueFromExpr() |
||
781 | ** can handle (i.e. not CURRENT_TIME etc.) |
||
782 | */ |
||
783 | if ( pDflt != null ) |
||
784 | { |
||
785 | sqlite3_value pVal = null; |
||
786 | if ( sqlite3ValueFromExpr( db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, ref pVal ) != 0 ) |
||
787 | { |
||
788 | // db.mallocFailed = 1; |
||
789 | return; |
||
790 | } |
||
791 | if ( pVal == null ) |
||
792 | { |
||
793 | sqlite3ErrorMsg( pParse, "Cannot add a column with non-constant default" ); |
||
794 | return; |
||
795 | } |
||
796 | sqlite3ValueFree( ref pVal ); |
||
797 | } |
||
798 | |||
799 | /* Modify the CREATE TABLE statement. */ |
||
800 | zCol = pColDef.z.Substring( 0, pColDef.n ).Replace( ";", " " ).Trim();//sqlite3DbStrNDup(db, (char*)pColDef.z, pColDef.n); |
||
801 | if ( zCol != null ) |
||
802 | { |
||
803 | // char zEnd = zCol[pColDef.n-1]; |
||
804 | int savedDbFlags = db.flags; |
||
805 | // while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ |
||
806 | // zEnd-- = '\0'; |
||
807 | // } |
||
808 | db.flags |= SQLITE_PreferBuiltin; |
||
809 | sqlite3NestedParse( pParse, |
||
810 | "UPDATE \"%w\".%s SET " + |
||
811 | "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " + |
||
812 | "WHERE type = 'table' AND name = %Q", |
||
813 | zDb, SCHEMA_TABLE( iDb ), pNew.addColOffset, zCol, pNew.addColOffset + 1, |
||
814 | zTab |
||
815 | ); |
||
816 | sqlite3DbFree( db, ref zCol ); |
||
817 | db.flags = savedDbFlags; |
||
818 | } |
||
819 | |||
820 | /* If the default value of the new column is NULL, then set the file |
||
821 | ** format to 2. If the default value of the new column is not NULL, |
||
822 | ** the file format becomes 3. |
||
823 | */ |
||
824 | sqlite3MinimumFileFormat( pParse, iDb, pDflt != null ? 3 : 2 ); |
||
825 | |||
826 | /* Reload the schema of the modified table. */ |
||
827 | reloadTableSchema( pParse, pTab, pTab.zName ); |
||
828 | } |
||
829 | |||
830 | /* |
||
831 | ** This function is called by the parser after the table-name in |
||
832 | ** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument |
||
833 | ** pSrc is the full-name of the table being altered. |
||
834 | ** |
||
835 | ** This routine makes a (partial) copy of the Table structure |
||
836 | ** for the table being altered and sets Parse.pNewTable to point |
||
837 | ** to it. Routines called by the parser as the column definition |
||
838 | ** is parsed (i.e. sqlite3AddColumn()) add the new Column data to |
||
839 | ** the copy. The copy of the Table structure is deleted by tokenize.c |
||
840 | ** after parsing is finished. |
||
841 | ** |
||
842 | ** Routine sqlite3AlterFinishAddColumn() will be called to complete |
||
843 | ** coding the "ALTER TABLE ... ADD" statement. |
||
844 | */ |
||
845 | static void sqlite3AlterBeginAddColumn( Parse pParse, SrcList pSrc ) |
||
846 | { |
||
847 | Table pNew; |
||
848 | Table pTab; |
||
849 | Vdbe v; |
||
850 | int iDb; |
||
851 | int i; |
||
852 | int nAlloc; |
||
853 | sqlite3 db = pParse.db; |
||
854 | |||
855 | /* Look up the table being altered. */ |
||
856 | Debug.Assert( pParse.pNewTable == null ); |
||
857 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) ); |
||
858 | // if ( db.mallocFailed != 0 ) goto exit_begin_add_column; |
||
859 | pTab = sqlite3LocateTable( pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase ); |
||
860 | if ( pTab == null ) |
||
861 | goto exit_begin_add_column; |
||
862 | |||
863 | if ( IsVirtual( pTab ) ) |
||
864 | { |
||
865 | sqlite3ErrorMsg( pParse, "virtual tables may not be altered" ); |
||
866 | goto exit_begin_add_column; |
||
867 | } |
||
868 | |||
869 | /* Make sure this is not an attempt to ALTER a view. */ |
||
870 | if ( pTab.pSelect != null ) |
||
871 | { |
||
872 | sqlite3ErrorMsg( pParse, "Cannot add a column to a view" ); |
||
873 | goto exit_begin_add_column; |
||
874 | } |
||
875 | if ( SQLITE_OK != isSystemTable( pParse, pTab.zName ) ) |
||
876 | { |
||
877 | goto exit_begin_add_column; |
||
878 | } |
||
879 | |||
880 | Debug.Assert( pTab.addColOffset > 0 ); |
||
881 | iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); |
||
882 | |||
883 | /* Put a copy of the Table struct in Parse.pNewTable for the |
||
884 | ** sqlite3AddColumn() function and friends to modify. But modify |
||
885 | ** the name by adding an "sqlite_altertab_" prefix. By adding this |
||
886 | ** prefix, we insure that the name will not collide with an existing |
||
887 | ** table because user table are not allowed to have the "sqlite_" |
||
888 | ** prefix on their name. |
||
889 | */ |
||
890 | pNew = new Table();// (Table*)sqlite3DbMallocZero( db, sizeof(Table)) |
||
891 | if ( pNew == null ) |
||
892 | goto exit_begin_add_column; |
||
893 | pParse.pNewTable = pNew; |
||
894 | pNew.nRef = 1; |
||
895 | pNew.nCol = pTab.nCol; |
||
896 | Debug.Assert( pNew.nCol > 0 ); |
||
897 | nAlloc = ( ( ( pNew.nCol - 1 ) / 8 ) * 8 ) + 8; |
||
898 | Debug.Assert( nAlloc >= pNew.nCol && nAlloc % 8 == 0 && nAlloc - pNew.nCol < 8 ); |
||
899 | pNew.aCol = new Column[nAlloc];// (Column*)sqlite3DbMallocZero( db, sizeof(Column) * nAlloc ); |
||
900 | pNew.zName = sqlite3MPrintf( db, "sqlite_altertab_%s", pTab.zName ); |
||
901 | if ( pNew.aCol == null || pNew.zName == null ) |
||
902 | { |
||
903 | // db.mallocFailed = 1; |
||
904 | goto exit_begin_add_column; |
||
905 | } |
||
906 | // memcpy( pNew.aCol, pTab.aCol, sizeof(Column) * pNew.nCol ); |
||
907 | for ( i = 0; i < pNew.nCol; i++ ) |
||
908 | { |
||
909 | Column pCol = pTab.aCol[i].Copy(); |
||
910 | // sqlite3DbStrDup( db, pCol.zName ); |
||
911 | pCol.zColl = null; |
||
912 | pCol.zType = null; |
||
913 | pCol.pDflt = null; |
||
914 | pCol.zDflt = null; |
||
915 | pNew.aCol[i] = pCol; |
||
916 | } |
||
917 | pNew.pSchema = db.aDb[iDb].pSchema; |
||
918 | pNew.addColOffset = pTab.addColOffset; |
||
919 | pNew.nRef = 1; |
||
920 | |||
921 | /* Begin a transaction and increment the schema cookie. */ |
||
922 | sqlite3BeginWriteOperation( pParse, 0, iDb ); |
||
923 | v = sqlite3GetVdbe( pParse ); |
||
924 | if ( v == null ) |
||
925 | goto exit_begin_add_column; |
||
926 | sqlite3ChangeCookie( pParse, iDb ); |
||
927 | |||
928 | exit_begin_add_column: |
||
929 | sqlite3SrcListDelete( db, ref pSrc ); |
||
930 | return; |
||
931 | } |
||
932 | #endif // * SQLITE_ALTER_TABLE */ |
||
933 | } |
||
934 | } |