/* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This routine implements eponymous virtual table for SQLite that gives ** all of the files associated with a single check-in. The table works ** as a table-valued function. ** ** The source code filename "foci" is short for "Files of Check-in". ** ** Usage example: ** ** SELECT * FROM files_of_checkin('trunk'); ** ** The "schema" for the temp.foci table is: ** ** CREATE TABLE files_of_checkin( ** checkinID INTEGER, -- RID for the check-in manifest ** filename TEXT, -- Name of a file ** uuid TEXT, -- hash of the file ** previousName TEXT, -- Name of the file in previous check-in ** perm TEXT, -- Permissions on the file ** symname TEXT HIDDEN -- Symbolic name of the check-in. ** ); ** ** The hidden symname column is (optionally) used as a query parameter to ** identify the particular check-in to parse. The checkinID parameter ** (such is a unique numeric RID rather than symbolic name) can also be used ** to identify the check-in. Example: ** ** SELECT * FROM files_of_checkin ** WHERE checkinID=symbolic_name_to_rid('trunk'); ** */ #include "config.h" #include "foci.h" #include /* ** The schema for the virtual table: */ static const char zFociSchema[] = @ CREATE TABLE files_of_checkin( @ checkinID INTEGER, -- RID for the check-in manifest @ filename TEXT, -- Name of a file @ uuid TEXT, -- hash of the file @ previousName TEXT, -- Name of the file in previous check-in @ perm TEXT, -- Permissions on the file @ symname TEXT HIDDEN -- Symbolic name of the check-in @ ); ; #define FOCI_CHECKINID 0 #define FOCI_FILENAME 1 #define FOCI_UUID 2 #define FOCI_PREVNAME 3 #define FOCI_PERM 4 #define FOCI_SYMNAME 5 #if INTERFACE /* ** The subclasses of sqlite3_vtab and sqlite3_vtab_cursor tables ** that implement the files_of_checkin virtual table. */ struct FociTable { sqlite3_vtab base; /* Base class - must be first */ }; struct FociCursor { sqlite3_vtab_cursor base; /* Base class - must be first */ Manifest *pMan; /* Current manifest */ ManifestFile *pFile; /* Current file */ int iFile; /* File index */ }; #endif /* INTERFACE */ /* ** Connect to or create a foci virtual table. */ static int fociConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ FociTable *pTab; pTab = (FociTable *)sqlite3_malloc(sizeof(FociTable)); memset(pTab, 0, sizeof(FociTable)); sqlite3_declare_vtab(db, zFociSchema); *ppVtab = &pTab->base; return SQLITE_OK; } /* ** Disconnect from or destroy a focivfs virtual table. */ static int fociDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** Available scan methods: ** ** (0) A full scan. Visit every manifest in the repo. (Slow) ** (1) checkinID=?. visit only the single manifest specified. ** (2) symName=? visit only the single manifest specified. */ static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int i; pIdxInfo->estimatedCost = 1000000000.0; for(i=0; inConstraint; i++){ if( !pIdxInfo->aConstraint[i].usable ) continue; if( pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ && (pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID || pIdxInfo->aConstraint[i].iColumn==FOCI_SYMNAME) ){ if( pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID ){ pIdxInfo->idxNum = 1; }else{ pIdxInfo->idxNum = 2; } pIdxInfo->estimatedCost = 1.0; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; break; } } return SQLITE_OK; } /* ** Open a new focivfs cursor. */ static int fociOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ FociCursor *pCsr; pCsr = (FociCursor *)sqlite3_malloc(sizeof(FociCursor)); memset(pCsr, 0, sizeof(FociCursor)); pCsr->base.pVtab = pVTab; *ppCursor = (sqlite3_vtab_cursor *)pCsr; return SQLITE_OK; } /* ** Close a focivfs cursor. */ static int fociClose(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; manifest_destroy(pCsr->pMan); sqlite3_free(pCsr); return SQLITE_OK; } /* ** Move a focivfs cursor to the next entry in the file. */ static int fociNext(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; pCsr->pFile = manifest_file_next(pCsr->pMan, 0); pCsr->iFile++; return SQLITE_OK; } static int fociEof(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; return pCsr->pFile==0; } static int fociFilter( sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ FociCursor *pCur = (FociCursor *)pCursor; manifest_destroy(pCur->pMan); if( idxNum ){ int rid; if( idxNum==1 ){ rid = sqlite3_value_int(argv[0]); }else{ rid = symbolic_name_to_rid((const char*)sqlite3_value_text(argv[0]),"ci"); } pCur->pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pCur->pMan ){ manifest_file_rewind(pCur->pMan); pCur->pFile = manifest_file_next(pCur->pMan, 0); } }else{ pCur->pMan = 0; } pCur->iFile = 0; return SQLITE_OK; } static int fociColumn( sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i ){ FociCursor *pCsr = (FociCursor *)pCursor; switch( i ){ case FOCI_CHECKINID: sqlite3_result_int(ctx, pCsr->pMan->rid); break; case FOCI_FILENAME: sqlite3_result_text(ctx, pCsr->pFile->zName, -1, SQLITE_TRANSIENT); break; case FOCI_UUID: sqlite3_result_text(ctx, pCsr->pFile->zUuid, -1, SQLITE_TRANSIENT); break; case FOCI_PREVNAME: sqlite3_result_text(ctx, pCsr->pFile->zPrior, -1, SQLITE_TRANSIENT); break; case FOCI_PERM: sqlite3_result_text(ctx, pCsr->pFile->zPerm, -1, SQLITE_TRANSIENT); break; case FOCI_SYMNAME: break; } return SQLITE_OK; } static int fociRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ FociCursor *pCsr = (FociCursor *)pCursor; *pRowid = pCsr->iFile; return SQLITE_OK; } int foci_register(sqlite3 *db){ static sqlite3_module foci_module = { 0, /* iVersion */ fociConnect, /* xCreate */ fociConnect, /* xConnect */ fociBestIndex, /* xBestIndex */ fociDisconnect, /* xDisconnect */ fociDisconnect, /* xDestroy */ fociOpen, /* xOpen - open a cursor */ fociClose, /* xClose - close a cursor */ fociFilter, /* xFilter - configure scan constraints */ fociNext, /* xNext - advance a cursor */ fociEof, /* xEof - check for end of scan */ fociColumn, /* xColumn - read data */ fociRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0 /* xRollbackTo */ }; sqlite3_create_module(db, "files_of_checkin", &foci_module, 0); return SQLITE_OK; }