|
eb656de…
|
aku
|
1 |
## -*- tcl -*- |
|
eb656de…
|
aku
|
2 |
# # ## ### ##### ######## ############# ##################### |
|
eb656de…
|
aku
|
3 |
## Copyright (c) 2007 Andreas Kupries. |
|
eb656de…
|
aku
|
4 |
# |
|
eb656de…
|
aku
|
5 |
# This software is licensed as described in the file LICENSE, which |
|
eb656de…
|
aku
|
6 |
# you should have received as part of this distribution. |
|
eb656de…
|
aku
|
7 |
# |
|
eb656de…
|
aku
|
8 |
# This software consists of voluntary contributions made by many |
|
eb656de…
|
aku
|
9 |
# individuals. For exact contribution history, see the revision |
|
eb656de…
|
aku
|
10 |
# history and logs, available at http://fossil-scm.hwaci.com/fossil |
|
eb656de…
|
aku
|
11 |
# # ## ### ##### ######## ############# ##################### |
|
eb656de…
|
aku
|
12 |
|
|
eb656de…
|
aku
|
13 |
## State manager. Maintains the sqlite database used by all the other |
|
eb656de…
|
aku
|
14 |
## parts of the system, especially the passes and their support code, |
|
eb656de…
|
aku
|
15 |
## to persist and restore their state across invokations. |
|
eb656de…
|
aku
|
16 |
|
|
eb656de…
|
aku
|
17 |
# # ## ### ##### ######## ############# ##################### |
|
eb656de…
|
aku
|
18 |
## Requirements |
|
eb656de…
|
aku
|
19 |
|
|
eb656de…
|
aku
|
20 |
package require Tcl 8.4 ; # Required runtime. |
|
eb656de…
|
aku
|
21 |
package require snit ; # OO system. |
|
eb656de…
|
aku
|
22 |
package require fileutil ; # File operations. |
|
eb656de…
|
aku
|
23 |
package require sqlite3 ; # Database access. |
|
eb656de…
|
aku
|
24 |
package require vc::tools::trouble ; # Error reporting. |
|
eb656de…
|
aku
|
25 |
package require vc::tools::log ; # User feedback. |
|
eb656de…
|
aku
|
26 |
|
|
eb656de…
|
aku
|
27 |
# # ## ### ##### ######## ############# ##################### |
|
b679ca3…
|
aku
|
28 |
## |
|
eb656de…
|
aku
|
29 |
|
|
eb656de…
|
aku
|
30 |
snit::type ::vc::fossil::import::cvs::state { |
|
eb656de…
|
aku
|
31 |
# # ## ### ##### ######## ############# |
|
eb656de…
|
aku
|
32 |
## Public API |
|
eb656de…
|
aku
|
33 |
|
|
e288af3…
|
aku
|
34 |
typemethod usedb {path} { |
|
eb656de…
|
aku
|
35 |
# Immediate validation. There are are two possibilities to |
|
eb656de…
|
aku
|
36 |
# consider. The path exists or it doesn't. |
|
eb656de…
|
aku
|
37 |
|
|
eb656de…
|
aku
|
38 |
# In the first case it has to be a readable and writable file, |
|
eb656de…
|
aku
|
39 |
# and it has to be a proper sqlite database. Further checks |
|
eb656de…
|
aku
|
40 |
# regarding the required tables will be done later, by the |
|
eb656de…
|
aku
|
41 |
# passes, during their setup. |
|
eb656de…
|
aku
|
42 |
|
|
eb656de…
|
aku
|
43 |
# In the second case we have to be able to create the file, |
|
eb656de…
|
aku
|
44 |
# and check that. This is done by opening it, sqlite will then |
|
eb656de…
|
aku
|
45 |
# try to create it, and may fail. |
|
eb656de…
|
aku
|
46 |
|
|
7208c7a…
|
mjanssen
|
47 |
if {[::file exists $path]} { |
|
eb656de…
|
aku
|
48 |
if {![fileutil::test $path frw msg {cvs2fossil state}]} { |
|
eb656de…
|
aku
|
49 |
trouble fatal $msg |
|
eb656de…
|
aku
|
50 |
return |
|
eb656de…
|
aku
|
51 |
} |
|
eb656de…
|
aku
|
52 |
} |
|
eb656de…
|
aku
|
53 |
|
|
eb656de…
|
aku
|
54 |
if {[catch { |
|
eb656de…
|
aku
|
55 |
sqlite3 ${type}::TEMP $path |
|
7208c7a…
|
mjanssen
|
56 |
${type}::TEMP eval {PRAGMA synchronous=OFF;} |
|
eb656de…
|
aku
|
57 |
} res]} { |
|
eb656de…
|
aku
|
58 |
trouble fatal $res |
|
eb656de…
|
aku
|
59 |
return |
|
eb656de…
|
aku
|
60 |
} |
|
eb656de…
|
aku
|
61 |
|
|
eb656de…
|
aku
|
62 |
# A previously defined state database is closed before |
|
eb656de…
|
aku
|
63 |
# committing to the new definition. We do not store the path |
|
eb656de…
|
aku
|
64 |
# itself, this ensures that the file is _not_ cleaned up after |
|
eb656de…
|
aku
|
65 |
# a run. |
|
eb656de…
|
aku
|
66 |
|
|
fb1e36d…
|
aku
|
67 |
set mystate ${type}::STATE |
|
fb1e36d…
|
aku
|
68 |
set mypath {} |
|
fb1e36d…
|
aku
|
69 |
|
|
fb1e36d…
|
aku
|
70 |
catch { $mystate close } |
|
fb1e36d…
|
aku
|
71 |
rename ${type}::TEMP $mystate |
|
eb656de…
|
aku
|
72 |
|
|
fb1e36d…
|
aku
|
73 |
log write 2 state "is $path" |
|
eb656de…
|
aku
|
74 |
return |
|
eb656de…
|
aku
|
75 |
} |
|
eb656de…
|
aku
|
76 |
|
|
eb656de…
|
aku
|
77 |
typemethod setup {} { |
|
eb656de…
|
aku
|
78 |
# If, and only if no state database was defined by the user |
|
eb656de…
|
aku
|
79 |
# then it is now the time to create our own using a tempfile. |
|
eb656de…
|
aku
|
80 |
|
|
fb1e36d…
|
aku
|
81 |
if {$mystate ne ""} return |
|
eb656de…
|
aku
|
82 |
|
|
eb656de…
|
aku
|
83 |
set mypath [fileutil::tempfile cvs2fossil_state_] |
|
fb1e36d…
|
aku
|
84 |
set mystate ${type}::STATE |
|
fb1e36d…
|
aku
|
85 |
sqlite3 $mystate $mypath |
|
7208c7a…
|
mjanssen
|
86 |
$mystate eval {PRAGMA synchronous=OFF;} |
|
eb656de…
|
aku
|
87 |
|
|
eb656de…
|
aku
|
88 |
log write 2 state "using $mypath" |
|
eb656de…
|
aku
|
89 |
return |
|
eb656de…
|
aku
|
90 |
} |
|
eb656de…
|
aku
|
91 |
|
|
eb656de…
|
aku
|
92 |
typemethod release {} { |
|
eb656de…
|
aku
|
93 |
log write 2 state release |
|
eb656de…
|
aku
|
94 |
${type}::STATE close |
|
eb656de…
|
aku
|
95 |
if {$mypath eq ""} return |
|
7208c7a…
|
mjanssen
|
96 |
::file delete $mypath |
|
fb1e36d…
|
aku
|
97 |
return |
|
fb1e36d…
|
aku
|
98 |
} |
|
fb1e36d…
|
aku
|
99 |
|
|
e288af3…
|
aku
|
100 |
# Declare a table needed for the storing of persistent state, and |
|
e288af3…
|
aku
|
101 |
# its structure. A possibly previously existing definition is |
|
e288af3…
|
aku
|
102 |
# dropped. To be used when a table is needed and not assumed to |
|
e288af3…
|
aku
|
103 |
# exist from previous passes. |
|
10f9d51…
|
aku
|
104 |
|
|
e288af3…
|
aku
|
105 |
typemethod extend {name definition {indices {}}} { |
|
e288af3…
|
aku
|
106 |
log write 5 state "extend $name" |
|
e288af3…
|
aku
|
107 |
Save "extend $name ================================" |
|
fb1e36d…
|
aku
|
108 |
|
|
fb1e36d…
|
aku
|
109 |
$mystate transaction { |
|
fb1e36d…
|
aku
|
110 |
catch { $mystate eval "DROP TABLE $name" } |
|
fb1e36d…
|
aku
|
111 |
$mystate eval "CREATE TABLE $name ( $definition )" |
|
74854a3…
|
aku
|
112 |
|
|
74854a3…
|
aku
|
113 |
set id 0 |
|
74854a3…
|
aku
|
114 |
foreach columns $indices { |
|
e288af3…
|
aku
|
115 |
log write 5 state "index $name$id" |
|
74854a3…
|
aku
|
116 |
|
|
74854a3…
|
aku
|
117 |
$mystate eval "CREATE INDEX ${name}$id ON ${name} ( [join $columns ,] )" |
|
74854a3…
|
aku
|
118 |
incr id |
|
74854a3…
|
aku
|
119 |
} |
|
fb1e36d…
|
aku
|
120 |
} |
|
fb1e36d…
|
aku
|
121 |
return |
|
fb1e36d…
|
aku
|
122 |
} |
|
fb1e36d…
|
aku
|
123 |
|
|
e288af3…
|
aku
|
124 |
# Declare that a table is needed for reading from and/or storing |
|
e288af3…
|
aku
|
125 |
# to persistent state, and is assumed to already exist. A missing |
|
e288af3…
|
aku
|
126 |
# table is an internal error causing an immediate exit. |
|
10f9d51…
|
aku
|
127 |
|
|
e288af3…
|
aku
|
128 |
typemethod use {name} { |
|
e288af3…
|
aku
|
129 |
log write 5 state "use $name" |
|
e288af3…
|
aku
|
130 |
Save "use $name ===================================" |
|
fb1e36d…
|
aku
|
131 |
|
|
fb1e36d…
|
aku
|
132 |
set found [llength [$mystate eval { |
|
fb1e36d…
|
aku
|
133 |
SELECT name |
|
fb1e36d…
|
aku
|
134 |
FROM sqlite_master |
|
fb1e36d…
|
aku
|
135 |
WHERE type = 'table' |
|
fb1e36d…
|
aku
|
136 |
AND name = $name |
|
fb1e36d…
|
aku
|
137 |
; |
|
fb1e36d…
|
aku
|
138 |
}]] |
|
fb1e36d…
|
aku
|
139 |
|
|
47d52d1…
|
aku
|
140 |
# No assert, would cause cycle in package dependencies |
|
fb1e36d…
|
aku
|
141 |
if {$found} return |
|
fb1e36d…
|
aku
|
142 |
trouble internal "The required table \"$name\" is not defined." |
|
fb1e36d…
|
aku
|
143 |
# Not reached |
|
fb1e36d…
|
aku
|
144 |
return |
|
fb1e36d…
|
aku
|
145 |
} |
|
fb1e36d…
|
aku
|
146 |
|
|
ae19c0f…
|
aku
|
147 |
typemethod discard {name} { |
|
ae19c0f…
|
aku
|
148 |
# Method for a user to remove outdated information from the |
|
ae19c0f…
|
aku
|
149 |
# persistent state, table by table. |
|
ae19c0f…
|
aku
|
150 |
|
|
e288af3…
|
aku
|
151 |
log write 5 state "discard $name" |
|
ae19c0f…
|
aku
|
152 |
|
|
ae19c0f…
|
aku
|
153 |
$mystate transaction { |
|
ae19c0f…
|
aku
|
154 |
catch { $mystate eval "DROP TABLE $name" } |
|
ae19c0f…
|
aku
|
155 |
} |
|
ae19c0f…
|
aku
|
156 |
return |
|
ae19c0f…
|
aku
|
157 |
} |
|
ae19c0f…
|
aku
|
158 |
|
|
fb1e36d…
|
aku
|
159 |
typemethod run {args} { |
|
e288af3…
|
aku
|
160 |
Save $args |
|
fb1e36d…
|
aku
|
161 |
return [uplevel 1 [linsert $args 0 $mystate eval]] |
|
6559f32…
|
aku
|
162 |
} |
|
6559f32…
|
aku
|
163 |
|
|
6559f32…
|
aku
|
164 |
typemethod foreachrow {sql script} { |
|
6559f32…
|
aku
|
165 |
Save $sql |
|
6559f32…
|
aku
|
166 |
uplevel 1 [list $mystate eval $sql $script] |
|
6559f32…
|
aku
|
167 |
return |
|
e288af3…
|
aku
|
168 |
} |
|
e288af3…
|
aku
|
169 |
|
|
96b7bfb…
|
aku
|
170 |
typemethod one {args} { |
|
e288af3…
|
aku
|
171 |
Save $args |
|
e288af3…
|
aku
|
172 |
return [uplevel 1 [linsert $args 0 $mystate onecolumn]] |
|
042d54b…
|
aku
|
173 |
} |
|
042d54b…
|
aku
|
174 |
|
|
042d54b…
|
aku
|
175 |
typemethod transaction {script} { |
|
042d54b…
|
aku
|
176 |
return [uplevel 1 [list $mystate transaction $script]] |
|
042d54b…
|
aku
|
177 |
} |
|
042d54b…
|
aku
|
178 |
|
|
042d54b…
|
aku
|
179 |
typemethod id {} { |
|
042d54b…
|
aku
|
180 |
return [$mystate last_insert_rowid] |
|
042d54b…
|
aku
|
181 |
} |
|
042d54b…
|
aku
|
182 |
|
|
e288af3…
|
aku
|
183 |
typemethod savequeriesto {path} { |
|
e288af3…
|
aku
|
184 |
set mysavepath $path |
|
e288af3…
|
aku
|
185 |
return |
|
e288af3…
|
aku
|
186 |
} |
|
e288af3…
|
aku
|
187 |
|
|
e288af3…
|
aku
|
188 |
# # ## ### ##### ######## ############# |
|
e288af3…
|
aku
|
189 |
|
|
e288af3…
|
aku
|
190 |
proc Save {text} { |
|
e288af3…
|
aku
|
191 |
::variable mysavepath |
|
e288af3…
|
aku
|
192 |
if {$mysavepath eq ""} return |
|
e288af3…
|
aku
|
193 |
fileutil::appendToFile $mysavepath $text\n\n |
|
e288af3…
|
aku
|
194 |
return |
|
e288af3…
|
aku
|
195 |
} |
|
e288af3…
|
aku
|
196 |
|
|
eb656de…
|
aku
|
197 |
# # ## ### ##### ######## ############# |
|
eb656de…
|
aku
|
198 |
## State |
|
eb656de…
|
aku
|
199 |
|
|
e288af3…
|
aku
|
200 |
typevariable mystate {} ; # Sqlite database (command) holding the converter state. |
|
e288af3…
|
aku
|
201 |
typevariable mypath {} ; # Path to the database, for cleanup of a temp database. |
|
e288af3…
|
aku
|
202 |
typevariable mysavepath {} ; # Path where to save queries for introspection. |
|
eb656de…
|
aku
|
203 |
|
|
eb656de…
|
aku
|
204 |
# # ## ### ##### ######## ############# |
|
eb656de…
|
aku
|
205 |
## Internal methods |
|
eb656de…
|
aku
|
206 |
|
|
eb656de…
|
aku
|
207 |
|
|
eb656de…
|
aku
|
208 |
# # ## ### ##### ######## ############# |
|
eb656de…
|
aku
|
209 |
## Configuration |
|
eb656de…
|
aku
|
210 |
|
|
eb656de…
|
aku
|
211 |
pragma -hasinstances no ; # singleton |
|
eb656de…
|
aku
|
212 |
pragma -hastypeinfo no ; # no introspection |
|
eb656de…
|
aku
|
213 |
pragma -hastypedestroy no ; # immortal |
|
eb656de…
|
aku
|
214 |
|
|
eb656de…
|
aku
|
215 |
# # ## ### ##### ######## ############# |
|
eb656de…
|
aku
|
216 |
} |
|
eb656de…
|
aku
|
217 |
|
|
eb656de…
|
aku
|
218 |
namespace eval ::vc::fossil::import::cvs { |
|
eb656de…
|
aku
|
219 |
namespace export state |
|
eb656de…
|
aku
|
220 |
namespace eval state { |
|
eb656de…
|
aku
|
221 |
namespace import ::vc::tools::trouble |
|
eb656de…
|
aku
|
222 |
namespace import ::vc::tools::log |
|
eb656de…
|
aku
|
223 |
log register state |
|
eb656de…
|
aku
|
224 |
} |
|
eb656de…
|
aku
|
225 |
} |
|
eb656de…
|
aku
|
226 |
|
|
eb656de…
|
aku
|
227 |
# # ## ### ##### ######## ############# ##################### |
|
eb656de…
|
aku
|
228 |
## Ready |
|
eb656de…
|
aku
|
229 |
|
|
eb656de…
|
aku
|
230 |
package provide vc::fossil::import::cvs::state 1.0 |
|
eb656de…
|
aku
|
231 |
return |