描述
此函数打开位于数据库深发展中qRow行szColumn,表szTable中的BLOB的句柄;换句话说,相同的BLOB将被选择:
SELECT szColumn FROM szDb.szTable WHERE rowid = qRow;
C / C ++语法
int sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); |
PB语法
FUNCTION sqlite3_blob_open ( _ BYVAL hDbc AS DWORD, _ BYREF szDb AS ASCIIZ, _ BYREF szTable AS ASCIIZ, _ BYREF szColumn AS ASCIIZ, _ BYVAL qRow AS QUAD, _ BYVAL flags AS LONG, _ BYREF ppBlob AS DWORD _ ) AS LONG |
参数
pDb
[in]??数据库连接句柄。必须是从sqlite3_open,sqlite3_open16或sqlite3_open_v2获取的NULL指针或sqlite3对象指针,而不是先前关闭。使用NULL指针参数调用sqlite3_close是无害的操作。
szDb
[in]数据库名称。请注意,数据库名称不是包含数据库的文件名,而是使用ATTACH连接数据库时AS关键字后出现的数据库的符号名称。对于主数据库文件,数据库名称为“main”。对于TEMP表,数据库名称为“temp”。
szTable
[in]表名。
szColumn
[in]列名称。
qRow
[in]行号。如果BLOB句柄指向的行由UPDATE,DELETE或ON CONFLICT副作用修改,则BLOB句柄被标记为“已过期”。如果行的任何列都被更改,即使是除了BLOB句柄打开的列之外的列也是如此。调用sqlite3_blob_read和sqlite3_blob_write过期的BLOB句柄失败,返回代码为SQLITE_ABORT。在BLOB到期之前写入BLOB的更改在BLOB到期之前不会回滚。如果交易继续完成,这些更改将终止。
flags
[in]如果flags参数不为零,则打开BLOB进行读写访问。如果为零,则BLOB被打开以进行读取访问。不可能打开作为索引或主键的一部分进行写入的列。如果启用外键约束,则无法打开作为子键的一部分进行写入的列。
ppBlob
[out]指向接收de blob指针的变量的指针。
返回值
成功后,返回SQLITE_OK,并将新的BLOB句柄写入ppBlob.否则返回错误代码,ppBlob设置为空指针。此函数设置数据库连接错误代码和可通过sqlite3_errcode和sqlite3_errmsg和相关功能访问的消息。请注意,ppBlob变量始终被初始化为使ppBlobsqlite3_blob_close调用sqlite3_blob_close的安全性无论此函数的成功或失败如何。
备注
使用sqlite3_blob_bytes函数确定打开的Blob的大小。blob的大小可能不会被此函数更改。使用UPDATE SQL命令更改blob的大小。
如果需要,可以使用sqlite3_bind_zeroblob和sqlite3_result_zeroblob函数和内置的zeroblob SQL函数来创建一个空的零填充blob,使用此函数读取或写入。
为了避免资源泄漏,最终将通过调用sqlite3_blob_close释放每个开放的BLOB句柄。
C ++实现代码
/*
** Open a blob handle.
*/
SQLITE_API int sqlite3_blob_open(
sqlite3* db, /* The database connection */
const char *zDb, /* The attached database containing the blob */
const char *zTable, /* The table containing the blob */
const char *zColumn, /* The column containing the blob */
sqlite_int64 iRow, /* The row containing the glob */
int flags, /* True -> read/write access, false -> read-only */
sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */
){
int nAttempt = 0;
int iCol; /* Index of zColumn in row-record */
/* This VDBE program seeks a btree cursor to the identified
** db/table/row entry. The reason for using a vdbe program instead
** of writing code to use the b-tree layer directly is that the
** vdbe program will take advantage of the various transaction,
** locking and error handling infrastructure built into the vdbe.
**
** After seeking the cursor, the vdbe executes an OP_ResultRow.
** Code external to the Vdbe then "borrows" the b-tree cursor and
** uses it to implement the blob_read(), blob_write() and
** blob_bytes() functions.
**
** The sqlite3_blob_close() function finalizes the vdbe program,
** which closes the b-tree cursor and (possibly) commits the
** transaction.
*/
static const VdbeOpList openBlob[] = {
{OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
{OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */
{OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */
/* One of the following two instructions is replaced by an OP_Noop. */
{OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
{OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
{OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */
{OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */
{OP_Column, 0, 0, 1}, /* 7 */
{OP_ResultRow, 1, 0, 0}, /* 8 */
{OP_Goto, 0, 5, 0}, /* 9 */
{OP_Close, 0, 0, 0}, /* 10 */
{OP_Halt, 0, 0, 0}, /* 11 */
};
int rc = SQLITE_OK;
char *zErr = 0;
Table *pTab;
Parse *pParse = 0;
Incrblob *pBlob = 0;
flags = !!flags; /* flags = (flags ? 1 : 0); */
*ppBlob = 0;
sqlite3_mutex_enter(db->mutex);
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
if( !pBlob ) goto blob_open_out;
pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
if( !pParse ) goto blob_open_out;
do {
memset(pParse, 0, sizeof(Parse));
pParse->db = db;
sqlite3DbFree(db, zErr);
zErr = 0;
sqlite3BtreeEnterAll(db);
pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
if( pTab && IsVirtual(pTab) ){
pTab = 0;
sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
}
#ifndef SQLITE_OMIT_VIEW
if( pTab && pTab->pSelect ){
pTab = 0;
sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable);
}
#endif
if( !pTab ){
if( pParse->zErrMsg ){
sqlite3DbFree(db, zErr);
zErr = pParse->zErrMsg;
pParse->zErrMsg = 0;
}
rc = SQLITE_ERROR;
sqlite3BtreeLeaveAll(db);
goto blob_open_out;
}
/* Now search pTab for the exact column. */
for(iCol=0; iCol<pTab->nCol; iCol++) {
if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
break;
}
}
if( iCol==pTab->nCol ){
sqlite3DbFree(db, zErr);
zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);
rc = SQLITE_ERROR;
sqlite3BtreeLeaveAll(db);
goto blob_open_out;
}
/* If the value is being opened for writing, check that the
** column is not indexed, and that it is not part of a foreign key.
** It is against the rules to open a column to which either of these
** descriptions applies for writing. */
if( flags ){
const char *zFault = 0;
Index *pIdx;
#ifndef SQLITE_OMIT_FOREIGN_KEY
if( db->flags&SQLITE_ForeignKeys ){
/* Check that the column is not part of an FK child key definition. It
** is not necessary to check if it is part of a parent key, as parent
** key columns must be indexed. The check below will pick up this
** case. */
FKey *pFKey;
for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
int j;
for(j=0; j<pFKey->nCol; j++){
if( pFKey->aCol[j].iFrom==iCol ){
zFault = "foreign key";
}
}
}
}
#endif
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int j;
for(j=0; j<pIdx->nColumn; j++){
if( pIdx->aiColumn[j]==iCol ){
zFault = "indexed";
}
}
}
if( zFault ){
sqlite3DbFree(db, zErr);
zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault);
rc = SQLITE_ERROR;
sqlite3BtreeLeaveAll(db);
goto blob_open_out;
}
}
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db);
assert( pBlob->pStmt || db->mallocFailed );
if( pBlob->pStmt ){
Vdbe *v = (Vdbe *)pBlob->pStmt;
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
/* Configure the OP_Transaction */
sqlite3VdbeChangeP1(v, 0, iDb);
sqlite3VdbeChangeP2(v, 0, flags);
/* Configure the OP_VerifyCookie */
sqlite3VdbeChangeP1(v, 1, iDb);
sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration);
/* Make sure a mutex is held on the table to be accessed */
sqlite3VdbeUsesBtree(v, iDb);
/* Configure the OP_TableLock instruction */
#ifdef SQLITE_OMIT_SHARED_CACHE
sqlite3VdbeChangeToNoop(v, 2);
#else
sqlite3VdbeChangeP1(v, 2, iDb);
sqlite3VdbeChangeP2(v, 2, pTab->tnum);
sqlite3VdbeChangeP3(v, 2, flags);
sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
#endif
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
** parameter of the other to pTab->tnum. */
sqlite3VdbeChangeToNoop(v, 4 - flags);
sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
sqlite3VdbeChangeP3(v, 3 + flags, iDb);
/* Configure the number of columns. Configure the cursor to
** think that the table has one more column than it really
** does. An OP_Column to retrieve this imaginary column will
** always return an SQL NULL. This is useful because it means
** we can invoke OP_Column to fill in the vdbe cursors type
** and offset cache without causing any IO.
*/
sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
sqlite3VdbeChangeP2(v, 7, pTab->nCol);
if( !db->mallocFailed ){
pParse->nVar = 1;
pParse->nMem = 1;
pParse->nTab = 1;
sqlite3VdbeMakeReady(v, pParse);
}
}
pBlob->flags = flags;
pBlob->iCol = iCol;
pBlob->db = db;
sqlite3BtreeLeaveAll(db);
if( db->mallocFailed ){
goto blob_open_out;
}
sqlite3_bind_int64(pBlob->pStmt, 1, iRow);
rc = blobSeekToRow(pBlob, iRow, &zErr);
} while( (++nAttempt)<5 && rc==SQLITE_SCHEMA );
blob_open_out:
if( rc==SQLITE_OK && db->mallocFailed==0 ){
*ppBlob = (sqlite3_blob *)pBlob;
}else{
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
sqlite3DbFree(db, pBlob);
}
sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
sqlite3StackFree(db, pParse);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}