/* ** Create a new phantom with the given UUID and return its artifact ID. */ int content_new(const char *zUuid, int isPrivate){ int rid; static Stmt s1, s2, s3; assert( g.repositoryOpen ); db_begin_transaction(); if( uuid_is_shunned(zUuid) ){ db_end_transaction(0); return 0; } db_static_prepare(&s1, "INSERT INTO blob(rcvid,size,uuid,content)" "VALUES(0,-1,:uuid,NULL)" ); db_bind_text(&s1, ":uuid", zUuid); db_exec(&s1); rid = db_last_insert_rowid(); db_static_prepare(&s2, "INSERT INTO phantom VALUES(:rid)" ); db_bind_int(&s2, ":rid", rid); db_exec(&s2); if( g.markPrivate || isPrivate ){ db_multi_exec("INSERT INTO private VALUES(%d)", rid); }else{ db_static_prepare(&s3, "INSERT INTO unclustered VALUES(:rid)" ); db_bind_int(&s3, ":rid", rid); db_exec(&s3); } bag_insert(&contentCache.missing, rid); db_end_transaction(0); return rid; }
/* ** Check to see if checkin "rid" is a leaf and either add it to the LEAF ** table if it is, or remove it if it is not. */ void leaf_check(int rid){ static Stmt checkIfLeaf; static Stmt addLeaf; static Stmt removeLeaf; int rc; db_static_prepare(&checkIfLeaf, "SELECT 1 FROM plink" " WHERE pid=:rid" " AND coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=:rid),'trunk')" " == coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=plink.cid),'trunk');", TAG_BRANCH, TAG_BRANCH ); db_bind_int(&checkIfLeaf, ":rid", rid); rc = db_step(&checkIfLeaf); db_reset(&checkIfLeaf); if( rc==SQLITE_ROW ){ db_static_prepare(&removeLeaf, "DELETE FROM leaf WHERE rid=:rid"); db_bind_int(&removeLeaf, ":rid", rid); db_step(&removeLeaf); db_reset(&removeLeaf); }else{ db_static_prepare(&addLeaf, "INSERT OR IGNORE INTO leaf VALUES(:rid)"); db_bind_int(&addLeaf, ":rid", rid); db_step(&addLeaf); db_reset(&addLeaf); } }
/* ** Check content that was received with rcvid and return true if any ** fork was created. */ int fossil_any_has_fork(int rcvid){ static Stmt q; int fForkSeen = 0; if( rcvid==0 ) return 0; db_static_prepare(&q, " SELECT pid FROM plink WHERE pid>0 AND isprim" " AND cid IN (SELECT blob.rid FROM blob" " WHERE rcvid=:rcvid)"); db_bind_int(&q, ":rcvid", rcvid); while( !fForkSeen && db_step(&q)==SQLITE_ROW ){ int pid = db_column_int(&q, 0); if( count_nonbranch_children(pid)>1 ){ compute_leaves(pid,1); if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ int rid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC LIMIT 1"); fForkSeen = fossil_find_nearest_fork(rid, db_open_local(0))!=0; } } } db_finalize(&q); return fForkSeen; }
/* ** Compute the "mtime" of the file given whose blob.rid is "fid" that ** is part of check-in "vid". The mtime will be the mtime on vid or ** some ancestor of vid where fid first appears. */ int mtime_of_manifest_file( int vid, /* The check-in that contains fid */ int fid, /* The id of the file whose check-in time is sought */ i64 *pMTime /* Write result here */ ){ static int prevVid = -1; static Stmt q; if( prevVid!=vid ){ prevVid = vid; db_multi_exec("DROP TABLE IF EXISTS temp.ok;" "CREATE TEMP TABLE ok(x INTEGER PRIMARY KEY);"); compute_ancestors(vid, 100000000, 1); } db_static_prepare(&q, "SELECT (max(event.mtime)-2440587.5)*86400 FROM mlink, event" " WHERE mlink.mid=event.objid" " AND +mlink.mid IN ok" " AND mlink.fid=:fid"); db_bind_int(&q, ":fid", fid); if( db_step(&q)!=SQLITE_ROW ){ db_reset(&q); return 1; } *pMTime = db_column_int64(&q, 0); db_reset(&q); return 0; }
/* This is a helper routine for test-artifacts. ** ** Check to see that artifact zUuid exists in the repository. If it does, ** return 0. If it does not, generate an error message and return 1. */ static int check_exists( const char *zUuid, /* The artifact we are checking for */ unsigned flags, /* Flags */ Manifest *p, /* The control artifact that references zUuid */ const char *zRole, /* Role of zUuid in p */ const char *zDetail /* Additional information, such as a filename */ ){ static Stmt q; int rc = 0; db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid"); if( zUuid==0 || zUuid[0]==0 ) return 0; db_bind_text(&q, ":uuid", zUuid); if( db_step(&q)==SQLITE_ROW ){ int size = db_column_int(&q, 0); if( size<0 ) rc = 2; }else{ rc = 1; } db_reset(&q); if( rc ){ const char *zCFType = "control artifact"; char *zSrc; char *zDate; char *zErrType = "MISSING"; if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ if( flags & MISSING_SHUNNED ) return 0; zErrType = "SHUNNED"; } switch( p->type ){ case CFTYPE_MANIFEST: zCFType = "check-in"; break; case CFTYPE_CLUSTER: zCFType = "cluster"; break; case CFTYPE_CONTROL: zCFType = "tag"; break; case CFTYPE_WIKI: zCFType = "wiki"; break; case CFTYPE_TICKET: zCFType = "ticket"; break; case CFTYPE_ATTACHMENT: zCFType = "attachment"; break; case CFTYPE_EVENT: zCFType = "event"; break; } zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid); if( p->rDate>0.0 ){ zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); }else{ zDate = db_text(0, "SELECT datetime(rcvfrom.mtime)" " FROM blob, rcvfrom" " WHERE blob.rcvid=rcvfrom.rcvid" " AND blob.rid=%d", p->rid); } fossil_print("%s: %s\n %s %s %S (%d) %s\n", zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); if( zDetail && zDetail[0] ){ fossil_print(" %s\n", zDetail); } fossil_free(zSrc); fossil_free(zDate); rc = 1; } return rc; }
/* ** Make sure an artifact is public. */ void content_make_public(int rid){ static Stmt s1; db_static_prepare(&s1, "DELETE FROM private WHERE rid=:rid" ); db_bind_int(&s1, ":rid", rid); db_exec(&s1); }
/* ** Return true if the given RID is marked as PRIVATE. */ int content_is_private(int rid){ static Stmt s1; int rc; db_static_prepare(&s1, "SELECT 1 FROM private WHERE rid=:rid" ); db_bind_int(&s1, ":rid", rid); rc = db_step(&s1); db_reset(&s1); return rc==SQLITE_ROW; }
/* ** Return the blob.size field given blob.rid */ int content_size(int rid, int dflt){ static Stmt q; int sz = dflt; db_static_prepare(&q, "SELECT size FROM blob WHERE rid=:r"); db_bind_int(&q, ":r", rid); if( db_step(&q)==SQLITE_ROW ){ sz = db_column_int(&q, 0); } db_reset(&q); return sz; }
/* ** Get the blob.content value for blob.rid=rid. Return 1 on success or ** 0 on failure. */ static int content_of_blob(int rid, Blob *pBlob){ static Stmt q; int rc = 0; db_static_prepare(&q, "SELECT content FROM blob WHERE rid=:rid AND size>=0"); db_bind_int(&q, ":rid", rid); if( db_step(&q)==SQLITE_ROW ){ db_ephemeral_blob(&q, 0, pBlob); blob_uncompress(pBlob, pBlob); rc = 1; } db_reset(&q); return rc; }
/* ** Return the srcid associated with rid. Or return 0 if rid is ** original content and not a delta. */ static int findSrcid(int rid){ static Stmt q; int srcid; db_static_prepare(&q, "SELECT srcid FROM delta WHERE rid=:rid"); db_bind_int(&q, ":rid", rid); if( db_step(&q)==SQLITE_ROW ){ srcid = db_column_int(&q, 0); }else{ srcid = 0; } db_reset(&q); return srcid; }
/* ** The input is guaranteed to be a 40-character well-formed UUID. ** Find its rid. */ int fast_uuid_to_rid(const char *zUuid){ static Stmt q; int rid; db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid"); db_bind_text(&q, ":uuid", zUuid); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); }else{ rid = 0; } db_reset(&q); return rid; }
/* ** Schedule a leaf check for "rid" and its parents. */ void leaf_eventually_check(int rid){ static Stmt parentsOf; db_static_prepare(&parentsOf, "SELECT pid FROM plink WHERE cid=:rid AND pid>0" ); db_bind_int(&parentsOf, ":rid", rid); bag_insert(&needToCheck, rid); while( db_step(&parentsOf)==SQLITE_ROW ){ bag_insert(&needToCheck, db_column_int(&parentsOf, 0)); } db_reset(&parentsOf); }
/* ** Count the number of primary non-branch children for the given check-in. ** ** A primary child is one where the parent is the primary parent, not ** a merge parent. A "leaf" is a node that has zero children of any ** kind. This routine counts only primary children. ** ** A non-branch child is one which is on the same branch as the parent. */ int count_nonbranch_children(int pid){ int nNonBranch = 0; static Stmt q; static const char zSql[] = @ SELECT count(*) FROM plink @ WHERE pid=:pid AND isprim @ AND coalesce((SELECT value FROM tagxref @ WHERE tagid=%d AND rid=plink.pid), 'trunk') @ =coalesce((SELECT value FROM tagxref @ WHERE tagid=%d AND rid=plink.cid), 'trunk') ; db_static_prepare(&q, zSql, TAG_BRANCH, TAG_BRANCH); db_bind_int(&q, ":pid", pid); if( db_step(&q)==SQLITE_ROW ){ nNonBranch = db_column_int(&q, 0); } db_reset(&q); return nNonBranch; }
/* ** Mark artifact rid as being available now. Update the cache to ** show that everything that was formerly unavailable because rid ** was missing is now available. */ static void content_mark_available(int rid){ Bag pending; static Stmt q; if( bag_find(&contentCache.available, rid) ) return; bag_init(&pending); bag_insert(&pending, rid); while( (rid = bag_first(&pending))!=0 ){ bag_remove(&pending, rid); bag_remove(&contentCache.missing, rid); bag_insert(&contentCache.available, rid); db_static_prepare(&q, "SELECT rid FROM delta WHERE srcid=:rid"); db_bind_int(&q, ":rid", rid); while( db_step(&q)==SQLITE_ROW ){ int nx = db_column_int(&q, 0); bag_insert(&pending, nx); } db_reset(&q); } bag_clear(&pending); }
/* ** Insert an artifact into the BLOB table if it isn't there already. ** If zMark is not zero, create a cross-reference from that mark back ** to the newly inserted artifact. ** ** If saveUuid is true, then pContent is a commit record. Record its ** UUID in gg.zPrevCheckin. */ static int fast_insert_content(Blob *pContent, const char *zMark, int saveUuid){ Blob hash; Blob cmpr; int rid; sha1sum_blob(pContent, &hash); rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &hash); if( rid==0 ){ static Stmt ins; db_static_prepare(&ins, "INSERT INTO blob(uuid, size, content) VALUES(:uuid, :size, :content)" ); db_bind_text(&ins, ":uuid", blob_str(&hash)); db_bind_int(&ins, ":size", gg.nData); blob_compress(pContent, &cmpr); db_bind_blob(&ins, ":content", &cmpr); db_step(&ins); db_reset(&ins); blob_reset(&cmpr); rid = db_last_insert_rowid(); } if( zMark ){ db_multi_exec( "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" "VALUES(%Q,%d,%B)", zMark, rid, &hash ); db_multi_exec( "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" "VALUES(%B,%d,%B)", &hash, rid, &hash ); } if( saveUuid ){ fossil_free(gg.zPrevCheckin); gg.zPrevCheckin = fossil_strdup(blob_str(&hash)); } blob_reset(&hash); return rid; }
/* ** Rebuild cross-referencing information for the artifact ** rid with content pBase and all of its descendants. This ** routine clears the content buffer before returning. ** ** If the zFNameFormat variable is set, then this routine is ** called to run "fossil deconstruct" instead of the usual ** "fossil rebuild". In that case, instead of rebuilding the ** cross-referencing information, write the file content out ** to the appropriate directory. ** ** In both cases, this routine automatically recurses to process ** other artifacts that are deltas off of the current artifact. ** This is the most efficient way to extract all of the original ** artifact content from the Fossil repository. */ static void rebuild_step(int rid, int size, Blob *pBase){ static Stmt q1; Bag children; Blob copy; Blob *pUse; int nChild, i, cid; while( rid>0 ){ /* Fix up the "blob.size" field if needed. */ if( size!=blob_size(pBase) ){ db_multi_exec( "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid ); } /* Find all children of artifact rid */ db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid"); db_bind_int(&q1, ":rid", rid); bag_init(&children); while( db_step(&q1)==SQLITE_ROW ){ int cid = db_column_int(&q1, 0); if( !bag_find(&bagDone, cid) ){ bag_insert(&children, cid); } } nChild = bag_count(&children); db_reset(&q1); /* Crosslink the artifact */ if( nChild==0 ){ pUse = pBase; }else{ blob_copy(©, pBase); pUse = © } if( zFNameFormat==0 ){ /* We are doing "fossil rebuild" */ manifest_crosslink(rid, pUse, MC_NONE); }else{ /* We are doing "fossil deconstruct" */ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); blob_write_to_file(pUse,zFile); free(zFile); free(zUuid); blob_reset(pUse); } assert( blob_is_reset(pUse) ); rebuild_step_done(rid); /* Call all children recursively */ rid = 0; for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ static Stmt q2; int sz; db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid"); db_bind_int(&q2, ":rid", cid); if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ Blob delta, next; db_ephemeral_blob(&q2, 0, &delta); blob_uncompress(&delta, &delta); blob_delta_apply(pBase, &delta, &next); blob_reset(&delta); db_reset(&q2); if( i<nChild ){ rebuild_step(cid, sz, &next); }else{ /* Tail recursion */ rid = cid; size = sz; blob_reset(pBase); *pBase = next; } }else{ db_reset(&q2); blob_reset(pBase); } } bag_clear(&children); } }
/* ** Output a "committer" record for the given user. */ static void print_person(const char *zUser){ static Stmt q; const char *zContact; char *zName; char *zEmail; int i, j; if( zUser==0 ){ printf(" <unknown>"); return; } db_static_prepare(&q, "SELECT info FROM user WHERE login=:user"); db_bind_text(&q, ":user", zUser); if( db_step(&q)!=SQLITE_ROW ){ db_reset(&q); for(i=0; zUser[i] && zUser[i]!='>' && zUser[i]!='<'; i++){} if( zUser[i]==0 ){ printf(" %s <%s>", zUser, zUser); return; } zName = mprintf("%s", zUser); for(i=j=0; zName[i]; i++){ if( zName[i]!='<' && zName[i]!='>' ){ zName[j++] = zName[i]; } } zName[j] = 0; printf(" %s <%s>", zName, zUser); free(zName); return; } /* ** We have contact information. ** It may or may not contain an email address. */ zContact = db_column_text(&q, 0); for(i=0; zContact[i] && zContact[i]!='>' && zContact[i]!='<'; i++){} if( zContact[i]==0 ){ /* No email address found. Take as user info if not empty */ printf(" %s <%s>", zContact[0] ? zContact : zUser, zUser); db_reset(&q); return; } if( zContact[i]=='<' ){ /* ** Found beginning of email address. Look for the end and extract ** the part. */ zEmail = mprintf("%s", &zContact[i]); for(i=0; zEmail[i] && zEmail[i]!='>'; i++){} if( zEmail[i]=='>' ) zEmail[i+1] = 0; }else{ /* ** Found an end marker for email, but nothing else. */ zEmail = mprintf("<%s>", zUser); } /* ** Here zContact[i] either '<' or '>'. Extract the string _before_ ** either as user name. */ zName = mprintf("%.*s", i-1, zContact); for(i=j=0; zName[i]; i++){ if( zName[i]!='"' ) zName[j++] = zName[i]; } zName[j] = 0; printf(" %s %s", zName, zEmail); free(zName); free(zEmail); db_reset(&q); }