55 val all_settings = ISABELLE_BUILD_OPTIONS :: ml_settings |
52 val all_settings = ISABELLE_BUILD_OPTIONS :: ml_settings |
56 |
53 |
57 type Entry = (String, String) |
54 type Entry = (String, String) |
58 type T = List[Entry] |
55 type T = List[Entry] |
59 |
56 |
60 object Entry |
57 object Entry { |
61 { |
|
62 def unapply(s: String): Option[Entry] = |
58 def unapply(s: String): Option[Entry] = |
63 for { (a, b) <- Properties.Eq.unapply(s) } |
59 for { (a, b) <- Properties.Eq.unapply(s) } |
64 yield (a, Library.perhaps_unquote(b)) |
60 yield (a, Library.perhaps_unquote(b)) |
65 def getenv(a: String): String = |
61 def getenv(a: String): String = |
66 Properties.Eq(a, quote(Isabelle_System.getenv(a))) |
62 Properties.Eq(a, quote(Isabelle_System.getenv(a))) |
128 |
121 |
129 def is_log(file: JFile, |
122 def is_log(file: JFile, |
130 prefixes: List[String] = |
123 prefixes: List[String] = |
131 List(Build_History.log_prefix, Identify.log_prefix, Identify.log_prefix2, |
124 List(Build_History.log_prefix, Identify.log_prefix, Identify.log_prefix2, |
132 Isatest.log_prefix, AFP_Test.log_prefix, Jenkins.log_prefix), |
125 Isatest.log_prefix, AFP_Test.log_prefix, Jenkins.log_prefix), |
133 suffixes: List[String] = List(".log", ".log.gz", ".log.xz")): Boolean = |
126 suffixes: List[String] = List(".log", ".log.gz", ".log.xz") |
134 { |
127 ): Boolean = { |
135 val name = file.getName |
128 val name = file.getName |
136 |
129 |
137 prefixes.exists(name.startsWith) && |
130 prefixes.exists(name.startsWith) && |
138 suffixes.exists(name.endsWith) && |
131 suffixes.exists(name.endsWith) && |
139 name != "isatest.log" && |
132 name != "isatest.log" && |
267 |
257 |
268 |
258 |
269 |
259 |
270 /** digested meta info: produced by Admin/build_history in log.xz file **/ |
260 /** digested meta info: produced by Admin/build_history in log.xz file **/ |
271 |
261 |
272 object Meta_Info |
262 object Meta_Info { |
273 { |
|
274 val empty: Meta_Info = Meta_Info(Nil, Nil) |
263 val empty: Meta_Info = Meta_Info(Nil, Nil) |
275 } |
264 } |
276 |
265 |
277 sealed case class Meta_Info(props: Properties.T, settings: Settings.T) |
266 sealed case class Meta_Info(props: Properties.T, settings: Settings.T) { |
278 { |
|
279 def is_empty: Boolean = props.isEmpty && settings.isEmpty |
267 def is_empty: Boolean = props.isEmpty && settings.isEmpty |
280 |
268 |
281 def get(c: SQL.Column): Option[String] = |
269 def get(c: SQL.Column): Option[String] = |
282 Properties.get(props, c.name) orElse |
270 Properties.get(props, c.name) orElse |
283 Properties.get(settings, c.name) |
271 Properties.get(settings, c.name) |
284 |
272 |
285 def get_date(c: SQL.Column): Option[Date] = |
273 def get_date(c: SQL.Column): Option[Date] = |
286 get(c).map(Log_File.Date_Format.parse) |
274 get(c).map(Log_File.Date_Format.parse) |
287 } |
275 } |
288 |
276 |
289 object Identify |
277 object Identify { |
290 { |
|
291 val log_prefix = "isabelle_identify_" |
278 val log_prefix = "isabelle_identify_" |
292 val log_prefix2 = "plain_identify_" |
279 val log_prefix2 = "plain_identify_" |
293 |
280 |
294 def engine(log_file: Log_File): String = |
281 def engine(log_file: Log_File): String = |
295 if (log_file.name.startsWith(Jenkins.log_prefix)) "jenkins_identify" |
282 if (log_file.name.startsWith(Jenkins.log_prefix)) "jenkins_identify" |
306 val No_End = new Regex("""$.""") |
293 val No_End = new Regex("""$.""") |
307 val Isabelle_Version = List(new Regex("""^Isabelle version: (\S+)$""")) |
294 val Isabelle_Version = List(new Regex("""^Isabelle version: (\S+)$""")) |
308 val AFP_Version = List(new Regex("""^AFP version: (\S+)$""")) |
295 val AFP_Version = List(new Regex("""^AFP version: (\S+)$""")) |
309 } |
296 } |
310 |
297 |
311 object Isatest |
298 object Isatest { |
312 { |
|
313 val log_prefix = "isatest-makeall-" |
299 val log_prefix = "isatest-makeall-" |
314 val engine = "isatest" |
300 val engine = "isatest" |
315 val Start = new Regex("""^------------------- starting test --- (.+) --- (.+)$""") |
301 val Start = new Regex("""^------------------- starting test --- (.+) --- (.+)$""") |
316 val End = new Regex("""^------------------- test (?:successful|FAILED) --- (.+) --- .*$""") |
302 val End = new Regex("""^------------------- test (?:successful|FAILED) --- (.+) --- .*$""") |
317 val Isabelle_Version = List(new Regex("""^Isabelle version: (\S+)$""")) |
303 val Isabelle_Version = List(new Regex("""^Isabelle version: (\S+)$""")) |
318 } |
304 } |
319 |
305 |
320 object AFP_Test |
306 object AFP_Test { |
321 { |
|
322 val log_prefix = "afp-test-devel-" |
307 val log_prefix = "afp-test-devel-" |
323 val engine = "afp-test" |
308 val engine = "afp-test" |
324 val Start = new Regex("""^Start test(?: for .+)? at ([^,]+), (.*)$""") |
309 val Start = new Regex("""^Start test(?: for .+)? at ([^,]+), (.*)$""") |
325 val Start_Old = new Regex("""^Start test(?: for .+)? at ([^,]+)$""") |
310 val Start_Old = new Regex("""^Start test(?: for .+)? at ([^,]+)$""") |
326 val End = new Regex("""^End test on (.+), .+, elapsed time:.*$""") |
311 val End = new Regex("""^End test on (.+), .+, elapsed time:.*$""") |
327 val Isabelle_Version = List(new Regex("""^Isabelle version: .* -- hg id (\S+)$""")) |
312 val Isabelle_Version = List(new Regex("""^Isabelle version: .* -- hg id (\S+)$""")) |
328 val AFP_Version = List(new Regex("""^AFP version: .* -- hg id (\S+)$""")) |
313 val AFP_Version = List(new Regex("""^AFP version: .* -- hg id (\S+)$""")) |
329 val Bad_Init = new Regex("""^cp:.*: Disc quota exceeded$""") |
314 val Bad_Init = new Regex("""^cp:.*: Disc quota exceeded$""") |
330 } |
315 } |
331 |
316 |
332 object Jenkins |
317 object Jenkins { |
333 { |
|
334 val log_prefix = "jenkins_" |
318 val log_prefix = "jenkins_" |
335 val engine = "jenkins" |
319 val engine = "jenkins" |
336 val Host = new Regex("""^Building remotely on (\S+) \((\S+)\).*$""") |
320 val Host = new Regex("""^Building remotely on (\S+) \((\S+)\).*$""") |
337 val Start = new Regex("""^(?:Started by an SCM change|Started from command line by admin|).*$""") |
321 val Start = new Regex("""^(?:Started by an SCM change|Started from command line by admin|).*$""") |
338 val Start_Date = new Regex("""^Build started at (.+)$""") |
322 val Start_Date = new Regex("""^Build started at (.+)$""") |
346 new Regex("""^ISABELLE_CI_AFP_ID="(\w+)".*$""")) |
330 new Regex("""^ISABELLE_CI_AFP_ID="(\w+)".*$""")) |
347 val CONFIGURATION = "=== CONFIGURATION ===" |
331 val CONFIGURATION = "=== CONFIGURATION ===" |
348 val BUILD = "=== BUILD ===" |
332 val BUILD = "=== BUILD ===" |
349 } |
333 } |
350 |
334 |
351 private def parse_meta_info(log_file: Log_File): Meta_Info = |
335 private def parse_meta_info(log_file: Log_File): Meta_Info = { |
352 { |
|
353 def parse(engine: String, host: String, start: Date, |
336 def parse(engine: String, host: String, start: Date, |
354 End: Regex, Isabelle_Version: List[Regex], AFP_Version: List[Regex]): Meta_Info = |
337 End: Regex, Isabelle_Version: List[Regex], AFP_Version: List[Regex] |
355 { |
338 ): Meta_Info = { |
356 val build_id = |
339 val build_id = { |
357 { |
|
358 val prefix = proper_string(host) orElse proper_string(engine) getOrElse "build" |
340 val prefix = proper_string(host) orElse proper_string(engine) getOrElse "build" |
359 prefix + ":" + start.time.ms |
341 prefix + ":" + start.time.ms |
360 } |
342 } |
361 val build_engine = if (engine == "") Nil else List(Prop.build_engine.name -> engine) |
343 val build_engine = if (engine == "") Nil else List(Prop.build_engine.name -> engine) |
362 val build_host = if (host == "") Nil else List(Prop.build_host.name -> host) |
344 val build_host = if (host == "") Nil else List(Prop.build_host.name -> host) |
440 sources: Option[String] = None, |
421 sources: Option[String] = None, |
441 heap_size: Option[Long] = None, |
422 heap_size: Option[Long] = None, |
442 status: Option[Session_Status.Value] = None, |
423 status: Option[Session_Status.Value] = None, |
443 errors: List[String] = Nil, |
424 errors: List[String] = Nil, |
444 theory_timings: Map[String, Timing] = Map.empty, |
425 theory_timings: Map[String, Timing] = Map.empty, |
445 ml_statistics: List[Properties.T] = Nil) |
426 ml_statistics: List[Properties.T] = Nil |
446 { |
427 ) { |
447 def proper_groups: Option[String] = if (groups.isEmpty) None else Some(cat_lines(groups)) |
428 def proper_groups: Option[String] = if (groups.isEmpty) None else Some(cat_lines(groups)) |
448 def finished: Boolean = status == Some(Session_Status.finished) |
429 def finished: Boolean = status == Some(Session_Status.finished) |
449 def failed: Boolean = status == Some(Session_Status.failed) |
430 def failed: Boolean = status == Some(Session_Status.failed) |
450 } |
431 } |
451 |
432 |
452 object Build_Info |
433 object Build_Info { |
453 { |
|
454 val sessions_dummy: Map[String, Session_Entry] = |
434 val sessions_dummy: Map[String, Session_Entry] = |
455 Map("" -> Session_Entry(theory_timings = Map("" -> Timing.zero))) |
435 Map("" -> Session_Entry(theory_timings = Map("" -> Timing.zero))) |
456 } |
436 } |
457 |
437 |
458 sealed case class Build_Info(sessions: Map[String, Session_Entry]) |
438 sealed case class Build_Info(sessions: Map[String, Session_Entry]) { |
459 { |
|
460 def finished_sessions: List[String] = for ((a, b) <- sessions.toList if b.finished) yield a |
439 def finished_sessions: List[String] = for ((a, b) <- sessions.toList if b.finished) yield a |
461 def failed_sessions: List[String] = for ((a, b) <- sessions.toList if b.failed) yield a |
440 def failed_sessions: List[String] = for ((a, b) <- sessions.toList if b.failed) yield a |
462 } |
441 } |
463 |
442 |
464 private def parse_build_info(log_file: Log_File, parse_ml_statistics: Boolean): Build_Info = |
443 private def parse_build_info(log_file: Log_File, parse_ml_statistics: Boolean): Build_Info = { |
465 { |
444 object Chapter_Name { |
466 object Chapter_Name |
|
467 { |
|
468 def unapply(s: String): Some[(String, String)] = |
445 def unapply(s: String): Some[(String, String)] = |
469 space_explode('/', s) match { |
446 space_explode('/', s) match { |
470 case List(chapter, name) => Some((chapter, name)) |
447 case List(chapter, name) => Some((chapter, name)) |
471 case _ => Some(("", s)) |
448 case _ => Some(("", s)) |
472 } |
449 } |
482 new Regex("""^Timing (\S+) \((\d+) threads, (\d+\.\d+)s elapsed time, (\d+\.\d+)s cpu time, (\d+\.\d+)s GC time.*$""") |
459 new Regex("""^Timing (\S+) \((\d+) threads, (\d+\.\d+)s elapsed time, (\d+\.\d+)s cpu time, (\d+\.\d+)s GC time.*$""") |
483 val Session_Started = new Regex("""^(?:Running|Building) (\S+) \.\.\.$""") |
460 val Session_Started = new Regex("""^(?:Running|Building) (\S+) \.\.\.$""") |
484 val Sources = new Regex("""^Sources (\S+) (\S{""" + SHA1.digest_length + """})$""") |
461 val Sources = new Regex("""^Sources (\S+) (\S{""" + SHA1.digest_length + """})$""") |
485 val Heap = new Regex("""^Heap (\S+) \((\d+) bytes\)$""") |
462 val Heap = new Regex("""^Heap (\S+) \((\d+) bytes\)$""") |
486 |
463 |
487 object Theory_Timing |
464 object Theory_Timing { |
488 { |
|
489 def unapply(line: String): Option[(String, (String, Timing))] = |
465 def unapply(line: String): Option[(String, (String, Timing))] = |
490 Protocol.Theory_Timing_Marker.unapply(line.replace('~', '-')).map(log_file.parse_props) |
466 Protocol.Theory_Timing_Marker.unapply(line.replace('~', '-')).map(log_file.parse_props) |
491 match { |
467 match { |
492 case Some((SESSION_NAME, session) :: props) => |
468 case Some((SESSION_NAME, session) :: props) => |
493 for (theory <- Markup.Name.unapply(props)) |
469 for (theory <- Markup.Name.unapply(props)) |
612 session_timing: Properties.T, |
588 session_timing: Properties.T, |
613 command_timings: List[Properties.T], |
589 command_timings: List[Properties.T], |
614 theory_timings: List[Properties.T], |
590 theory_timings: List[Properties.T], |
615 ml_statistics: List[Properties.T], |
591 ml_statistics: List[Properties.T], |
616 task_statistics: List[Properties.T], |
592 task_statistics: List[Properties.T], |
617 errors: List[String]) |
593 errors: List[String] |
618 { |
594 ) { |
619 def error(s: String): Session_Info = |
595 def error(s: String): Session_Info = |
620 copy(errors = errors ::: List(s)) |
596 copy(errors = errors ::: List(s)) |
621 } |
597 } |
622 |
598 |
623 private def parse_session_info( |
599 private def parse_session_info( |
624 log_file: Log_File, |
600 log_file: Log_File, |
625 command_timings: Boolean, |
601 command_timings: Boolean, |
626 theory_timings: Boolean, |
602 theory_timings: Boolean, |
627 ml_statistics: Boolean, |
603 ml_statistics: Boolean, |
628 task_statistics: Boolean): Session_Info = |
604 task_statistics: Boolean |
629 { |
605 ): Session_Info = { |
630 Session_Info( |
606 Session_Info( |
631 session_timing = log_file.find_props(Protocol.Session_Timing_Marker) getOrElse Nil, |
607 session_timing = log_file.find_props(Protocol.Session_Timing_Marker) getOrElse Nil, |
632 command_timings = |
608 command_timings = |
633 if (command_timings) log_file.filter_props(Protocol.Command_Timing_Marker) else Nil, |
609 if (command_timings) log_file.filter_props(Protocol.Command_Timing_Marker) else Nil, |
634 theory_timings = |
610 theory_timings = |
710 build_log_table("ml_statistics", List(log_name, session_name, ml_statistics)) |
685 build_log_table("ml_statistics", List(log_name, session_name, ml_statistics)) |
711 |
686 |
712 |
687 |
713 /* AFP versions */ |
688 /* AFP versions */ |
714 |
689 |
715 val isabelle_afp_versions_table: SQL.Table = |
690 val isabelle_afp_versions_table: SQL.Table = { |
716 { |
|
717 val version1 = Prop.isabelle_version |
691 val version1 = Prop.isabelle_version |
718 val version2 = Prop.afp_version |
692 val version2 = Prop.afp_version |
719 build_log_table("isabelle_afp_versions", List(version1.make_primary_key, version2), |
693 build_log_table("isabelle_afp_versions", List(version1.make_primary_key, version2), |
720 SQL.select(List(version1, version2), distinct = true) + meta_info_table + |
694 SQL.select(List(version1, version2), distinct = true) + meta_info_table + |
721 " WHERE " + version1.defined + " AND " + version2.defined) |
695 " WHERE " + version1.defined + " AND " + version2.defined) |
726 |
700 |
727 def pull_date(afp: Boolean = false): SQL.Column = |
701 def pull_date(afp: Boolean = false): SQL.Column = |
728 if (afp) SQL.Column.date("afp_pull_date") |
702 if (afp) SQL.Column.date("afp_pull_date") |
729 else SQL.Column.date("pull_date") |
703 else SQL.Column.date("pull_date") |
730 |
704 |
731 def pull_date_table(afp: Boolean = false): SQL.Table = |
705 def pull_date_table(afp: Boolean = false): SQL.Table = { |
732 { |
|
733 val (name, versions) = |
706 val (name, versions) = |
734 if (afp) ("afp_pull_date", List(Prop.isabelle_version, Prop.afp_version)) |
707 if (afp) ("afp_pull_date", List(Prop.isabelle_version, Prop.afp_version)) |
735 else ("pull_date", List(Prop.isabelle_version)) |
708 else ("pull_date", List(Prop.isabelle_version)) |
736 |
709 |
737 build_log_table(name, versions.map(_.make_primary_key) ::: List(pull_date(afp)), |
710 build_log_table(name, versions.map(_.make_primary_key) ::: List(pull_date(afp)), |
767 else if (rev == "" && rev2 != "") " OR " + eq2 |
742 else if (rev == "" && rev2 != "") " OR " + eq2 |
768 else if (rev != "" && rev2 != "") " OR (" + eq1 + " AND " + eq2 + ")" |
743 else if (rev != "" && rev2 != "") " OR (" + eq1 + " AND " + eq2 + ")" |
769 else ""))) |
744 else ""))) |
770 } |
745 } |
771 |
746 |
772 def select_recent_log_names(days: Int): SQL.Source = |
747 def select_recent_log_names(days: Int): SQL.Source = { |
773 { |
|
774 val table1 = meta_info_table |
748 val table1 = meta_info_table |
775 val table2 = recent_pull_date_table(days) |
749 val table2 = recent_pull_date_table(days) |
776 table1.select(List(log_name), distinct = true) + SQL.join_inner + table2.query_named + |
750 table1.select(List(log_name), distinct = true) + SQL.join_inner + table2.query_named + |
777 " ON " + Prop.isabelle_version(table1) + " = " + Prop.isabelle_version(table2) |
751 " ON " + Prop.isabelle_version(table1) + " = " + Prop.isabelle_version(table2) |
778 } |
752 } |
779 |
753 |
780 def select_recent_versions(days: Int, |
754 def select_recent_versions( |
781 rev: String = "", afp_rev: Option[String] = None, sql: SQL.Source = ""): SQL.Source = |
755 days: Int, |
782 { |
756 rev: String = "", |
|
757 afp_rev: Option[String] = None, |
|
758 sql: SQL.Source = "" |
|
759 ): SQL.Source = { |
783 val afp = afp_rev.isDefined |
760 val afp = afp_rev.isDefined |
784 val version = Prop.isabelle_version |
761 val version = Prop.isabelle_version |
785 val table1 = recent_pull_date_table(days, rev = rev, afp_rev = afp_rev) |
762 val table1 = recent_pull_date_table(days, rev = rev, afp_rev = afp_rev) |
786 val table2 = meta_info_table |
763 val table2 = meta_info_table |
787 val aux_table = SQL.Table("aux", table2.columns, table2.select(sql = sql)) |
764 val aux_table = SQL.Table("aux", table2.columns, table2.select(sql = sql)) |
844 /* database access */ |
820 /* database access */ |
845 |
821 |
846 def store(options: Options, cache: XML.Cache = XML.Cache.make()): Store = |
822 def store(options: Options, cache: XML.Cache = XML.Cache.make()): Store = |
847 new Store(options, cache) |
823 new Store(options, cache) |
848 |
824 |
849 class Store private[Build_Log](options: Options, val cache: XML.Cache) |
825 class Store private[Build_Log](options: Options, val cache: XML.Cache) { |
850 { |
|
851 def open_database( |
826 def open_database( |
852 user: String = options.string("build_log_database_user"), |
827 user: String = options.string("build_log_database_user"), |
853 password: String = options.string("build_log_database_password"), |
828 password: String = options.string("build_log_database_password"), |
854 database: String = options.string("build_log_database_name"), |
829 database: String = options.string("build_log_database_name"), |
855 host: String = options.string("build_log_database_host"), |
830 host: String = options.string("build_log_database_host"), |
856 port: Int = options.int("build_log_database_port"), |
831 port: Int = options.int("build_log_database_port"), |
857 ssh_host: String = options.string("build_log_ssh_host"), |
832 ssh_host: String = options.string("build_log_ssh_host"), |
858 ssh_user: String = options.string("build_log_ssh_user"), |
833 ssh_user: String = options.string("build_log_ssh_user"), |
859 ssh_port: Int = options.int("build_log_ssh_port")): PostgreSQL.Database = |
834 ssh_port: Int = options.int("build_log_ssh_port") |
860 { |
835 ): PostgreSQL.Database = { |
861 PostgreSQL.open_database( |
836 PostgreSQL.open_database( |
862 user = user, password = password, database = database, host = host, port = port, |
837 user = user, password = password, database = database, host = host, port = port, |
863 ssh = |
838 ssh = |
864 if (ssh_host == "") None |
839 if (ssh_host == "") None |
865 else Some(SSH.open_session(options, host = ssh_host, user = ssh_user, port = ssh_port)), |
840 else Some(SSH.open_session(options, host = ssh_host, user = ssh_user, port = ssh_port)), |
866 ssh_close = true) |
841 ssh_close = true) |
867 } |
842 } |
868 |
843 |
869 def update_database( |
844 def update_database( |
870 db: PostgreSQL.Database, dirs: List[Path], ml_statistics: Boolean = false): Unit = |
845 db: PostgreSQL.Database, dirs: List[Path], ml_statistics: Boolean = false): Unit = { |
871 { |
|
872 val log_files = |
846 val log_files = |
873 dirs.flatMap(dir => |
847 dirs.flatMap(dir => |
874 File.find_files(dir.file, pred = Log_File.is_log(_), follow_links = true)) |
848 File.find_files(dir.file, pred = Log_File.is_log(_), follow_links = true)) |
875 write_info(db, log_files, ml_statistics = ml_statistics) |
849 write_info(db, log_files, ml_statistics = ml_statistics) |
876 |
850 |
877 db.create_view(Data.pull_date_table()) |
851 db.create_view(Data.pull_date_table()) |
878 db.create_view(Data.pull_date_table(afp = true)) |
852 db.create_view(Data.pull_date_table(afp = true)) |
879 db.create_view(Data.universal_table) |
853 db.create_view(Data.universal_table) |
880 } |
854 } |
881 |
855 |
882 def snapshot_database(db: PostgreSQL.Database, sqlite_database: Path, |
856 def snapshot_database( |
883 days: Int = 100, ml_statistics: Boolean = false): Unit = |
857 db: PostgreSQL.Database, |
884 { |
858 sqlite_database: Path, |
|
859 days: Int = 100, |
|
860 ml_statistics: Boolean = false |
|
861 ): Unit = { |
885 Isabelle_System.make_directory(sqlite_database.dir) |
862 Isabelle_System.make_directory(sqlite_database.dir) |
886 sqlite_database.file.delete |
863 sqlite_database.file.delete |
887 |
864 |
888 using(SQLite.open_database(sqlite_database))(db2 => |
865 using(SQLite.open_database(sqlite_database))(db2 => { |
889 { |
|
890 db.transaction { |
866 db.transaction { |
891 db2.transaction { |
867 db2.transaction { |
892 // main content |
868 // main content |
893 db2.create_table(Data.meta_info_table) |
869 db2.create_table(Data.meta_info_table) |
894 db2.create_table(Data.sessions_table) |
870 db2.create_table(Data.sessions_table) |
910 read_build_info(db, log_name, ml_statistics = true)) |
886 read_build_info(db, log_name, ml_statistics = true)) |
911 } |
887 } |
912 } |
888 } |
913 |
889 |
914 // pull_date |
890 // pull_date |
915 for (afp <- List(false, true)) |
891 for (afp <- List(false, true)) { |
916 { |
|
917 val afp_rev = if (afp) Some("") else None |
892 val afp_rev = if (afp) Some("") else None |
918 val table = Data.pull_date_table(afp) |
893 val table = Data.pull_date_table(afp) |
919 db2.create_table(table) |
894 db2.create_table(table) |
920 db2.using_statement(table.insert())(stmt2 => |
895 db2.using_statement(table.insert())(stmt2 => { |
921 { |
|
922 db.using_statement( |
896 db.using_statement( |
923 Data.recent_pull_date_table(days, afp_rev = afp_rev).query)(stmt => |
897 Data.recent_pull_date_table(days, afp_rev = afp_rev).query)(stmt => { |
924 { |
|
925 val res = stmt.execute_query() |
898 val res = stmt.execute_query() |
926 while (res.next()) { |
899 while (res.next()) { |
927 for ((c, i) <- table.columns.zipWithIndex) { |
900 for ((c, i) <- table.columns.zipWithIndex) { |
928 stmt2.string(i + 1) = res.get_string(c) |
901 stmt2.string(i + 1) = res.get_string(c) |
929 } |
902 } |
943 |
916 |
944 def domain(db: SQL.Database, table: SQL.Table, column: SQL.Column): Set[String] = |
917 def domain(db: SQL.Database, table: SQL.Table, column: SQL.Column): Set[String] = |
945 db.using_statement(table.select(List(column), distinct = true))(stmt => |
918 db.using_statement(table.select(List(column), distinct = true))(stmt => |
946 stmt.execute_query().iterator(_.string(column)).toSet) |
919 stmt.execute_query().iterator(_.string(column)).toSet) |
947 |
920 |
948 def update_meta_info(db: SQL.Database, log_name: String, meta_info: Meta_Info): Unit = |
921 def update_meta_info(db: SQL.Database, log_name: String, meta_info: Meta_Info): Unit = { |
949 { |
|
950 val table = Data.meta_info_table |
922 val table = Data.meta_info_table |
951 db.using_statement(db.insert_permissive(table))(stmt => |
923 db.using_statement(db.insert_permissive(table))(stmt => { |
952 { |
|
953 stmt.string(1) = log_name |
924 stmt.string(1) = log_name |
954 for ((c, i) <- table.columns.tail.zipWithIndex) { |
925 for ((c, i) <- table.columns.tail.zipWithIndex) { |
955 if (c.T == SQL.Type.Date) |
926 if (c.T == SQL.Type.Date) |
956 stmt.date(i + 2) = meta_info.get_date(c) |
927 stmt.date(i + 2) = meta_info.get_date(c) |
957 else |
928 else |
1014 stmt.execute() |
981 stmt.execute() |
1015 } |
982 } |
1016 }) |
983 }) |
1017 } |
984 } |
1018 |
985 |
1019 def update_ml_statistics(db: SQL.Database, log_name: String, build_info: Build_Info): Unit = |
986 def update_ml_statistics(db: SQL.Database, log_name: String, build_info: Build_Info): Unit = { |
1020 { |
|
1021 val table = Data.ml_statistics_table |
987 val table = Data.ml_statistics_table |
1022 db.using_statement(db.insert_permissive(table))(stmt => |
988 db.using_statement(db.insert_permissive(table))(stmt => { |
1023 { |
|
1024 val ml_stats: List[(String, Option[Bytes])] = |
989 val ml_stats: List[(String, Option[Bytes])] = |
1025 Par_List.map[(String, Session_Entry), (String, Option[Bytes])]( |
990 Par_List.map[(String, Session_Entry), (String, Option[Bytes])]( |
1026 { case (a, b) => (a, Properties.compress(b.ml_statistics, cache = cache.xz).proper) }, |
991 { case (a, b) => (a, Properties.compress(b.ml_statistics, cache = cache.xz).proper) }, |
1027 build_info.sessions.iterator.filter(p => p._2.ml_statistics.nonEmpty).toList) |
992 build_info.sessions.iterator.filter(p => p._2.ml_statistics.nonEmpty).toList) |
1028 val entries = if (ml_stats.nonEmpty) ml_stats else List("" -> None) |
993 val entries = if (ml_stats.nonEmpty) ml_stats else List("" -> None) |
1033 stmt.execute() |
998 stmt.execute() |
1034 } |
999 } |
1035 }) |
1000 }) |
1036 } |
1001 } |
1037 |
1002 |
1038 def write_info(db: SQL.Database, files: List[JFile], ml_statistics: Boolean = false): Unit = |
1003 def write_info(db: SQL.Database, files: List[JFile], ml_statistics: Boolean = false): Unit = { |
1039 { |
1004 abstract class Table_Status(table: SQL.Table) { |
1040 abstract class Table_Status(table: SQL.Table) |
|
1041 { |
|
1042 db.create_table(table) |
1005 db.create_table(table) |
1043 private var known: Set[String] = domain(db, table, Data.log_name) |
1006 private var known: Set[String] = domain(db, table, Data.log_name) |
1044 |
1007 |
1045 def required(file: JFile): Boolean = !known(Log_File.plain_name(file.getName)) |
1008 def required(file: JFile): Boolean = !known(Log_File.plain_name(file.getName)) |
1046 |
1009 |
1047 def update_db(db: SQL.Database, log_file: Log_File): Unit |
1010 def update_db(db: SQL.Database, log_file: Log_File): Unit |
1048 def update(log_file: Log_File): Unit = |
1011 def update(log_file: Log_File): Unit = { |
1049 { |
|
1050 if (!known(log_file.name)) { |
1012 if (!known(log_file.name)) { |
1051 update_db(db, log_file) |
1013 update_db(db, log_file) |
1052 known += log_file.name |
1014 known += log_file.name |
1053 } |
1015 } |
1054 } |
1016 } |
1075 } |
1037 } |
1076 }) |
1038 }) |
1077 |
1039 |
1078 for (file_group <- |
1040 for (file_group <- |
1079 files.filter(file => status.exists(_.required(file))). |
1041 files.filter(file => status.exists(_.required(file))). |
1080 grouped(options.int("build_log_transaction_size") max 1)) |
1042 grouped(options.int("build_log_transaction_size") max 1)) { |
1081 { |
|
1082 val log_files = Par_List.map[JFile, Log_File](Log_File.apply, file_group) |
1043 val log_files = Par_List.map[JFile, Log_File](Log_File.apply, file_group) |
1083 db.transaction { log_files.foreach(log_file => status.foreach(_.update(log_file))) } |
1044 db.transaction { log_files.foreach(log_file => status.foreach(_.update(log_file))) } |
1084 } |
1045 } |
1085 } |
1046 } |
1086 |
1047 |
1087 def read_meta_info(db: SQL.Database, log_name: String): Option[Meta_Info] = |
1048 def read_meta_info(db: SQL.Database, log_name: String): Option[Meta_Info] = { |
1088 { |
|
1089 val table = Data.meta_info_table |
1049 val table = Data.meta_info_table |
1090 val columns = table.columns.tail |
1050 val columns = table.columns.tail |
1091 db.using_statement(table.select(columns, Data.log_name.where_equal(log_name)))(stmt => |
1051 db.using_statement(table.select(columns, Data.log_name.where_equal(log_name)))(stmt => { |
1092 { |
|
1093 val res = stmt.execute_query() |
1052 val res = stmt.execute_query() |
1094 if (!res.next()) None |
1053 if (!res.next()) None |
1095 else { |
1054 else { |
1096 val results = |
1055 val results = |
1097 columns.map(c => c.name -> |
1056 columns.map(c => c.name -> |