Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
vdbeblob.c535 linesDownload Raw Back to src
1/*2** 2007 May 13**4** The author disclaims copyright to this source code.  In place of5** 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**13** This file contains code used to implement incremental BLOB I/O.14*/15 16#include "sqliteInt.h"17#include "vdbeInt.h"18 19#ifndef SQLITE_OMIT_INCRBLOB20 21/*22** Valid sqlite3_blob* handles point to Incrblob structures.23*/24typedef struct Incrblob Incrblob;25struct Incrblob {26  int nByte;              /* Size of open blob, in bytes */27  int iOffset;            /* Byte offset of blob in cursor data */28  u16 iCol;               /* Table column this handle is open on */29  BtCursor *pCsr;         /* Cursor pointing at blob row */30  sqlite3_stmt *pStmt;    /* Statement holding cursor open */31  sqlite3 *db;            /* The associated database */32  char *zDb;              /* Database name */33  Table *pTab;            /* Table object */34};35 36 37/*38** This function is used by both blob_open() and blob_reopen(). It seeks39** the b-tree cursor associated with blob handle p to point to row iRow.40** If successful, SQLITE_OK is returned and subsequent calls to41** sqlite3_blob_read() or sqlite3_blob_write() access the specified row.42**43** If an error occurs, or if the specified row does not exist or does not44** contain a value of type TEXT or BLOB in the column nominated when the45** blob handle was opened, then an error code is returned and *pzErr may46** be set to point to a buffer containing an error message. It is the47** responsibility of the caller to free the error message buffer using48** sqlite3DbFree().49**50** If an error does occur, then the b-tree cursor is closed. All subsequent51** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will 52** immediately return SQLITE_ABORT.53*/54static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){55  int rc;                         /* Error code */56  char *zErr = 0;                 /* Error message */57  Vdbe *v = (Vdbe *)p->pStmt;58 59  /* Set the value of register r[1] in the SQL statement to integer iRow. 60  ** This is done directly as a performance optimization61  */62  sqlite3VdbeMemSetInt64(&v->aMem[1], iRow);63 64  /* If the statement has been run before (and is paused at the OP_ResultRow)65  ** then back it up to the point where it does the OP_NotExists.  This could66  ** have been down with an extra OP_Goto, but simply setting the program67  ** counter is faster. */68  if( v->pc>4 ){69    v->pc = 4;70    assert( v->aOp[v->pc].opcode==OP_NotExists );71    rc = sqlite3VdbeExec(v);72  }else{73    rc = sqlite3_step(p->pStmt);74  }75  if( rc==SQLITE_ROW ){76    VdbeCursor *pC = v->apCsr[0];77    u32 type;78    assert( pC!=0 );79    assert( pC->eCurType==CURTYPE_BTREE );80    type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0;81    testcase( pC->nHdrParsed==p->iCol );82    testcase( pC->nHdrParsed==p->iCol+1 );83    if( type<12 ){84      zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",85          type==0?"null": type==7?"real": "integer"86      );87      rc = SQLITE_ERROR;88      sqlite3_finalize(p->pStmt);89      p->pStmt = 0;90    }else{91      p->iOffset = pC->aType[p->iCol + pC->nField];92      p->nByte = sqlite3VdbeSerialTypeLen(type);93      p->pCsr =  pC->uc.pCursor;94      sqlite3BtreeIncrblobCursor(p->pCsr);95    }96  }97 98  if( rc==SQLITE_ROW ){99    rc = SQLITE_OK;100  }else if( p->pStmt ){101    rc = sqlite3_finalize(p->pStmt);102    p->pStmt = 0;103    if( rc==SQLITE_OK ){104      zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow);105      rc = SQLITE_ERROR;106    }else{107      zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db));108    }109  }110 111  assert( rc!=SQLITE_OK || zErr==0 );112  assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE );113 114  *pzErr = zErr;115  return rc;116}117 118/*119** Open a blob handle.120*/121int sqlite3_blob_open(122  sqlite3* db,            /* The database connection */123  const char *zDb,        /* The attached database containing the blob */124  const char *zTable,     /* The table containing the blob */125  const char *zColumn,    /* The column containing the blob */126  sqlite_int64 iRow,      /* The row containing the glob */127  int wrFlag,             /* True -> read/write access, false -> read-only */128  sqlite3_blob **ppBlob   /* Handle for accessing the blob returned here */129){130  int nAttempt = 0;131  int iCol;               /* Index of zColumn in row-record */132  int rc = SQLITE_OK;133  char *zErr = 0;134  Table *pTab;135  Incrblob *pBlob = 0;136  int iDb;137  Parse sParse;138 139#ifdef SQLITE_ENABLE_API_ARMOR140  if( ppBlob==0 ){141    return SQLITE_MISUSE_BKPT;142  }143#endif144  *ppBlob = 0;145#ifdef SQLITE_ENABLE_API_ARMOR146  if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){147    return SQLITE_MISUSE_BKPT;148  }149#endif150  wrFlag = !!wrFlag;                /* wrFlag = (wrFlag ? 1 : 0); */151 152  sqlite3_mutex_enter(db->mutex);153 154  pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));155  while(1){156    sqlite3ParseObjectInit(&sParse,db);157    if( !pBlob ) goto blob_open_out;158    sqlite3DbFree(db, zErr);159    zErr = 0;160 161    sqlite3BtreeEnterAll(db);162    pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);163    if( pTab && IsVirtual(pTab) ){164      pTab = 0;165      sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);166    }167    if( pTab && !HasRowid(pTab) ){168      pTab = 0;169      sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);170    }171    if( pTab && (pTab->tabFlags&TF_HasGenerated)!=0 ){172      pTab = 0;173      sqlite3ErrorMsg(&sParse, "cannot open table with generated columns: %s",174                      zTable);175    }176#ifndef SQLITE_OMIT_VIEW177    if( pTab && IsView(pTab) ){178      pTab = 0;179      sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);180    }181#endif182    if( pTab==0183     || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 &&184         sqlite3OpenTempDatabase(&sParse))185    ){186      if( sParse.zErrMsg ){187        sqlite3DbFree(db, zErr);188        zErr = sParse.zErrMsg;189        sParse.zErrMsg = 0;190      }191      rc = SQLITE_ERROR;192      sqlite3BtreeLeaveAll(db);193      goto blob_open_out;194    }195    pBlob->pTab = pTab;196    pBlob->zDb = db->aDb[iDb].zDbSName;197 198    /* Now search pTab for the exact column. */199    iCol = sqlite3ColumnIndex(pTab, zColumn);200    if( iCol<0 ){201      sqlite3DbFree(db, zErr);202      zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);203      rc = SQLITE_ERROR;204      sqlite3BtreeLeaveAll(db);205      goto blob_open_out;206    }207 208    /* If the value is being opened for writing, check that the209    ** column is not indexed, and that it is not part of a foreign key. 210    */211    if( wrFlag ){212      const char *zFault = 0;213      Index *pIdx;214#ifndef SQLITE_OMIT_FOREIGN_KEY215      if( db->flags&SQLITE_ForeignKeys ){216        /* Check that the column is not part of an FK child key definition. It217        ** is not necessary to check if it is part of a parent key, as parent218        ** key columns must be indexed. The check below will pick up this 219        ** case.  */220        FKey *pFKey;221        assert( IsOrdinaryTable(pTab) );222        for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){223          int j;224          for(j=0; j<pFKey->nCol; j++){225            if( pFKey->aCol[j].iFrom==iCol ){226              zFault = "foreign key";227            }228          }229        }230      }231#endif232      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){233        int j;234        for(j=0; j<pIdx->nKeyCol; j++){235          /* FIXME: Be smarter about indexes that use expressions */236          if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){237            zFault = "indexed";238          }239        }240      }241      if( zFault ){242        sqlite3DbFree(db, zErr);243        zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault);244        rc = SQLITE_ERROR;245        sqlite3BtreeLeaveAll(db);246        goto blob_open_out;247      }248    }249 250    pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);251    assert( pBlob->pStmt || db->mallocFailed );252    if( pBlob->pStmt ){253      254      /* This VDBE program seeks a btree cursor to the identified 255      ** db/table/row entry. The reason for using a vdbe program instead256      ** of writing code to use the b-tree layer directly is that the257      ** vdbe program will take advantage of the various transaction,258      ** locking and error handling infrastructure built into the vdbe.259      **260      ** After seeking the cursor, the vdbe executes an OP_ResultRow.261      ** Code external to the Vdbe then "borrows" the b-tree cursor and262      ** uses it to implement the blob_read(), blob_write() and 263      ** blob_bytes() functions.264      **265      ** The sqlite3_blob_close() function finalizes the vdbe program,266      ** which closes the b-tree cursor and (possibly) commits the 267      ** transaction.268      */269      static const int iLn = VDBE_OFFSET_LINENO(2);270      static const VdbeOpList openBlob[] = {271        {OP_TableLock,      0, 0, 0},  /* 0: Acquire a read or write lock */272        {OP_OpenRead,       0, 0, 0},  /* 1: Open a cursor */273        /* blobSeekToRow() will initialize r[1] to the desired rowid */274        {OP_NotExists,      0, 5, 1},  /* 2: Seek the cursor to rowid=r[1] */275        {OP_Column,         0, 0, 1},  /* 3  */276        {OP_ResultRow,      1, 0, 0},  /* 4  */277        {OP_Halt,           0, 0, 0},  /* 5  */278      };279      Vdbe *v = (Vdbe *)pBlob->pStmt;280      VdbeOp *aOp;281 282      sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, 283                           pTab->pSchema->schema_cookie,284                           pTab->pSchema->iGeneration);285      sqlite3VdbeChangeP5(v, 1);286      assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed );287      aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);288 289      /* Make sure a mutex is held on the table to be accessed */290      sqlite3VdbeUsesBtree(v, iDb); 291 292      if( db->mallocFailed==0 ){293        assert( aOp!=0 );294        /* Configure the OP_TableLock instruction */295#ifdef SQLITE_OMIT_SHARED_CACHE296        aOp[0].opcode = OP_Noop;297#else298        aOp[0].p1 = iDb;299        aOp[0].p2 = pTab->tnum;300        aOp[0].p3 = wrFlag;301        sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);302      }303      if( db->mallocFailed==0 ){304#endif305 306        /* Remove either the OP_OpenWrite or OpenRead. Set the P2 307        ** parameter of the other to pTab->tnum.  */308        if( wrFlag ) aOp[1].opcode = OP_OpenWrite;309        aOp[1].p2 = pTab->tnum;310        aOp[1].p3 = iDb;   311 312        /* Configure the number of columns. Configure the cursor to313        ** think that the table has one more column than it really314        ** does. An OP_Column to retrieve this imaginary column will315        ** always return an SQL NULL. This is useful because it means316        ** we can invoke OP_Column to fill in the vdbe cursors type 317        ** and offset cache without causing any IO.318        */319        aOp[1].p4type = P4_INT32;320        aOp[1].p4.i = pTab->nCol+1;321        aOp[3].p2 = pTab->nCol;322 323        sParse.nVar = 0;324        sParse.nMem = 1;325        sParse.nTab = 1;326        sqlite3VdbeMakeReady(v, &sParse);327      }328    }329   330    pBlob->iCol = iCol;331    pBlob->db = db;332    sqlite3BtreeLeaveAll(db);333    if( db->mallocFailed ){334      goto blob_open_out;335    }336    rc = blobSeekToRow(pBlob, iRow, &zErr);337    if( (++nAttempt)>=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break;338    sqlite3ParseObjectReset(&sParse);339  }340 341blob_open_out:342  if( rc==SQLITE_OK && db->mallocFailed==0 ){343    *ppBlob = (sqlite3_blob *)pBlob;344  }else{345    if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);346    sqlite3DbFree(db, pBlob);347  }348  sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);349  sqlite3DbFree(db, zErr);350  sqlite3ParseObjectReset(&sParse);351  rc = sqlite3ApiExit(db, rc);352  sqlite3_mutex_leave(db->mutex);353  return rc;354}355 356/*357** Close a blob handle that was previously created using358** sqlite3_blob_open().359*/360int sqlite3_blob_close(sqlite3_blob *pBlob){361  Incrblob *p = (Incrblob *)pBlob;362  int rc;363  sqlite3 *db;364 365  if( p ){366    sqlite3_stmt *pStmt = p->pStmt;367    db = p->db;368    sqlite3_mutex_enter(db->mutex);369    sqlite3DbFree(db, p);370    sqlite3_mutex_leave(db->mutex);371    rc = sqlite3_finalize(pStmt);372  }else{373    rc = SQLITE_OK;374  }375  return rc;376}377 378/*379** Perform a read or write operation on a blob380*/381static int blobReadWrite(382  sqlite3_blob *pBlob, 383  void *z, 384  int n, 385  int iOffset, 386  int (*xCall)(BtCursor*, u32, u32, void*)387){388  int rc = SQLITE_OK;389  Incrblob *p = (Incrblob *)pBlob;390  Vdbe *v;391  sqlite3 *db;392 393  if( p==0 ) return SQLITE_MISUSE_BKPT;394  db = p->db;395  sqlite3_mutex_enter(db->mutex);396  v = (Vdbe*)p->pStmt;397 398  if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){399    /* Request is out of range. Return a transient error. */400    rc = SQLITE_ERROR;401  }else if( v==0 ){402    /* If there is no statement handle, then the blob-handle has403    ** already been invalidated. Return SQLITE_ABORT in this case.404    */405    rc = SQLITE_ABORT;406  }else{407    /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is408    ** returned, clean-up the statement handle.409    */410    assert( db == v->db );411    sqlite3BtreeEnterCursor(p->pCsr);412 413#ifdef SQLITE_ENABLE_PREUPDATE_HOOK414    if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){415      /* If a pre-update hook is registered and this is a write cursor, 416      ** invoke it here. 417      ** 418      ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this419      ** operation should really be an SQLITE_UPDATE. This is probably420      ** incorrect, but is convenient because at this point the new.* values 421      ** are not easily obtainable. And for the sessions module, an 422      ** SQLITE_UPDATE where the PK columns do not change is handled in the 423      ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually424      ** slightly more efficient). Since you cannot write to a PK column425      ** using the incremental-blob API, this works. For the sessions module426      ** anyhow.427      */428      if( sqlite3BtreeCursorIsValidNN(p->pCsr)==0 ){429        /* If the cursor is not currently valid, try to reseek it. This 430        ** always either fails or finds the correct row - the cursor will431        ** have been marked permanently CURSOR_INVALID if the open row has432        ** been deleted.  */433        int bDiff = 0;434        rc = sqlite3BtreeCursorRestore(p->pCsr, &bDiff);435        assert( bDiff==0 || sqlite3BtreeCursorIsValidNN(p->pCsr)==0 );436      }437      if( sqlite3BtreeCursorIsValidNN(p->pCsr) ){438        sqlite3_int64 iKey;439        iKey = sqlite3BtreeIntegerKey(p->pCsr);440        assert( v->apCsr[0]!=0 );441        assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );442        sqlite3VdbePreUpdateHook(443            v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol444        );445      }446    }447    if( rc==SQLITE_OK ){448      rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);449    }450#else451    rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);452#endif453 454    sqlite3BtreeLeaveCursor(p->pCsr);455    if( rc==SQLITE_ABORT ){456      sqlite3VdbeFinalize(v);457      p->pStmt = 0;458    }else{459      v->rc = rc;460    }461  }462  sqlite3Error(db, rc);463  rc = sqlite3ApiExit(db, rc);464  sqlite3_mutex_leave(db->mutex);465  return rc;466}467 468/*469** Read data from a blob handle.470*/471int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){472  return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreePayloadChecked);473}474 475/*476** Write data to a blob handle.477*/478int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){479  return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData);480}481 482/*483** Query a blob handle for the size of the data.484**485** The Incrblob.nByte field is fixed for the lifetime of the Incrblob486** so no mutex is required for access.487*/488int sqlite3_blob_bytes(sqlite3_blob *pBlob){489  Incrblob *p = (Incrblob *)pBlob;490  return (p && p->pStmt) ? p->nByte : 0;491}492 493/*494** Move an existing blob handle to point to a different row of the same495** database table.496**497** If an error occurs, or if the specified row does not exist or does not498** contain a blob or text value, then an error code is returned and the499** database handle error code and message set. If this happens, then all 500** subsequent calls to sqlite3_blob_xxx() functions (except blob_close()) 501** immediately return SQLITE_ABORT.502*/503int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){504  int rc;505  Incrblob *p = (Incrblob *)pBlob;506  sqlite3 *db;507 508  if( p==0 ) return SQLITE_MISUSE_BKPT;509  db = p->db;510  sqlite3_mutex_enter(db->mutex);511 512  if( p->pStmt==0 ){513    /* If there is no statement handle, then the blob-handle has514    ** already been invalidated. Return SQLITE_ABORT in this case.515    */516    rc = SQLITE_ABORT;517  }else{518    char *zErr;519    ((Vdbe*)p->pStmt)->rc = SQLITE_OK;520    rc = blobSeekToRow(p, iRow, &zErr);521    if( rc!=SQLITE_OK ){522      sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);523      sqlite3DbFree(db, zErr);524    }525    assert( rc!=SQLITE_SCHEMA );526  }527 528  rc = sqlite3ApiExit(db, rc);529  assert( rc==SQLITE_OK || p->pStmt==0 );530  sqlite3_mutex_leave(db->mutex);531  return rc;532}533 534#endif /* #ifndef SQLITE_OMIT_INCRBLOB */535