/*
** 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 <assert.h>
/*
** 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; i<pIdxInfo->nConstraint; 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;
}