summaryrefslogtreecommitdiff
path: root/src/foci.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/foci.c')
-rw-r--r--src/foci.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/src/foci.c b/src/foci.c
new file mode 100644
index 0000000..20fea47
--- /dev/null
+++ b/src/foci.c
@@ -0,0 +1,278 @@
+/*
+** 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, /* xFindFunction */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
+ };
+ sqlite3_create_module(db, "files_of_checkin", &foci_module, 0);
+ return SQLITE_OK;
+}