diff options
Diffstat (limited to 'libraries/sqlite/win32/delete.c')
-rwxr-xr-x | libraries/sqlite/win32/delete.c | 467 |
1 files changed, 0 insertions, 467 deletions
diff --git a/libraries/sqlite/win32/delete.c b/libraries/sqlite/win32/delete.c deleted file mode 100755 index 12e61cd..0000000 --- a/libraries/sqlite/win32/delete.c +++ /dev/null | |||
@@ -1,467 +0,0 @@ | |||
1 | /* | ||
2 | ** 2001 September 15 | ||
3 | ** | ||
4 | ** The author disclaims copyright to this source code. In place of | ||
5 | ** a legal notice, here is a blessing: | ||
6 | ** | ||
7 | ** May you do good and not evil. | ||
8 | ** May you find forgiveness for yourself and forgive others. | ||
9 | ** May you share freely, never taking more than you give. | ||
10 | ** | ||
11 | ************************************************************************* | ||
12 | ** This file contains C code routines that are called by the parser | ||
13 | ** in order to generate code for DELETE FROM statements. | ||
14 | ** | ||
15 | ** $Id: delete.c,v 1.130 2007/08/16 04:30:40 drh Exp $ | ||
16 | */ | ||
17 | #include "sqliteInt.h" | ||
18 | |||
19 | /* | ||
20 | ** Look up every table that is named in pSrc. If any table is not found, | ||
21 | ** add an error message to pParse->zErrMsg and return NULL. If all tables | ||
22 | ** are found, return a pointer to the last table. | ||
23 | */ | ||
24 | Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ | ||
25 | Table *pTab = 0; | ||
26 | int i; | ||
27 | struct SrcList_item *pItem; | ||
28 | for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){ | ||
29 | pTab = sqlite3LocateTable(pParse, pItem->zName, pItem->zDatabase); | ||
30 | sqlite3DeleteTable(pItem->pTab); | ||
31 | pItem->pTab = pTab; | ||
32 | if( pTab ){ | ||
33 | pTab->nRef++; | ||
34 | } | ||
35 | } | ||
36 | return pTab; | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | ** Check to make sure the given table is writable. If it is not | ||
41 | ** writable, generate an error message and return 1. If it is | ||
42 | ** writable return 0; | ||
43 | */ | ||
44 | int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ | ||
45 | if( (pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0 | ||
46 | && pParse->nested==0) | ||
47 | #ifndef SQLITE_OMIT_VIRTUALTABLE | ||
48 | || (pTab->pMod && pTab->pMod->pModule->xUpdate==0) | ||
49 | #endif | ||
50 | ){ | ||
51 | sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); | ||
52 | return 1; | ||
53 | } | ||
54 | #ifndef SQLITE_OMIT_VIEW | ||
55 | if( !viewOk && pTab->pSelect ){ | ||
56 | sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); | ||
57 | return 1; | ||
58 | } | ||
59 | #endif | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | ** Generate code that will open a table for reading. | ||
65 | */ | ||
66 | void sqlite3OpenTable( | ||
67 | Parse *p, /* Generate code into this VDBE */ | ||
68 | int iCur, /* The cursor number of the table */ | ||
69 | int iDb, /* The database index in sqlite3.aDb[] */ | ||
70 | Table *pTab, /* The table to be opened */ | ||
71 | int opcode /* OP_OpenRead or OP_OpenWrite */ | ||
72 | ){ | ||
73 | Vdbe *v; | ||
74 | if( IsVirtual(pTab) ) return; | ||
75 | v = sqlite3GetVdbe(p); | ||
76 | assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); | ||
77 | sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName); | ||
78 | sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); | ||
79 | VdbeComment((v, "# %s", pTab->zName)); | ||
80 | sqlite3VdbeAddOp(v, opcode, iCur, pTab->tnum); | ||
81 | sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); | ||
82 | } | ||
83 | |||
84 | |||
85 | /* | ||
86 | ** Generate code for a DELETE FROM statement. | ||
87 | ** | ||
88 | ** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL; | ||
89 | ** \________/ \________________/ | ||
90 | ** pTabList pWhere | ||
91 | */ | ||
92 | void sqlite3DeleteFrom( | ||
93 | Parse *pParse, /* The parser context */ | ||
94 | SrcList *pTabList, /* The table from which we should delete things */ | ||
95 | Expr *pWhere /* The WHERE clause. May be null */ | ||
96 | ){ | ||
97 | Vdbe *v; /* The virtual database engine */ | ||
98 | Table *pTab; /* The table from which records will be deleted */ | ||
99 | const char *zDb; /* Name of database holding pTab */ | ||
100 | int end, addr = 0; /* A couple addresses of generated code */ | ||
101 | int i; /* Loop counter */ | ||
102 | WhereInfo *pWInfo; /* Information about the WHERE clause */ | ||
103 | Index *pIdx; /* For looping over indices of the table */ | ||
104 | int iCur; /* VDBE Cursor number for pTab */ | ||
105 | sqlite3 *db; /* Main database structure */ | ||
106 | AuthContext sContext; /* Authorization context */ | ||
107 | int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ | ||
108 | NameContext sNC; /* Name context to resolve expressions in */ | ||
109 | int iDb; /* Database number */ | ||
110 | int memCnt = 0; /* Memory cell used for change counting */ | ||
111 | |||
112 | #ifndef SQLITE_OMIT_TRIGGER | ||
113 | int isView; /* True if attempting to delete from a view */ | ||
114 | int triggers_exist = 0; /* True if any triggers exist */ | ||
115 | #endif | ||
116 | |||
117 | sContext.pParse = 0; | ||
118 | db = pParse->db; | ||
119 | if( pParse->nErr || db->mallocFailed ){ | ||
120 | goto delete_from_cleanup; | ||
121 | } | ||
122 | assert( pTabList->nSrc==1 ); | ||
123 | |||
124 | /* Locate the table which we want to delete. This table has to be | ||
125 | ** put in an SrcList structure because some of the subroutines we | ||
126 | ** will be calling are designed to work with multiple tables and expect | ||
127 | ** an SrcList* parameter instead of just a Table* parameter. | ||
128 | */ | ||
129 | pTab = sqlite3SrcListLookup(pParse, pTabList); | ||
130 | if( pTab==0 ) goto delete_from_cleanup; | ||
131 | |||
132 | /* Figure out if we have any triggers and if the table being | ||
133 | ** deleted from is a view | ||
134 | */ | ||
135 | #ifndef SQLITE_OMIT_TRIGGER | ||
136 | triggers_exist = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0); | ||
137 | isView = pTab->pSelect!=0; | ||
138 | #else | ||
139 | # define triggers_exist 0 | ||
140 | # define isView 0 | ||
141 | #endif | ||
142 | #ifdef SQLITE_OMIT_VIEW | ||
143 | # undef isView | ||
144 | # define isView 0 | ||
145 | #endif | ||
146 | |||
147 | if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ | ||
148 | goto delete_from_cleanup; | ||
149 | } | ||
150 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | ||
151 | assert( iDb<db->nDb ); | ||
152 | zDb = db->aDb[iDb].zName; | ||
153 | if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ | ||
154 | goto delete_from_cleanup; | ||
155 | } | ||
156 | |||
157 | /* If pTab is really a view, make sure it has been initialized. | ||
158 | */ | ||
159 | if( sqlite3ViewGetColumnNames(pParse, pTab) ){ | ||
160 | goto delete_from_cleanup; | ||
161 | } | ||
162 | |||
163 | /* Allocate a cursor used to store the old.* data for a trigger. | ||
164 | */ | ||
165 | if( triggers_exist ){ | ||
166 | oldIdx = pParse->nTab++; | ||
167 | } | ||
168 | |||
169 | /* Resolve the column names in the WHERE clause. | ||
170 | */ | ||
171 | assert( pTabList->nSrc==1 ); | ||
172 | iCur = pTabList->a[0].iCursor = pParse->nTab++; | ||
173 | memset(&sNC, 0, sizeof(sNC)); | ||
174 | sNC.pParse = pParse; | ||
175 | sNC.pSrcList = pTabList; | ||
176 | if( sqlite3ExprResolveNames(&sNC, pWhere) ){ | ||
177 | goto delete_from_cleanup; | ||
178 | } | ||
179 | |||
180 | /* Start the view context | ||
181 | */ | ||
182 | if( isView ){ | ||
183 | sqlite3AuthContextPush(pParse, &sContext, pTab->zName); | ||
184 | } | ||
185 | |||
186 | /* Begin generating code. | ||
187 | */ | ||
188 | v = sqlite3GetVdbe(pParse); | ||
189 | if( v==0 ){ | ||
190 | goto delete_from_cleanup; | ||
191 | } | ||
192 | if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); | ||
193 | sqlite3BeginWriteOperation(pParse, triggers_exist, iDb); | ||
194 | |||
195 | /* If we are trying to delete from a view, realize that view into | ||
196 | ** a ephemeral table. | ||
197 | */ | ||
198 | if( isView ){ | ||
199 | Select *pView = sqlite3SelectDup(db, pTab->pSelect); | ||
200 | sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0); | ||
201 | sqlite3SelectDelete(pView); | ||
202 | } | ||
203 | |||
204 | /* Initialize the counter of the number of rows deleted, if | ||
205 | ** we are counting rows. | ||
206 | */ | ||
207 | if( db->flags & SQLITE_CountRows ){ | ||
208 | memCnt = pParse->nMem++; | ||
209 | sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt); | ||
210 | } | ||
211 | |||
212 | /* Special case: A DELETE without a WHERE clause deletes everything. | ||
213 | ** It is easier just to erase the whole table. Note, however, that | ||
214 | ** this means that the row change count will be incorrect. | ||
215 | */ | ||
216 | if( pWhere==0 && !triggers_exist && !IsVirtual(pTab) ){ | ||
217 | if( db->flags & SQLITE_CountRows ){ | ||
218 | /* If counting rows deleted, just count the total number of | ||
219 | ** entries in the table. */ | ||
220 | int endOfLoop = sqlite3VdbeMakeLabel(v); | ||
221 | int addr2; | ||
222 | if( !isView ){ | ||
223 | sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); | ||
224 | } | ||
225 | sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2); | ||
226 | addr2 = sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt); | ||
227 | sqlite3VdbeAddOp(v, OP_Next, iCur, addr2); | ||
228 | sqlite3VdbeResolveLabel(v, endOfLoop); | ||
229 | sqlite3VdbeAddOp(v, OP_Close, iCur, 0); | ||
230 | } | ||
231 | if( !isView ){ | ||
232 | sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb); | ||
233 | if( !pParse->nested ){ | ||
234 | sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC); | ||
235 | } | ||
236 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ | ||
237 | assert( pIdx->pSchema==pTab->pSchema ); | ||
238 | sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, iDb); | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | /* The usual case: There is a WHERE clause so we have to scan through | ||
243 | ** the table and pick which records to delete. | ||
244 | */ | ||
245 | else{ | ||
246 | /* Begin the database scan | ||
247 | */ | ||
248 | pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0); | ||
249 | if( pWInfo==0 ) goto delete_from_cleanup; | ||
250 | |||
251 | /* Remember the rowid of every item to be deleted. | ||
252 | */ | ||
253 | sqlite3VdbeAddOp(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, 0); | ||
254 | sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0); | ||
255 | if( db->flags & SQLITE_CountRows ){ | ||
256 | sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt); | ||
257 | } | ||
258 | |||
259 | /* End the database scan loop. | ||
260 | */ | ||
261 | sqlite3WhereEnd(pWInfo); | ||
262 | |||
263 | /* Open the pseudo-table used to store OLD if there are triggers. | ||
264 | */ | ||
265 | if( triggers_exist ){ | ||
266 | sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); | ||
267 | sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol); | ||
268 | } | ||
269 | |||
270 | /* Delete every item whose key was written to the list during the | ||
271 | ** database scan. We have to delete items after the scan is complete | ||
272 | ** because deleting an item can change the scan order. | ||
273 | */ | ||
274 | end = sqlite3VdbeMakeLabel(v); | ||
275 | |||
276 | /* This is the beginning of the delete loop when there are | ||
277 | ** row triggers. | ||
278 | */ | ||
279 | if( triggers_exist ){ | ||
280 | addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end); | ||
281 | if( !isView ){ | ||
282 | sqlite3VdbeAddOp(v, OP_Dup, 0, 0); | ||
283 | sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); | ||
284 | } | ||
285 | sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); | ||
286 | sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); | ||
287 | sqlite3VdbeAddOp(v, OP_RowData, iCur, 0); | ||
288 | sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0); | ||
289 | if( !isView ){ | ||
290 | sqlite3VdbeAddOp(v, OP_Close, iCur, 0); | ||
291 | } | ||
292 | |||
293 | (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab, | ||
294 | -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, | ||
295 | addr); | ||
296 | } | ||
297 | |||
298 | if( !isView ){ | ||
299 | /* Open cursors for the table we are deleting from and all its | ||
300 | ** indices. If there are row triggers, this happens inside the | ||
301 | ** OP_FifoRead loop because the cursor have to all be closed | ||
302 | ** before the trigger fires. If there are no row triggers, the | ||
303 | ** cursors are opened only once on the outside the loop. | ||
304 | */ | ||
305 | sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); | ||
306 | |||
307 | /* This is the beginning of the delete loop when there are no | ||
308 | ** row triggers */ | ||
309 | if( !triggers_exist ){ | ||
310 | addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end); | ||
311 | } | ||
312 | |||
313 | /* Delete the row */ | ||
314 | #ifndef SQLITE_OMIT_VIRTUALTABLE | ||
315 | if( IsVirtual(pTab) ){ | ||
316 | pParse->pVirtualLock = pTab; | ||
317 | sqlite3VdbeOp3(v, OP_VUpdate, 0, 1, (const char*)pTab->pVtab, P3_VTAB); | ||
318 | }else | ||
319 | #endif | ||
320 | { | ||
321 | sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | /* If there are row triggers, close all cursors then invoke | ||
326 | ** the AFTER triggers | ||
327 | */ | ||
328 | if( triggers_exist ){ | ||
329 | if( !isView ){ | ||
330 | for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ | ||
331 | sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); | ||
332 | } | ||
333 | sqlite3VdbeAddOp(v, OP_Close, iCur, 0); | ||
334 | } | ||
335 | (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, | ||
336 | oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, | ||
337 | addr); | ||
338 | } | ||
339 | |||
340 | /* End of the delete loop */ | ||
341 | sqlite3VdbeAddOp(v, OP_Goto, 0, addr); | ||
342 | sqlite3VdbeResolveLabel(v, end); | ||
343 | |||
344 | /* Close the cursors after the loop if there are no row triggers */ | ||
345 | if( !triggers_exist && !IsVirtual(pTab) ){ | ||
346 | for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ | ||
347 | sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); | ||
348 | } | ||
349 | sqlite3VdbeAddOp(v, OP_Close, iCur, 0); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | ** Return the number of rows that were deleted. If this routine is | ||
355 | ** generating code because of a call to sqlite3NestedParse(), do not | ||
356 | ** invoke the callback function. | ||
357 | */ | ||
358 | if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){ | ||
359 | sqlite3VdbeAddOp(v, OP_MemLoad, memCnt, 0); | ||
360 | sqlite3VdbeAddOp(v, OP_Callback, 1, 0); | ||
361 | sqlite3VdbeSetNumCols(v, 1); | ||
362 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P3_STATIC); | ||
363 | } | ||
364 | |||
365 | delete_from_cleanup: | ||
366 | sqlite3AuthContextPop(&sContext); | ||
367 | sqlite3SrcListDelete(pTabList); | ||
368 | sqlite3ExprDelete(pWhere); | ||
369 | return; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | ** This routine generates VDBE code that causes a single row of a | ||
374 | ** single table to be deleted. | ||
375 | ** | ||
376 | ** The VDBE must be in a particular state when this routine is called. | ||
377 | ** These are the requirements: | ||
378 | ** | ||
379 | ** 1. A read/write cursor pointing to pTab, the table containing the row | ||
380 | ** to be deleted, must be opened as cursor number "base". | ||
381 | ** | ||
382 | ** 2. Read/write cursors for all indices of pTab must be open as | ||
383 | ** cursor number base+i for the i-th index. | ||
384 | ** | ||
385 | ** 3. The record number of the row to be deleted must be on the top | ||
386 | ** of the stack. | ||
387 | ** | ||
388 | ** This routine pops the top of the stack to remove the record number | ||
389 | ** and then generates code to remove both the table record and all index | ||
390 | ** entries that point to that record. | ||
391 | */ | ||
392 | void sqlite3GenerateRowDelete( | ||
393 | sqlite3 *db, /* The database containing the index */ | ||
394 | Vdbe *v, /* Generate code into this VDBE */ | ||
395 | Table *pTab, /* Table containing the row to be deleted */ | ||
396 | int iCur, /* Cursor number for the table */ | ||
397 | int count /* Increment the row change counter */ | ||
398 | ){ | ||
399 | int addr; | ||
400 | addr = sqlite3VdbeAddOp(v, OP_NotExists, iCur, 0); | ||
401 | sqlite3GenerateRowIndexDelete(v, pTab, iCur, 0); | ||
402 | sqlite3VdbeAddOp(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); | ||
403 | if( count ){ | ||
404 | sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC); | ||
405 | } | ||
406 | sqlite3VdbeJumpHere(v, addr); | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | ** This routine generates VDBE code that causes the deletion of all | ||
411 | ** index entries associated with a single row of a single table. | ||
412 | ** | ||
413 | ** The VDBE must be in a particular state when this routine is called. | ||
414 | ** These are the requirements: | ||
415 | ** | ||
416 | ** 1. A read/write cursor pointing to pTab, the table containing the row | ||
417 | ** to be deleted, must be opened as cursor number "iCur". | ||
418 | ** | ||
419 | ** 2. Read/write cursors for all indices of pTab must be open as | ||
420 | ** cursor number iCur+i for the i-th index. | ||
421 | ** | ||
422 | ** 3. The "iCur" cursor must be pointing to the row that is to be | ||
423 | ** deleted. | ||
424 | */ | ||
425 | void sqlite3GenerateRowIndexDelete( | ||
426 | Vdbe *v, /* Generate code into this VDBE */ | ||
427 | Table *pTab, /* Table containing the row to be deleted */ | ||
428 | int iCur, /* Cursor number for the table */ | ||
429 | char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */ | ||
430 | ){ | ||
431 | int i; | ||
432 | Index *pIdx; | ||
433 | |||
434 | for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ | ||
435 | if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue; | ||
436 | sqlite3GenerateIndexKey(v, pIdx, iCur); | ||
437 | sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | ** Generate code that will assemble an index key and put it on the top | ||
443 | ** of the tack. The key with be for index pIdx which is an index on pTab. | ||
444 | ** iCur is the index of a cursor open on the pTab table and pointing to | ||
445 | ** the entry that needs indexing. | ||
446 | */ | ||
447 | void sqlite3GenerateIndexKey( | ||
448 | Vdbe *v, /* Generate code into this VDBE */ | ||
449 | Index *pIdx, /* The index for which to generate a key */ | ||
450 | int iCur /* Cursor number for the pIdx->pTable table */ | ||
451 | ){ | ||
452 | int j; | ||
453 | Table *pTab = pIdx->pTable; | ||
454 | |||
455 | sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); | ||
456 | for(j=0; j<pIdx->nColumn; j++){ | ||
457 | int idx = pIdx->aiColumn[j]; | ||
458 | if( idx==pTab->iPKey ){ | ||
459 | sqlite3VdbeAddOp(v, OP_Dup, j, 0); | ||
460 | }else{ | ||
461 | sqlite3VdbeAddOp(v, OP_Column, iCur, idx); | ||
462 | sqlite3ColumnDefault(v, pTab, idx); | ||
463 | } | ||
464 | } | ||
465 | sqlite3VdbeAddOp(v, OP_MakeIdxRec, pIdx->nColumn, 0); | ||
466 | sqlite3IndexAffinityStr(v, pIdx); | ||
467 | } | ||