src/Pure/General/file_store.scala
author wenzelm
Wed, 12 Feb 2025 20:21:33 +0100
changeset 82151 a42414afe05f
parent 82150 2eb2aa0375fb
child 82161 843c2205b076
permissions -rw-r--r--
more operations;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     1
/*  Title:      Pure/General/file_store.scala
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     3
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     4
Persistent store for file-system content, backed by SQL database
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     5
(notably SQLite).
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     6
*/
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     7
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     8
package isabelle
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
     9
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    10
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    11
import java.io.{File => JFile}
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    12
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    13
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    14
object File_Store {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    15
  /* main operations */
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    16
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    17
  def make_database(database: Path, dir: Path,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    18
    pred: JFile => Boolean = _ => true,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    19
    name_prefix: String = "",
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    20
    compress_options: Compress.Options = Compress.Options(),
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    21
    compress_cache: Compress.Cache = Compress.Cache.none
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    22
  ): Unit = {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    23
    database.file.delete()
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    24
    using(SQLite.open_database(database)) { db =>
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    25
      private_data.transaction_lock(db, create = true) {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    26
        val root = dir.canonical_file
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    27
        val root_path = File.path(root)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    28
        for {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    29
          file <- File.find_files(root, pred = pred)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    30
          rel_path <- File.relative_path(root_path, File.path(file))
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    31
        } {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    32
          val entry =
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    33
            Entry.read_file(rel_path, name_prefix = name_prefix, dir = root_path,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    34
              compress_options = compress_options, compress_cache = compress_cache)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    35
          private_data.write_entry(db, entry)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    36
        }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    37
      }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    38
    }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    39
  }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    40
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    41
  def database_entry(database: Path, name: String): Option[Entry] =
82149
18709ffb8137 tuned signature: more explicit operations;
wenzelm
parents: 82148
diff changeset
    42
    if (database.is_file) {
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    43
      using(SQLite.open_database(database)) { db =>
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    44
        private_data.transaction_lock(db) {
82149
18709ffb8137 tuned signature: more explicit operations;
wenzelm
parents: 82148
diff changeset
    45
          if (private_data.tables_ok(db)) private_data.read_entry(db, name)
18709ffb8137 tuned signature: more explicit operations;
wenzelm
parents: 82148
diff changeset
    46
          else None
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    47
        }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    48
      }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    49
    }
82149
18709ffb8137 tuned signature: more explicit operations;
wenzelm
parents: 82148
diff changeset
    50
    else None
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    51
82151
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    52
  def database_extract(database: Path, dir: Path,
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    53
    compress_cache: Compress.Cache = Compress.Cache.none
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    54
  ): Unit = {
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    55
    if (database.is_file) {
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    56
      using(SQLite.open_database(database)) { db =>
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    57
        private_data.transaction_lock(db) {
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    58
          if (private_data.tables_ok(db)) {
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    59
            for {
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    60
              name <- private_data.read_names(db)
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    61
              entry <- private_data.read_entry(db, name)
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    62
            } entry.write_file(dir, compress_cache = compress_cache)
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    63
          }
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    64
        }
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    65
      }
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    66
    }
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    67
  }
a42414afe05f more operations;
wenzelm
parents: 82150
diff changeset
    68
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    69
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    70
  /* entry */
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    71
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    72
  object Entry {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    73
    def read_file(path: Path,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    74
      name_prefix: String = "",
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    75
      dir: Path = Path.current,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    76
      compress_options: Compress.Options = Compress.Options(),
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    77
      compress_cache: Compress.Cache = Compress.Cache.none
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    78
    ): Entry = {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    79
      val name = Url.append_path(name_prefix, path.expand.implode)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    80
      val bs = Bytes.read(dir + path)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    81
      val size = bs.size
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    82
      val executable = File.is_executable(dir + path)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    83
      val (compressed, body) = bs.maybe_compress(options = compress_options, cache = compress_cache)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    84
      Entry(name, size, executable, compressed, body)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    85
    }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    86
  }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    87
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    88
  sealed case class Entry(
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    89
    name: String,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    90
    size: Long,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    91
    executable: Boolean,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    92
    compressed: Boolean,
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    93
    body: Bytes
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    94
  ) {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    95
    require(name.nonEmpty && size >= 0 && (size > 0 || compressed))
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    96
82150
2eb2aa0375fb tuned signature;
wenzelm
parents: 82149
diff changeset
    97
    def content(compress_cache: Compress.Cache = Compress.Cache.none): Bytes =
2eb2aa0375fb tuned signature;
wenzelm
parents: 82149
diff changeset
    98
      if (compressed) body.uncompress(cache = compress_cache) else body
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
    99
82150
2eb2aa0375fb tuned signature;
wenzelm
parents: 82149
diff changeset
   100
    def write_file(dir: Path, compress_cache: Compress.Cache = Compress.Cache.none): Unit = {
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   101
      val path = Path.explode(name)
82150
2eb2aa0375fb tuned signature;
wenzelm
parents: 82149
diff changeset
   102
      File.content(path, content(compress_cache = compress_cache)).write(dir)
82148
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   103
      if (executable) File.set_executable(dir + path)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   104
    }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   105
  }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   106
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   107
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   108
  /* SQL data model */
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   109
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   110
  object private_data extends SQL.Data() {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   111
    override lazy val tables: SQL.Tables = SQL.Tables(Base.table)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   112
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   113
    object Base {
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   114
      val name = SQL.Column.string("name").make_primary_key
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   115
      val size = SQL.Column.long("size")
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   116
      val executable = SQL.Column.bool("executable")
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   117
      val compressed = SQL.Column.bool("compressed")
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   118
      val body = SQL.Column.bytes("body")
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   119
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   120
      val table = SQL.Table("isabelle_file_store", List(name, size, executable, compressed, body))
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   121
    }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   122
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   123
    def read_names(db: SQL.Database): List[String] =
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   124
      db.execute_query_statement(
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   125
        Base.table.select(List(Base.name)),
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   126
        List.from[String], _.string(Base.name)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   127
      ).sorted
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   128
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   129
    def read_entry(db: SQL.Database, name: String): Option[Entry] =
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   130
      db.execute_query_statementO[Entry](
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   131
        Base.table.select(List(Base.size, Base.executable, Base.compressed, Base.body),
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   132
          sql = Base.name.where_equal(name)),
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   133
        { res =>
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   134
          val size = res.long(Base.size)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   135
          val executable = res.bool(Base.executable)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   136
          val compressed = res.bool(Base.compressed)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   137
          val body = res.bytes(Base.body)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   138
          Entry(name, size, executable, compressed, body)
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   139
        })
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   140
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   141
    def write_entry(db: SQL.Database, entry: Entry): Unit =
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   142
      db.execute_statement(Base.table.insert(), body = { stmt =>
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   143
        stmt.string(1) = entry.name
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   144
        stmt.long(2) = entry.size
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   145
        stmt.bool(3) = entry.executable
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   146
        stmt.bool(4) = entry.compressed
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   147
        stmt.bytes(5) = entry.body
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   148
      })
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   149
  }
b387a9099b72 support persistent store for file-system content (notably SQLite);
wenzelm
parents:
diff changeset
   150
}