# # # patch "cmd_files.cc" # from [e51fa86b305fd51c79fac69f57e31e54810efc1e] # to [2edcbe2df9fb1890c646a386be837879315f0995] # # patch "database.cc" # from [3a221934a73d25f35c980a3f857b1cadde18d70f] # to [f52aceca72edd2079f6203ae0ad016d5cf5891fb] # # patch "database.hh" # from [62ca0ac4e28df39f316de9117fc10a171c73f1a5] # to [47695146fe0bf270c37e3e4646efd6d6b78abef0] # # patch "database_check.cc" # from [beb0e60421b8bf7552071a9af31ef6630cfd3b51] # to [ae542ecb0c5c74e73bc900a9307de9ceaaffc5a0] # # patch "migrate_ancestry.cc" # from [2b91880e4558527585bccbba0a85705f6fda6304] # to [351c11cd194241e30554fa81c2f4b7425e76daf2] # # patch "migrate_schema.cc" # from [ce591f0adeb455c607c3b5a934aa7a7179c15dc6] # to [71649cdd46641a9c63c8b6dfec3a02af6a426541] # # patch "schema.sql" # from [956c61eca12f587d85043622d852a4d85c72a80b] # to [e95592782a6420be6e3a0e20f8d053147de38d70] # # patch "vocab.hh" # from [17ac4c8f007f5b46d0c34f5140942ac8c9f8ae75] # to [c5ab0a9b34f8c7a2f23a00b9782e25542ea90083] # ============================================================ --- database.cc 3a221934a73d25f35c980a3f857b1cadde18d70f +++ database.cc f52aceca72edd2079f6203ae0ad016d5cf5891fb @@ -113,9 +113,10 @@ namespace { struct query_param { - enum arg_type { text, blob }; + enum arg_type { text, blob, int64 }; arg_type type; - string data; + string string_data; + u64 int_data; }; query_param @@ -130,6 +131,7 @@ namespace query_param q = { query_param::text, txt, + 0, }; return q; } @@ -140,10 +142,22 @@ namespace query_param q = { query_param::blob, blb, + 0, }; return q; } + query_param + int64(u64 const & num) + { + query_param q = { + query_param::int64, + "", + num, + }; + return q; + } + struct query { explicit query(string const & cmd) @@ -336,12 +350,20 @@ private: string const & data_table, string const & delta_table); + void get_version_size(id const & ident, + file_size & size, + string const & size_table); + void drop(id const & base, string const & table); + void put_file_delta(file_id const & ident, file_id const & base, file_delta const & del); + void put_file_size(file_id const & ident, + file_data const & data); + void put_roster_delta(revision_id const & ident, revision_id const & base, roster_delta const & del); @@ -617,7 +639,9 @@ database_impl::check_caches() { if (table_has_data("revisions")) { - E(table_has_data("rosters") && table_has_data("heights"), + E(table_has_data("rosters") + && table_has_data("heights") + && table_has_data("file_sizes"), origin::no_fault, F("database %s lacks some cached data\n" "run '%s db regenerate_caches' to restore use of this database") @@ -995,6 +1019,7 @@ database::info(ostream & out, bool analy counts.push_back(imp->count("roster_deltas")); counts.push_back(imp->count("files")); counts.push_back(imp->count("file_deltas")); + counts.push_back(imp->count("file_sizes")); counts.push_back(imp->count("revisions")); counts.push_back(imp->count("revision_ancestry")); counts.push_back(imp->count("revision_certs")); @@ -1032,6 +1057,8 @@ database::info(ostream & out, bool analy bytes.push_back(imp->space("files", "length(id) + length(data)", total)); bytes.push_back(imp->space("file_deltas", "length(id) + length(base) + length(delta)", total)); + bytes.push_back(imp->space("file_sizes", + "length(id) + length(size)", total)); bytes.push_back(imp->space("revisions", "length(id) + length(data)", total)); bytes.push_back(imp->space("revision_ancestry", "length(parent) + length(child)", total)); @@ -1067,6 +1094,7 @@ database::info(ostream & out, bool analy " roster deltas : %s\n" " full files : %s\n" " file deltas : %s\n" + " file sizes : %s\n" " revisions : %s\n" " ancestry edges : %s\n" " certs : %s\n" @@ -1076,6 +1104,7 @@ database::info(ostream & out, bool analy " roster deltas : %s\n" " full files : %s\n" " file deltas : %s\n" + " file sizes : %s\n" " revisions : %s\n" " cached ancestry : %s\n" " certs : %s\n" @@ -1444,13 +1473,21 @@ database_impl::fetch(results & res, if (global_sanity.debug_p()) { string prefix; - string log(query.args[param-1].data); + string log; if (query.args[param-1].type == query_param::blob) { prefix = "x"; - log = encode_hexenc(log, origin::internal); + log = encode_hexenc(query.args[param-1].string_data, origin::internal); } + else if (query.args[param-1].type == query_param::int64) + { + log = lexical_cast(query.args[param-1].int_data); + } + else + { + log = query.args[param-1].string_data; + } if (log.size() > constants::db_log_line_sz) log = log.substr(0, constants::db_log_line_sz - 2) + ".."; @@ -1462,17 +1499,23 @@ database_impl::fetch(results & res, { case query_param::text: sqlite3_bind_text(i->second.stmt(), param, - idx(query.args, param - 1).data.c_str(), -1, + idx(query.args, param - 1).string_data.c_str(), -1, SQLITE_STATIC); break; case query_param::blob: { - string const & data = idx(query.args, param - 1).data; + string const & data = idx(query.args, param - 1).string_data; sqlite3_bind_blob(i->second.stmt(), param, data.data(), data.size(), SQLITE_STATIC); } break; + case query_param::int64: + { + u64 data = idx(query.args, param - 1).int_data; + sqlite3_bind_int64(i->second.stmt(), param, data); + } + break; default: I(false); } @@ -1591,7 +1634,7 @@ database_impl::schedule_delayed_file(fil void database_impl::schedule_delayed_file(file_id const & an_id, - file_data const & dat) + file_data const & dat) { if (!have_delayed_file(an_id)) { @@ -1934,6 +1977,16 @@ void } void +database_impl::put_file_size(file_id const & ident, + file_data const & data) +{ + I(!null_id(ident)); + file_size size = data.inner()().size(); + query q("INSERT INTO file_sizes(id, size) VALUES (?, ?)"); + execute(q % blob(ident.inner()()) % int64(size)); +} + +void database_impl::put_roster_delta(revision_id const & ident, revision_id const & base, roster_delta const & del) @@ -2044,6 +2097,18 @@ database_impl::get_version(id const & id vcache.insert_clean(ident, dat); } +void +database_impl::get_version_size(id const & ident, + file_size & size, + std::string const & size_table) +{ + results res; + query q("SELECT size FROM " + size_table + " WHERE id = ?"); + fetch(res, one_col, one_row, q % blob(ident())); + I(!res.empty()); + size = lexical_cast(res[0][0]); +} + struct roster_reconstruction_graph : public reconstruction_graph { database_impl & imp; @@ -2339,6 +2404,13 @@ void } void +database::get_file_size(file_id const & id, + file_size & size) +{ + imp->get_version_size(id.inner(), size, "file_sizes"); +} + +void database::get_manifest_version(manifest_id const & id, manifest_data & dat) { @@ -2352,9 +2424,12 @@ database::put_file(file_id const & id, file_data const & dat) { if (file_version_exists(id)) - L(FL("file version '%s' already exists in db") % id); - else - imp->schedule_delayed_file(id, dat); + { + L(FL("file version '%s' already exists in db") % id); + return; + } + imp->schedule_delayed_file(id, dat); + imp->put_file_size(id, dat); } void @@ -2415,6 +2490,7 @@ database::put_file_version(file_id const if (!file_or_manifest_base_exists(new_id, "files")) { imp->schedule_delayed_file(new_id, new_data); + imp->put_file_size(new_id, new_data); } if (!imp->delta_exists(old_id, new_id, "file_deltas")) { @@ -2423,6 +2499,10 @@ database::put_file_version(file_id const } if (make_forward_deltas) { + if (!file_or_manifest_base_exists(new_id, "files")) + { + imp->put_file_size(new_id, new_data); + } if (!imp->delta_exists(new_id, old_id, "file_deltas")) { put_file_delta(new_id, old_id, del); @@ -3039,6 +3119,12 @@ database::delete_existing_branch_leaves( imp->execute(query("DELETE FROM branch_leaves")); } +void +database::delete_existing_file_sizes() +{ + imp->execute(query("DELETE FROM file_sizes")); +} + /// Deletes one revision from the local database. /// @see kill_rev_locally void ============================================================ --- database.hh 62ca0ac4e28df39f316de9117fc10a171c73f1a5 +++ database.hh 47695146fe0bf270c37e3e4646efd6d6b78abef0 @@ -129,6 +129,10 @@ public: void get_file_version(file_id const & ident, file_data & dat); + // gets the (cached) size of the file if it exists + void get_file_size(file_id const & ident, + file_size & size); + // put file w/o predecessor into db void put_file(file_id const & new_id, file_data const & dat); @@ -328,6 +332,7 @@ public: void compute_branch_leaves(cert_value const & branch_name, std::set & revs); void recalc_branch_leaves(cert_value const & branch_name); void delete_existing_branch_leaves(); + void delete_existing_file_sizes(); // Used through project.cc outdated_indicator get_revision_certs(revision_id const & ident, ============================================================ --- schema.sql 956c61eca12f587d85043622d852a4d85c72a80b +++ schema.sql e95592782a6420be6e3a0e20f8d053147de38d70 @@ -37,6 +37,12 @@ CREATE TABLE file_deltas unique(id, base) ); +CREATE TABLE file_sizes + ( + id primary key, -- joins with files.id or file_deltas.id + size not null -- the size of the file in byte + ); + CREATE TABLE revisions ( id primary key, -- SHA1(text of revision) ============================================================ --- vocab.hh 17ac4c8f007f5b46d0c34f5140942ac8c9f8ae75 +++ vocab.hh c5ab0a9b34f8c7a2f23a00b9782e25542ea90083 @@ -90,6 +90,8 @@ typedef file< delta > file_delt typedef manifest< delta > manifest_delta; typedef file< delta > file_delta; +typedef u64 file_size; + // diff type; this is here and not diff_patch.hh, because options_list.hh // needs to refer to it enum diff_type ============================================================ --- migrate_schema.cc ce591f0adeb455c607c3b5a934aa7a7179c15dc6 +++ migrate_schema.cc 71649cdd46641a9c63c8b6dfec3a02af6a426541 @@ -676,7 +676,7 @@ char const migrate_add_heights[] = ; // this is a function because it has to refer to the numeric constant -// defined in schema_migration.hh. +// defined in migration.hh. static void migrate_add_ccode(sqlite3 * db, key_store &) { @@ -716,7 +716,7 @@ char const migrate_to_binary_hashes[] = "DROP TABLE tmp;" "CREATE INDEX revision_certs__id ON revision_certs (id);" - // We altered a comment on this table, thus we need to recreated it. + // We altered a comment on this table, thus we need to recreate it. // Additionally, this is the only schema change, so that we get another // schema hash to upgrade to. "ALTER TABLE branch_epochs RENAME TO tmp;" @@ -863,7 +863,14 @@ migrate_add_branch_leaf_cache(sqlite3 * } } +char const migrate_add_file_sizes[] = + "CREATE TABLE file_sizes\n" + " (\n" + " id primary key, -- joins with files.id or file_deltas.id\n" + " size not null -- the size of the file in byte\n" + " );"; + // these must be listed in order so that ones listed earlier override ones // listed later enum upgrade_regime @@ -950,9 +957,13 @@ const migration_event migration_events[] { "9c8d5a9ea8e29c69be6459300982a68321b0ec12", 0, migrate_add_branch_leaf_cache, upgrade_none }, + + { "0c956abae3e52522e4e0b7c5cbe7868f5047153e", + migrate_add_file_sizes, 0, upgrade_regen_caches }, + // The last entry in this table should always be the current // schema ID, with 0 for the migrators. - { "0c956abae3e52522e4e0b7c5cbe7868f5047153e", 0, 0, upgrade_none } + { "1f60cec1b0f6c8c095dc6d0ffeff2bd0af971ce1", 0, 0, upgrade_none } }; const size_t n_migration_events = (sizeof migration_events / sizeof migration_events[0]); @@ -1029,6 +1040,7 @@ calculate_schema_id(sqlite3 * db, string id tid; calculate_ident(data(schema, origin::database), tid); ident = encode_hexenc(tid(), tid.made_from); + L(FL("calculated schema id %s") % ident); } // Look through the migration_events table and return a pointer to the entry ============================================================ --- database_check.cc beb0e60421b8bf7552071a9af31ef6630cfd3b51 +++ database_check.cc ae542ecb0c5c74e73bc900a9307de9ceaaffc5a0 @@ -68,9 +68,10 @@ struct checked_file { struct checked_file { bool found; // found in db, retrieved and verified sha1 hash + bool size_ok; // recorded file size is correct size_t roster_refs; // number of roster references to this file - checked_file(): found(false), roster_refs(0) {} + checked_file(): found(false), size_ok(false), roster_refs(0) {} }; struct checked_roster { @@ -170,6 +171,11 @@ check_files(database & db, map 0 +// +// Error conditions: If the file id specified is unknown or invalid prints +// an error message to stderr and exits with status 1. +CMD_AUTOMATE(get_file_size, N_("FILEID"), + N_("Prints the size of a file (given an identifier)"), + "", + options::opts::none) +{ + E(args.size() == 1, origin::user, + F("wrong argument count")); + + database db(app); + hexenc hident(idx(args, 0)(), origin::user); + file_id ident(decode_hexenc_as(hident(), hident.made_from)); + file_size size; + db.get_file_size(ident, size); + output << lexical_cast(size) << "\n"; +} + // Name: get_file_of // Arguments: // 1: a filename ============================================================ --- migrate_ancestry.cc 2b91880e4558527585bccbba0a85705f6fda6304 +++ migrate_ancestry.cc 351c11cd194241e30554fa81c2f4b7425e76daf2 @@ -993,6 +993,7 @@ regenerate_caches(database & db) db.delete_existing_rosters(); db.delete_existing_heights(); + db.delete_existing_file_sizes(); vector sorted_ids; allrevs_toposorted(db, sorted_ids);