Downgrade warning to a trace in autoload-db.cpp
[hiphop-php.git] / hphp / runtime / ext / facts / autoload-db.cpp
blob009b69fc81b73774e8b624026d57c22880953dc8
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source path is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the path LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include <atomic>
18 #include <chrono>
19 #include <exception>
20 #include <fcntl.h>
21 #include <grp.h>
22 #include <string>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <utility>
27 #include <folly/experimental/io/FsUtil.h>
28 #include <folly/json.h>
30 #include "hphp/runtime/ext/facts/autoload-db.h"
31 #include "hphp/runtime/ext/facts/file-facts.h"
32 #include "hphp/util/assertions.h"
33 #include "hphp/util/hash.h"
34 #include "hphp/util/logger.h"
35 #include "hphp/util/trace.h"
37 TRACE_SET_MOD(facts);
39 namespace HPHP {
40 namespace Facts {
42 DBData::DBData(
43 folly::fs::path path, SQLite::OpenMode rwMode, ::gid_t gid, ::mode_t perms)
44 : m_path{std::move(path)}, m_rwMode{rwMode}, m_gid{gid}, m_perms{perms} {
45 always_assert(m_path.is_absolute());
47 // Coerce DB permissions into unix owner/group/other bits
48 FTRACE(
50 "Coercing DB permission bits {} to {}\n",
51 m_perms,
52 (m_perms | 0600) & 0666);
53 m_perms |= 0600;
54 m_perms &= 0666;
57 bool DBData::operator==(const DBData& rhs) const {
58 return m_path == rhs.m_path && m_rwMode == rhs.m_rwMode &&
59 m_gid == rhs.m_gid && m_perms == rhs.m_perms;
62 std::string DBData::toString() const {
63 return folly::sformat("DBData({}, {}, {})", m_path.native(), m_gid, m_perms);
66 size_t DBData::hash() const {
67 return folly::hash::hash_combine(
68 hash_string_cs(m_path.native().c_str(), m_path.native().size()),
69 std::hash<gid_t>{}(m_gid),
70 std::hash<mode_t>{}(m_perms));
73 namespace {
75 /**
76 * Create the given file if it doesn't exist, setting its group ownership and
77 * permissions along the way.
79 void setFilePerms(const folly::fs::path& path, ::gid_t gid, ::mode_t perms) {
80 FTRACE(
81 3, "Creating {} with gid={} and perms={}\n", path.native(), gid, perms);
82 int dbFd = ::open(path.native().c_str(), O_CREAT, perms);
83 if (dbFd == -1) {
84 FTRACE(1, "Could not open DB at {}: errno={}", path.native(), errno);
85 return;
87 SCOPE_EXIT {
88 ::close(dbFd);
90 if (::fchown(dbFd, -1, gid) == -1) {
91 FTRACE(
92 1, "Could not chown({}, -1, {}): errno={}", path.native(), gid, errno);
94 if (::fchmod(dbFd, perms) == -1) {
95 FTRACE(1, "Could not chmod({}, {}): errno={}", path.native(), perms, errno);
99 // Representation of inheritance kinds in the DB
101 // `extends`, `implements`, or `use`
102 const int kDeriveKindExtends = 0;
103 const int kDeriveKindRequireExtends = 1;
104 const int kDeriveKindRequireImplements = 2;
106 constexpr int toDBEnum(DeriveKind kind) {
107 switch (kind) {
108 case DeriveKind::Extends:
109 return kDeriveKindExtends;
110 case DeriveKind::RequireExtends:
111 return kDeriveKindRequireExtends;
112 case DeriveKind::RequireImplements:
113 return kDeriveKindRequireImplements;
115 return -1;
118 void createSchema(SQLiteTxn& txn) {
119 // Basically copied wholesale from FlibAutoloadMapSQL.php in WWW.
121 // Common DB
123 // Parent tables
124 txn.exec("CREATE TABLE IF NOT EXISTS all_paths ("
125 " pathid INTEGER PRIMARY KEY,"
126 " path TEXT NOT NULL UNIQUE"
127 ")");
129 // Table storing data about Classes, Interfaces, Enums, and Traits
130 txn.exec("CREATE TABLE IF NOT EXISTS type_details ("
131 " typeid INTEGER PRIMARY KEY,"
132 " name TEXT NOT NULL COLLATE NOCASE,"
133 " pathid INTEGER NOT NULL REFERENCES all_paths ON DELETE CASCADE,"
134 " kind_of TEXT NOT NULL,"
135 " flags INTEGER NOT NULL,"
136 " UNIQUE (pathid, name)"
137 ")");
139 // Path assocs
141 txn.exec(
142 "CREATE TABLE IF NOT EXISTS path_sha1sum ("
143 " pathid INTEGER NOT NULL UNIQUE REFERENCES all_paths ON DELETE CASCADE,"
144 " sha1sum TEXT NOT NULL"
145 ")");
147 txn.exec("CREATE TABLE IF NOT EXISTS function_paths ("
148 " pathid INTEGER NOT NULL REFERENCES all_paths ON DELETE CASCADE,"
149 " function TEXT NOT NULL COLLATE NOCASE,"
150 " UNIQUE (pathid, function)"
151 ")");
153 txn.exec("CREATE TABLE IF NOT EXISTS constant_paths ("
154 " pathid INTEGER NOT NULL REFERENCES all_paths ON DELETE CASCADE,"
155 " constant TEXT NOT NULL,"
156 " UNIQUE (pathid, constant)"
157 ")");
159 txn.exec(
160 "CREATE TABLE IF NOT EXISTS derived_types ("
161 " derived_id INTEGER NOT NULL REFERENCES type_details ON DELETE CASCADE,"
162 " base_name TEXT NOT NULL COLLATE NOCASE,"
163 " kind INTEGER NOT NULL,"
164 " UNIQUE (derived_id, base_name, kind)"
165 ")");
167 txn.exec("CREATE TABLE IF NOT EXISTS watchman ("
168 " id INTEGER PRIMARY KEY CHECK (id = 0),"
169 " clock TEXT NULL,"
170 " mergebase TEXT NULL"
171 ")");
173 txn.exec("CREATE TABLE IF NOT EXISTS type_attributes ("
174 " typeid INTEGER NOT NULL REFERENCES type_details ON DELETE CASCADE,"
175 " attribute_name TEXT NOT NULL,"
176 " attribute_position INTEGER NULL,"
177 " attribute_value TEXT NULL,"
178 " UNIQUE (typeid, attribute_name, attribute_position)"
179 ")");
182 void rebuildIndices(SQLiteTxn& txn) {
184 // Basically copied wholesale from FlibAutoloadMapSQL.php in WWW.
186 // type_details
187 txn.exec("CREATE INDEX IF NOT EXISTS type_details__name"
188 " ON type_details (name)");
189 txn.exec("CREATE INDEX IF NOT EXISTS type_details__pathid"
190 " ON type_details (pathid)");
192 // function_paths
193 txn.exec("CREATE INDEX IF NOT EXISTS function_paths__pathid"
194 " ON function_paths (pathid)");
195 txn.exec("CREATE INDEX IF NOT EXISTS function_paths__function"
196 " ON function_paths (function)");
198 // constant_paths
199 txn.exec("CREATE INDEX IF NOT EXISTS constant_paths__pathid"
200 " ON constant_paths (pathid)");
201 txn.exec("CREATE INDEX IF NOT EXISTS constant_paths__constant"
202 " ON constant_paths (constant)");
204 // derived_types
205 txn.exec("CREATE INDEX IF NOT EXISTS derived_types__base_name"
206 " ON derived_types (base_name)");
207 txn.exec("CREATE INDEX IF NOT EXISTS derived_types__derived_id"
208 " ON derived_types (derived_id)");
210 // type_attributes
211 txn.exec("CREATE INDEX IF NOT EXISTS "
212 "type_attributes__attribute_name__typeid__attribute_position"
213 " ON type_attributes (attribute_name, typeid, attribute_position)");
216 TypeKind toTypeKind(const std::string_view kind) {
217 if (kind == kTypeKindClass) {
218 return TypeKind::Class;
219 } else if (kind == kTypeKindInterface) {
220 return TypeKind::Interface;
221 } else if (kind == kTypeKindEnum) {
222 return TypeKind::Enum;
223 } else if (kind == kTypeKindTrait) {
224 return TypeKind::Trait;
225 } else if (kind == kTypeKindTypeAlias) {
226 return TypeKind::TypeAlias;
227 } else {
228 return TypeKind::Unknown;
232 std::string getTransitiveDerivedTypesQueryStr(
233 TypeKindMask kinds, DeriveKindMask deriveKinds) {
234 auto typeKindWhereClause = [&]() -> std::string {
235 if (kinds == kTypeKindAll || kinds == 0) {
236 return "";
238 std::string clause = "AND (name=@base OR kind_of IN (";
239 auto needsDelim = false;
240 for (auto kind :
241 {TypeKind::Class,
242 TypeKind::Interface,
243 TypeKind::Enum,
244 TypeKind::Trait,
245 TypeKind::TypeAlias}) {
246 if (kinds & static_cast<int>(kind)) {
247 if (needsDelim) {
248 clause += ", ";
249 } else {
250 needsDelim = true;
252 clause += "\"";
253 clause += toString(kind);
254 clause += "\"";
257 clause += "))";
258 return clause;
259 }();
260 auto deriveKindWhereClause = [&]() -> std::string {
261 if (deriveKinds == kDeriveKindAll || deriveKinds == 0) {
262 return "";
264 bool needsDelim = false;
265 std::string clause = "AND derived_types.kind IN (";
266 for (auto deriveKind :
267 {DeriveKind::Extends,
268 DeriveKind::RequireExtends,
269 DeriveKind::RequireImplements}) {
270 if (deriveKinds & static_cast<int>(deriveKind)) {
271 if (needsDelim) {
272 clause += ", ";
273 } else {
274 needsDelim = true;
276 clause += folly::sformat("{}", toDBEnum(deriveKind));
279 clause += ")";
280 return clause;
281 }();
282 return folly::sformat(
283 "WITH RECURSIVE subtypes(id) AS ("
284 " SELECT typeid FROM type_details WHERE name=@base"
285 " UNION SELECT derived_id FROM derived_types, type_details, subtypes"
286 " WHERE derived_types.base_name=type_details.name"
287 " AND type_details.typeid=subtypes.id"
288 " {}"
289 " {}"
291 " SELECT name, path, kind_of, flags FROM type_details"
292 " JOIN all_paths USING (pathid)"
293 " WHERE type_details.typeid IN (SELECT id FROM subtypes)"
294 " AND name <> @base"
295 " {}",
296 typeKindWhereClause,
297 deriveKindWhereClause,
298 typeKindWhereClause);
301 struct PathStmts {
302 explicit PathStmts(SQLite& db)
303 : m_insert{db.prepare(
304 "INSERT OR IGNORE INTO all_paths (path) VALUES (@path)")}
305 , m_erase{db.prepare("DELETE FROM all_paths WHERE path = @path")}
306 , m_getAll{db.prepare("SELECT path, sha1sum FROM path_sha1sum"
307 " JOIN all_paths USING (pathid)")} {
310 SQLiteStmt m_insert;
311 SQLiteStmt m_erase;
312 SQLiteStmt m_getAll;
315 struct Sha1HexStmts {
316 explicit Sha1HexStmts(SQLite& db)
317 : m_insert{db.prepare("INSERT OR REPLACE INTO path_sha1sum VALUES ("
318 " (SELECT pathid FROM all_paths WHERE path=@path),"
319 " @sha1sum"
320 ")")}
321 , m_get{db.prepare("SELECT sha1sum FROM path_sha1sum"
322 " JOIN all_paths USING (pathid)"
323 " WHERE path = @path")} {
326 SQLiteStmt m_insert;
327 SQLiteStmt m_get;
330 struct TypeStmts {
331 explicit TypeStmts(SQLite& db)
332 : m_insertDetails{db.prepare(
333 "INSERT OR IGNORE INTO type_details (pathid, name, kind_of, "
334 "flags)"
335 " VALUES ("
336 " (SELECT pathid FROM all_paths WHERE path=@path),"
337 " @name,"
338 " @kind_of,"
339 " @flags"
340 " )")}
341 , m_getTypePath{db.prepare("SELECT path FROM type_details"
342 " JOIN all_paths USING (pathid)"
343 " WHERE name=@type")}
344 , m_getPathTypes{db.prepare("SELECT name FROM type_details"
345 " JOIN all_paths USING (pathid)"
346 " WHERE path=@path")}
347 , m_getKindAndFlags{db.prepare("SELECT kind_of, flags FROM type_details"
348 " JOIN all_paths USING (pathid)"
349 " WHERE name=@type"
350 " AND path=@path")}
351 , m_insertBaseType{db.prepare(
352 "INSERT OR IGNORE INTO derived_types (base_name, derived_id, kind)"
353 " VALUES ("
354 " @base,"
355 " (SELECT typeid FROM type_details JOIN all_paths USING (pathid)"
356 " WHERE name=@derived AND path=@path),"
357 " @kind"
358 " )")}
359 , m_getBaseTypes{db.prepare("SELECT base_name FROM derived_types"
360 " JOIN type_details AS derived_type ON "
361 "(derived_type.typeid=derived_id)"
362 " JOIN all_paths USING (pathid)"
363 " WHERE derived_type.name = @derived"
364 " AND path = @path"
365 " AND kind = @kind")}
366 , m_getDerivedTypes{db.prepare(
367 "SELECT path, derived_type.name FROM derived_types"
368 " JOIN type_details AS derived_type ON "
369 "(derived_type.typeid=derived_id)"
370 " JOIN all_paths USING (pathid)"
371 " WHERE base_name = @base"
372 " AND kind = @kind")}
373 , m_insertAttribute{db.prepare(
374 "INSERT OR IGNORE INTO type_attributes ("
375 " typeid,"
376 " attribute_name,"
377 " attribute_position,"
378 " attribute_value"
380 " VALUES ("
381 " (SELECT typeid FROM type_details JOIN all_paths USING (pathid)"
382 " WHERE name=@type AND path=@path),"
383 " @attribute_name,"
384 " @attribute_position,"
385 " @attribute_value"
386 " )")}
387 , m_getAttributes{db.prepare("SELECT DISTINCT attribute_name"
388 " FROM type_attributes"
389 " JOIN type_details USING (typeid)"
390 " JOIN all_paths USING (pathid)"
391 " WHERE name=@type AND path = @path")}
392 , m_getAttributeArgs{db.prepare("SELECT attribute_value"
393 " FROM type_attributes"
394 " JOIN type_details USING (typeid)"
395 " JOIN all_paths USING (pathid)"
396 " WHERE name = @type"
397 " AND path = @path"
398 " AND attribute_name = @attribute_name")}
399 , m_getTypesWithAttribute{db.prepare(
400 "SELECT name, path from type_details"
401 " JOIN all_paths USING (pathid)"
402 " WHERE EXISTS ("
403 " SELECT * FROM type_attributes"
404 " WHERE attribute_name = @attribute_name"
405 " AND type_attributes.typeid=type_details.typeid"
406 " )")}
407 , m_getCorrectCase{db.prepare(
408 "SELECT name FROM type_details WHERE name=@name")}
409 , m_getAll{db.prepare("SELECT name, path from type_details JOIN "
410 "all_paths USING (pathid)")} {
413 SQLiteStmt m_insertDetails;
414 SQLiteStmt m_getTypePath;
415 SQLiteStmt m_getPathTypes;
416 SQLiteStmt m_getKindAndFlags;
417 SQLiteStmt m_insertBaseType;
418 SQLiteStmt m_getBaseTypes;
419 SQLiteStmt m_getDerivedTypes;
420 SQLiteStmt m_insertAttribute;
421 SQLiteStmt m_getAttributes;
422 SQLiteStmt m_getAttributeArgs;
423 SQLiteStmt m_getTypesWithAttribute;
424 SQLiteStmt m_getCorrectCase;
425 SQLiteStmt m_getAll;
428 struct FunctionStmts {
429 explicit FunctionStmts(SQLite& db)
430 : m_insert{db.prepare(
431 "INSERT OR IGNORE INTO function_paths (function, pathid) VALUES ("
432 " @function,"
433 " (SELECT pathid FROM all_paths WHERE path=@path)"
434 ")")}
435 , m_getFunctionPath{db.prepare("SELECT path FROM function_paths"
436 " JOIN all_paths USING (pathid)"
437 " WHERE function=@function")}
438 , m_getPathFunctions{db.prepare("SELECT function FROM function_paths"
439 " JOIN all_paths USING (pathid)"
440 " WHERE path=@path")}
441 , m_getCorrectCase{db.prepare(
442 "SELECT function from function_paths where function=@function")}
443 , m_getAll{db.prepare("SELECT function, path FROM function_paths JOIN "
444 "all_paths USING (pathid)")} {
447 SQLiteStmt m_insert;
448 SQLiteStmt m_getFunctionPath;
449 SQLiteStmt m_getPathFunctions;
450 SQLiteStmt m_getCorrectCase;
451 SQLiteStmt m_getAll;
454 struct ConstantStmts {
455 explicit ConstantStmts(SQLite& db)
456 : m_insert{db.prepare(
457 "INSERT OR IGNORE INTO constant_paths (constant, pathid) VALUES("
458 " @constant,"
459 " (SELECT pathid FROM all_paths"
460 " WHERE path=@path)"
461 ")")}
462 , m_getConstantPath{db.prepare("SELECT path FROM constant_paths"
463 " JOIN all_paths USING (pathid)"
464 " WHERE constant=@constant")}
465 , m_getPathConstants{db.prepare("SELECT constant FROM constant_paths"
466 " JOIN all_paths USING (pathid)"
467 " WHERE path=@path")}
468 , m_getAll{db.prepare("SELECT constant, path FROM constant_paths JOIN "
469 "all_paths USING (pathid)")} {
472 SQLiteStmt m_insert;
473 SQLiteStmt m_getConstantPath;
474 SQLiteStmt m_getPathConstants;
475 SQLiteStmt m_getAll;
478 struct ClockStmts {
479 explicit ClockStmts(SQLite& db)
480 : m_insert{db.prepare(
481 "INSERT OR REPLACE INTO watchman (OID, clock, mergebase)"
482 " VALUES (0, @clock, @mergebase)")}
483 , m_get{db.prepare("SELECT clock, mergebase FROM watchman WHERE OID=0")} {
485 SQLiteStmt m_insert;
486 SQLiteStmt m_get;
489 struct AutoloadDBImpl final : public AutoloadDB {
490 explicit AutoloadDBImpl(SQLite db)
491 : m_db{std::move(db)}
492 , m_pathStmts{m_db}
493 , m_sha1HexStmts{m_db}
494 , m_typeStmts{m_db}
495 , m_functionStmts{m_db}
496 , m_constantStmts{m_db}
497 , m_clockStmts{m_db} {
500 AutoloadDBImpl(const AutoloadDBImpl&) = delete;
501 AutoloadDBImpl(AutoloadDBImpl&&) noexcept = default;
502 AutoloadDBImpl& operator=(const AutoloadDBImpl&) = delete;
503 AutoloadDBImpl& operator=(AutoloadDBImpl&&) noexcept = delete;
505 ~AutoloadDBImpl() override = default;
507 static AutoloadDBImpl get(const DBData& dbData) {
508 assertx(dbData.m_path.is_absolute());
509 auto db = [&]() {
510 try {
511 return SQLite::connect(dbData.m_path.native(), dbData.m_rwMode);
512 } catch (SQLiteExc& e) {
513 throw std::runtime_error{folly::sformat(
514 "Couldn't open or create native Facts DB at {}",
515 dbData.m_path.native())};
517 }();
518 if (dbData.m_rwMode == SQLite::OpenMode::ReadWrite) {
519 // If writable, ensure the DB has the correct owner and permissions.
520 setFilePerms(dbData.m_path, dbData.m_gid, dbData.m_perms);
521 setFilePerms(
522 folly::fs::path{dbData.m_path} += "-shm",
523 dbData.m_gid,
524 dbData.m_perms);
525 setFilePerms(
526 folly::fs::path{dbData.m_path} += "-wal",
527 dbData.m_gid,
528 dbData.m_perms);
530 if (!db.isReadOnly()) {
531 try {
532 db.setJournalMode(SQLite::JournalMode::WAL);
533 } catch (SQLiteExc& e) {
534 switch (e.code()) {
535 case SQLiteExc::Code::BUSY:
536 // This happens if multiple connections attempt to set WAL mode at
537 // the same time. We only need one connection to succeed.
538 break;
539 default:
540 throw;
544 db.setSynchronousLevel(SQLite::SynchronousLevel::OFF);
546 auto txn = db.begin();
547 createSchema(txn);
548 rebuildIndices(txn);
549 db.setBusyTimeout(60'000);
550 txn.commit();
551 FTRACE(3, "Connected to SQLite DB at {}.\n", dbData.m_path.native());
554 return AutoloadDBImpl{std::move(db)};
557 SQLiteTxn begin() override {
558 return m_db.begin();
561 void insertPath(SQLiteTxn& txn, const folly::fs::path& path) override {
562 assertx(path.is_relative());
563 FTRACE(4, "Registering path {} in the DB\n", path.native());
564 auto query = txn.query(m_pathStmts.m_insert);
565 query.bindString("@path", path.native());
566 FTRACE(5, "Running {}\n", query.sql());
567 query.step();
570 void insertSha1Hex(
571 SQLiteTxn& txn,
572 const folly::fs::path& path,
573 const std::optional<std::string>& sha1hex) override {
574 assertx(path.is_relative());
575 auto query = txn.query(m_sha1HexStmts.m_insert);
576 query.bindString("@path", path.native());
577 if (sha1hex) {
578 query.bindString("@sha1sum", *sha1hex);
579 } else {
580 query.bindNull("@sha1sum");
582 FTRACE(5, "Running {}\n", query.sql());
583 query.step();
586 std::string getSha1Hex(SQLiteTxn& txn, const folly::fs::path& path) override {
587 assertx(path.is_relative());
588 auto query = txn.query(m_sha1HexStmts.m_get);
589 query.bindString("@path", path.native());
590 FTRACE(5, "Running {}\n", query.sql());
591 query.step();
592 return std::string{query.getString(0)};
595 void erasePath(SQLiteTxn& txn, const folly::fs::path& path) override {
596 assertx(path.is_relative());
597 auto query = txn.query(m_pathStmts.m_erase);
598 query.bindString("@path", path.native());
599 FTRACE(5, "Running {}\n", query.sql());
600 query.step();
603 void insertType(
604 SQLiteTxn& txn,
605 std::string_view type,
606 const folly::fs::path& path,
607 TypeKind kind,
608 int flags) override {
609 assertx(path.is_relative());
611 auto query = txn.query(m_typeStmts.m_insertDetails);
612 query.bindString("@name", type);
613 query.bindString("@path", path.native());
614 query.bindString("@kind_of", toString(kind));
615 query.bindInt("@flags", flags);
616 FTRACE(5, "Running {}\n", query.sql());
617 query.step();
621 std::vector<folly::fs::path>
622 getTypePath(SQLiteTxn& txn, std::string_view type) override {
623 auto query = txn.query(m_typeStmts.m_getTypePath);
624 query.bindString("@type", type);
625 std::vector<folly::fs::path> results;
626 FTRACE(5, "Running {}\n", query.sql());
627 for (query.step(); query.row(); query.step()) {
628 results.emplace_back(std::string{query.getString(0)});
630 return results;
633 std::vector<std::string>
634 getPathTypes(SQLiteTxn& txn, const folly::fs::path& path) override {
635 assertx(path.is_relative());
636 auto query = txn.query(m_typeStmts.m_getPathTypes);
637 query.bindString("@path", path.native());
638 std::vector<std::string> types;
639 FTRACE(5, "Running {}\n", query.sql());
640 for (query.step(); query.row(); query.step()) {
641 types.emplace_back(query.getString(0));
643 return types;
646 std::pair<TypeKind, int> getKindAndFlags(
647 SQLiteTxn& txn,
648 const std::string_view type,
649 const folly::fs::path& path) override {
650 auto query = txn.query(m_typeStmts.m_getKindAndFlags);
651 query.bindString("@type", type);
652 query.bindString("@path", path.native());
653 FTRACE(5, "Running {}\n", query.sql());
654 for (query.step(); query.row(); query.step()) {
655 return {toTypeKind(query.getString(0)), query.getInt(1)};
657 return {TypeKind::Unknown, 0};
660 void insertBaseType(
661 SQLiteTxn& txn,
662 const folly::fs::path& path,
663 const std::string_view derived,
664 DeriveKind kind,
665 const std::string_view base) override {
666 assertx(path.is_relative());
667 auto query = txn.query(m_typeStmts.m_insertBaseType);
668 query.bindString("@path", path.native());
669 query.bindString("@derived", derived);
670 query.bindInt("@kind", toDBEnum(kind));
671 query.bindString("@base", base);
672 FTRACE(5, "Running {}\n", query.sql());
673 query.step();
676 std::vector<std::string> getBaseTypes(
677 SQLiteTxn& txn,
678 const folly::fs::path& path,
679 const std::string_view derived,
680 DeriveKind kind) override {
681 assertx(path.is_relative());
682 auto query = txn.query(m_typeStmts.m_getBaseTypes);
683 query.bindString("@derived", derived);
684 query.bindString("@path", path.native());
685 query.bindInt("@kind", toDBEnum(kind));
686 std::vector<std::string> types;
687 FTRACE(5, "Running {}\n", query.sql());
688 for (query.step(); query.row(); query.step()) {
689 types.emplace_back(query.getString(0));
691 return types;
694 std::vector<std::pair<folly::fs::path, std::string>> getDerivedTypes(
695 SQLiteTxn& txn, const std::string_view base, DeriveKind kind) override {
696 auto query = txn.query(m_typeStmts.m_getDerivedTypes);
697 query.bindString("@base", base);
698 query.bindInt("@kind", toDBEnum(kind));
699 std::vector<std::pair<folly::fs::path, std::string>> edges;
700 FTRACE(5, "Running {}\n", query.sql());
701 for (query.step(); query.row(); query.step()) {
702 edges.push_back(
703 {folly::fs::path{std::string{query.getString(0)}},
704 std::string{query.getString(1)}});
706 return edges;
709 SQLiteStmt& getTransitiveDerivedTypesStmt(
710 TypeKindMask kinds, DeriveKindMask deriveKinds) {
711 auto it = m_derivedTypeStmts.find({kinds, deriveKinds});
712 if (it == m_derivedTypeStmts.end()) {
713 return m_derivedTypeStmts
714 .insert(
715 {std::make_tuple(kinds, deriveKinds),
716 m_db.prepare(
717 getTransitiveDerivedTypesQueryStr(kinds, deriveKinds))})
718 .first->second;
720 return it->second;
723 RowIter<DerivedTypeInfo> getTransitiveDerivedTypes(
724 SQLiteTxn& txn,
725 const std::string_view baseType,
726 TypeKindMask kinds = kTypeKindAll,
727 DeriveKindMask deriveKinds = kDeriveKindAll) override {
728 auto query = txn.query(getTransitiveDerivedTypesStmt(kinds, deriveKinds));
729 query.bindString("@base", baseType);
730 FTRACE(5, "Running {}\n", query.sql());
731 return RowIter<DerivedTypeInfo>{
732 std::move(query), [](SQLiteQuery& query) -> DerivedTypeInfo {
733 return {
734 query.getString(0),
735 query.getString(1),
736 toTypeKind(query.getString(2)),
737 query.getInt(3)};
741 void insertTypeAttribute(
742 SQLiteTxn& txn,
743 const folly::fs::path& path,
744 const std::string_view type,
745 const std::string_view attributeName,
746 std::optional<int> attributePosition,
747 const folly::dynamic* attributeValue) override {
749 std::string attrValueJson;
750 auto query = txn.query(m_typeStmts.m_insertAttribute);
752 auto const attributeValueKey = "@attribute_value";
753 if (attributeValue) {
754 attrValueJson = folly::toJson(*attributeValue);
755 query.bindString(attributeValueKey, attrValueJson);
756 } else {
757 query.bindNull(attributeValueKey);
760 auto const attributePositionKey = "@attribute_position";
761 if (attributePosition) {
762 query.bindInt(attributePositionKey, *attributePosition);
763 } else {
764 query.bindNull(attributePositionKey);
767 query.bindString("@type", type);
768 query.bindString("@path", path.native());
769 query.bindString("@attribute_name", attributeName);
771 FTRACE(5, "Running {}\n", query.sql());
772 query.step();
775 void analyze() override {
776 m_db.analyze();
779 std::vector<std::string> getAttributesOfType(
780 SQLiteTxn& txn,
781 const std::string_view type,
782 const folly::fs::path& path) override {
783 auto query = txn.query(m_typeStmts.m_getAttributes);
784 query.bindString("@type", type);
785 query.bindString("@path", path.native());
786 std::vector<std::string> results;
787 FTRACE(5, "Running {}\n", query.sql());
788 for (query.step(); query.row(); query.step()) {
789 results.emplace_back(query.getString(0));
791 return results;
794 std::vector<std::pair<std::string, folly::fs::path>> getTypesWithAttribute(
795 SQLiteTxn& txn, const std::string_view attributeName) override {
796 auto query = txn.query(m_typeStmts.m_getTypesWithAttribute);
797 query.bindString("@attribute_name", attributeName);
798 std::vector<std::pair<std::string, folly::fs::path>> results;
799 FTRACE(5, "Running {}\n", query.sql());
800 for (query.step(); query.row(); query.step()) {
801 results.emplace_back(query.getString(0), std::string{query.getString(1)});
803 return results;
806 std::vector<folly::dynamic> getAttributeArgs(
807 SQLiteTxn& txn,
808 const std::string_view type,
809 const std::string_view path,
810 const std::string_view attributeName) override {
811 auto query = txn.query(m_typeStmts.m_getAttributeArgs);
812 query.bindString("@type", type);
813 query.bindString("@path", path);
814 query.bindString("@attribute_name", attributeName);
815 FTRACE(5, "Running {}\n", query.sql());
816 std::vector<folly::dynamic> args;
817 for (query.step(); query.row(); query.step()) {
818 auto arg = query.getNullableString(0);
819 if (arg) {
820 args.push_back(folly::parseJson(*arg));
823 return args;
826 std::string
827 getTypeCorrectCase(SQLiteTxn& txn, std::string_view type) override {
828 auto query = txn.query(m_typeStmts.m_getCorrectCase);
829 query.bindString("@name", type);
830 for (query.step(); query.row(); query.step()) {
831 return std::string{query.getString(0)};
833 return {};
836 void insertFunction(
837 SQLiteTxn& txn,
838 std::string_view function,
839 const folly::fs::path& path) override {
840 assertx(path.is_relative());
841 auto query = txn.query(m_functionStmts.m_insert);
842 query.bindString("@function", function);
843 query.bindString("@path", path.native());
844 FTRACE(5, "Running {}\n", query.sql());
845 query.step();
848 std::vector<folly::fs::path>
849 getFunctionPath(SQLiteTxn& txn, std::string_view function) override {
850 auto query = txn.query(m_functionStmts.m_getFunctionPath);
851 query.bindString("@function", function);
852 FTRACE(5, "Running {}\n", query.sql());
853 std::vector<folly::fs::path> results;
854 for (query.step(); query.row(); query.step()) {
855 results.emplace_back(std::string{query.getString(0)});
857 return results;
860 std::vector<std::string>
861 getPathFunctions(SQLiteTxn& txn, const folly::fs::path& path) override {
862 assertx(path.is_relative());
863 auto query = txn.query(m_functionStmts.m_getPathFunctions);
864 query.bindString("@path", path.native());
865 std::vector<std::string> functions;
866 FTRACE(5, "Running {}\n", query.sql());
867 for (query.step(); query.row(); query.step()) {
868 functions.emplace_back(query.getString(0));
870 return functions;
873 std::string
874 getFunctionCorrectCase(SQLiteTxn& txn, std::string_view function) override {
875 auto query = txn.query(m_functionStmts.m_getCorrectCase);
876 query.bindString("@function", function);
877 for (query.step(); query.row(); query.step()) {
878 return std::string{query.getString(0)};
880 return {};
883 void insertConstant(
884 SQLiteTxn& txn,
885 std::string_view constant,
886 const folly::fs::path& path) override {
887 assertx(path.is_relative());
888 auto query = txn.query(m_constantStmts.m_insert);
889 query.bindString("@constant", constant);
890 query.bindString("@path", path.native());
891 FTRACE(5, "Running {}\n", query.sql());
892 query.step();
895 std::vector<folly::fs::path>
896 getConstantPath(SQLiteTxn& txn, std::string_view constant) override {
897 auto query = txn.query(m_constantStmts.m_getConstantPath);
898 query.bindString("@constant", constant);
899 std::vector<folly::fs::path> results;
900 FTRACE(5, "Running {}\n", query.sql());
901 for (query.step(); query.row(); query.step()) {
902 results.emplace_back(std::string{query.getString(0)});
904 return results;
907 std::vector<std::string>
908 getPathConstants(SQLiteTxn& txn, const folly::fs::path& path) override {
909 assertx(path.is_relative());
910 auto query = txn.query(m_constantStmts.m_getPathConstants);
911 query.bindString("@path", path.native());
912 std::vector<std::string> constants;
913 FTRACE(5, "Running {}\n", query.sql());
914 for (query.step(); query.row(); query.step()) {
915 constants.emplace_back(query.getString(0));
917 return constants;
920 RowIter<PathAndHash> getAllPathsAndHashes(SQLiteTxn& txn) override {
921 auto query = txn.query(m_pathStmts.m_getAll);
922 FTRACE(5, "Running {}\n", query.sql());
923 return RowIter<PathAndHash>{
924 std::move(query), [](SQLiteQuery& q) -> PathAndHash {
925 return {std::string{q.getString(0)}, q.getString(1)};
929 RowIter<SymbolPath> getAllTypePaths(SQLiteTxn& txn) override {
930 auto query = txn.query(m_typeStmts.m_getAll);
931 FTRACE(5, "Running {}\n", query.sql());
932 return RowIter<SymbolPath>{
933 std::move(query), [](SQLiteQuery& q) -> SymbolPath {
934 return {q.getString(0), {std::string{q.getString(1)}}};
938 RowIter<SymbolPath> getAllFunctionPaths(SQLiteTxn& txn) override {
939 auto query = txn.query(m_functionStmts.m_getAll);
940 FTRACE(5, "Running {}\n", query.sql());
941 return RowIter<SymbolPath>{
942 std::move(query), [](SQLiteQuery& q) -> SymbolPath {
943 return {q.getString(0), {std::string{q.getString(1)}}};
947 RowIter<SymbolPath> getAllConstantPaths(SQLiteTxn& txn) override {
948 auto query = txn.query(m_constantStmts.m_getAll);
949 FTRACE(5, "Running {}\n", query.sql());
950 return RowIter<SymbolPath>{
951 std::move(query), [](SQLiteQuery& q) -> SymbolPath {
952 return {q.getString(0), {std::string{q.getString(1)}}};
956 void insertClock(SQLiteTxn& txn, std::string_view clock) override {
957 auto query = txn.query(m_clockStmts.m_insert);
958 query.bindString("@clock", clock);
959 FTRACE(5, "Running {}\n", query.sql());
960 query.step();
963 std::string getClock(SQLiteTxn& txn) override {
964 auto query = txn.query(m_clockStmts.m_get);
965 FTRACE(5, "Running {}\n", query.sql());
966 query.step();
967 if (!query.row()) {
968 return {};
970 return std::string{query.getString(0)};
973 SQLite m_db;
974 PathStmts m_pathStmts;
975 Sha1HexStmts m_sha1HexStmts;
976 TypeStmts m_typeStmts;
977 hphp_hash_map<std::tuple<TypeKindMask, DeriveKindMask>, SQLiteStmt>
978 m_derivedTypeStmts;
979 FunctionStmts m_functionStmts;
980 ConstantStmts m_constantStmts;
981 ClockStmts m_clockStmts;
984 } // namespace
986 AutoloadDB::~AutoloadDB() = default;
988 THREAD_LOCAL(AutoloadDBThreadLocal, t_adb);
990 AutoloadDB& getDB(const DBData& dbData) {
991 AutoloadDBThreadLocal& dbVault = *t_adb.get();
992 auto& dbPtr = dbVault[{dbData.m_path.native(), dbData.m_rwMode}];
993 if (!dbPtr) {
994 dbPtr = std::make_unique<AutoloadDBImpl>(AutoloadDBImpl::get(dbData));
996 return *dbPtr;
999 } // namespace Facts
1000 } // namespace HPHP