Artifact e4b4a90ad76089fd89c6b64d49ea5fa423b02a7f
File
tools/cvs2fossil/lib/c2f_state.tcl
part of check-in
[74854a30b8]
- Added ability to declare indices on tables in the persistent state. Used this to declare indices on critical columns. Slows down the transactions saving changesets, this however is made up when it comes to successor/predecessor retrieval of changesets.
by
aku on
2007-12-02 03:40:56.
## -*- tcl -*-
# # ## ### ##### ######## ############# #####################
## Copyright (c) 2007 Andreas Kupries.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution.
#
# This software consists of voluntary contributions made by many
# individuals. For exact contribution history, see the revision
# history and logs, available at http://fossil-scm.hwaci.com/fossil
# # ## ### ##### ######## ############# #####################
## State manager. Maintains the sqlite database used by all the other
## parts of the system, especially the passes and their support code,
## to persist and restore their state across invokations.
# # ## ### ##### ######## ############# #####################
## Requirements
package require Tcl 8.4 ; # Required runtime.
package require snit ; # OO system.
package require fileutil ; # File operations.
package require sqlite3 ; # Database access.
package require vc::tools::trouble ; # Error reporting.
package require vc::tools::log ; # User feedback.
# # ## ### ##### ######## ############# #####################
##
snit::type ::vc::fossil::import::cvs::state {
# # ## ### ##### ######## #############
## Public API
typemethod use {path} {
# Immediate validation. There are are two possibilities to
# consider. The path exists or it doesn't.
# In the first case it has to be a readable and writable file,
# and it has to be a proper sqlite database. Further checks
# regarding the required tables will be done later, by the
# passes, during their setup.
# In the second case we have to be able to create the file,
# and check that. This is done by opening it, sqlite will then
# try to create it, and may fail.
if {[file exists $path]} {
if {![fileutil::test $path frw msg {cvs2fossil state}]} {
trouble fatal $msg
return
}
}
if {[catch {
sqlite3 ${type}::TEMP $path
} res]} {
trouble fatal $res
return
}
# A previously defined state database is closed before
# committing to the new definition. We do not store the path
# itself, this ensures that the file is _not_ cleaned up after
# a run.
set mystate ${type}::STATE
set mypath {}
catch { $mystate close }
rename ${type}::TEMP $mystate
log write 2 state "is $path"
return
}
typemethod setup {} {
# If, and only if no state database was defined by the user
# then it is now the time to create our own using a tempfile.
if {$mystate ne ""} return
set mypath [fileutil::tempfile cvs2fossil_state_]
set mystate ${type}::STATE
sqlite3 $mystate $mypath
log write 2 state "using $mypath"
return
}
typemethod release {} {
log write 2 state release
${type}::STATE close
if {$mypath eq ""} return
file delete $mypath
return
}
typemethod writing {name definition {indices {}}} {
# Method for a user to declare a table its needs for storing
# persistent state, and the expected structure. A possibly
# previously existing definition is dropped.
log write 1 state "writing $name" ; # TODO move to level 5 or so
$mystate transaction {
catch { $mystate eval "DROP TABLE $name" }
$mystate eval "CREATE TABLE $name ( $definition )"
set id 0
foreach columns $indices {
log write 1 state "index $name$id" ; # TODO move to level 5 or so
$mystate eval "CREATE INDEX ${name}$id ON ${name} ( [join $columns ,] )"
incr id
}
}
return
}
typemethod reading {name} {
log write 1 state "reading $name" ; # TODO move to level 5 or so
# Method for a user to declare a table it wishes to read
# from. A missing table is an internal error causing an
# immediate exit.
set found [llength [$mystate eval {
SELECT name
FROM sqlite_master
WHERE type = 'table'
AND name = $name
;
}]]
# No assert, would cause cycle in package dependencies
if {$found} return
trouble internal "The required table \"$name\" is not defined."
# Not reached
return
}
typemethod discard {name} {
# Method for a user to remove outdated information from the
# persistent state, table by table.
log write 1 state "discard $name" ; # TODO move to level 5 or so
$mystate transaction {
catch { $mystate eval "DROP TABLE $name" }
}
return
}
typemethod run {args} {
return [uplevel 1 [linsert $args 0 $mystate eval]]
}
typemethod one {args} {
return [lindex [uplevel 1 [linsert $args 0 $mystate eval]] 0]
}
typemethod transaction {script} {
return [uplevel 1 [list $mystate transaction $script]]
}
typemethod id {} {
return [$mystate last_insert_rowid]
}
# # ## ### ##### ######## #############
## State
typevariable mystate {} ; # Sqlite database (command) holding the converter state.
typevariable mypath {} ; # Path to the database, for cleanup of a temp database.
# # ## ### ##### ######## #############
## Internal methods
# # ## ### ##### ######## #############
## Configuration
pragma -hasinstances no ; # singleton
pragma -hastypeinfo no ; # no introspection
pragma -hastypedestroy no ; # immortal
# # ## ### ##### ######## #############
}
namespace eval ::vc::fossil::import::cvs {
namespace export state
namespace eval state {
namespace import ::vc::tools::trouble
namespace import ::vc::tools::log
log register state
}
}
# # ## ### ##### ######## ############# #####################
## Ready
package provide vc::fossil::import::cvs::state 1.0
return