1 # vim: foldmethod=marker foldmarker=<<<,>>> ft=tcl ts=4 shiftwidth=4
4 # Periodic housecleaning routine for recently_deceased trimming
6 class tlc
::DSchan_backend {
17 variable dbfile
":memory:"
20 method register_pool
{pool
{check_cb
{}}}
21 method deregister_pool
{pool
}
22 method add_item
{pool item
}
23 method change_item
{pool id newitem
}
24 method remove_item
{pool id
}
25 method get_item
{pool id
}
26 method item_count
{pool
}
28 method pool_exists
{pool
}
29 method id_exists
{pool id
}
39 variable general_info_jmid
42 variable in_init_transaction
0
44 method announce_pool
{pool
}
45 method announce_new
{pool id item
}
46 method announce_changed
{pool id olditem newitem
}
47 method announce_removed
{pool id item
}
48 method check_pool
{pool
}
49 method req_handler
{auth user seq rest
}
50 method pool_chan_cb
{pool op data
}
51 method general_info_chan_cb
{op data
}
57 configbody tlc
::DSchan_backend::headers { #<<<1
58 if {[info exists general_info_jmid
]} {
59 $auth jm
$general_info_jmid [list headers_changed
$headers]
64 configbody tlc
::DSchan_backend::id_column { #<<<1
65 if {[info exists general_info_jmid
]} {
66 $auth jm
$general_info_jmid [list id_column_changed
$id_column]
71 body tlc
::DSchan_backend::constructor {args
} { #<<<1
72 package require sqlite3
75 array set pool_jmids
{}
79 foreach reqf
{comp tag headers
} {
80 if {![info exists
$reqf]} {
81 error "Must set -$reqf"
86 if {$dbfile == ":memory:" ||
$dbfile == ""} {
87 set _dbfile
":memory:"
89 if {![file exists
$dbfile]} {
91 set fp
[open $dbfile w
]
94 set _dbfile
[file normalize
$dbfile]
95 while {[file type
$_dbfile] == "link"} {
96 set _dbfile
[file readlink
$_dbfile]
97 set _dbfile
[file normalize
$_dbfile]
99 if {[info exists file_hack
]} {
103 # Canonise dbfile >>>
106 sqlite3
[namespace current
]::$db $_dbfile
110 set auth
[$comp cget
-auth]
111 $comp handler
$tag [code
$this req_handler
]
115 body tlc
::DSchan_backend::destructor {} { #<<<1
116 if {[info exists db
]} {
119 if {!($persist) && $_dbfile != ":memory:"} {
120 if {[file exists
$_dbname]} {
128 body tlc
::DSchan_backend::register_pool {pool
{check_cb
{}}} { #<<<1
129 set pools
($pool) $check_cb
130 log debug
"registered pool"
135 body tlc
::DSchan_backend::deregister_pool {pool
} { #<<<1
137 if {[info exists pool_jmids
($pool)]} {
138 $auth jm_can
$pool_jmids($pool) [list pool_deregistered
]
139 $auth chans deregister_chan
$pool_jmids($pool)
141 array unset pool_jmids
$pool
148 array unset pools
$pool
149 log debug
"deregistered pool"
153 body tlc
::DSchan_backend::add_item {pool item
} { #<<<1
156 set id
[lindex $item $id_column]
158 if {[id_exists
$pool $id]} {
159 error "ID \"$id\" already exists in ($pool), adding ([join $item |])" \
160 "" [list duplicate_id
$id $pool]
162 set last_updated
[clock seconds
]
164 insert into pool_data
(
177 log debug
"announcing new item ($id) in pool ($pool)"
178 announce_new
$pool $id $item
182 body tlc
::DSchan_backend::change_item {pool id newitem
} { #<<<1
185 if {![id_exists
$pool $id]} {
186 error "ID $id does not exist in $pool"
189 set newid
[lindex $newitem $id_column]
191 error "Changing id column not allowed" "" \
192 [list id_column_changed
$id $newid]
195 set olditem
[get_item
$pool $id]
196 if {$olditem == $newitem} return
198 set last_updated
[clock seconds
]
204 last_updated
= $last_updated
209 log debug
"\nannouncing item change ($id) in pool ($pool)\nold: ($olditem)\nnew: ($newitem)"
210 announce_changed
$pool $id $olditem $newitem
214 body tlc
::DSchan_backend::remove_item {pool id
} { #<<<1
216 if {![id_exists
$pool $id]} {
217 log warning
"item ($id) not found in pool ($pool)"
221 set item
[get_item
$pool $id]
222 if {!($in_init_transaction)} {
226 set dbid
[$db onecolumn
{
242 set timeofdeath
[clock seconds
]
244 insert into recently_deceased
(
253 if {!($in_init_transaction)} {
259 error $errmsg "" $::errorCode
262 log debug
"announcing item removal ($id) from pool ($pool)"
263 announce_removed
$pool $id $item
267 body tlc
::DSchan_backend::get_item {pool id
} { #<<<1
278 # WARNING: this logic will break if more columns are selected
279 if {[llength $rows] == 0} {
280 error "ID $id does not exist in $pool"
282 if {[llength $rows] > 1} {
283 log warning
"Duplicate rows for id ($id) and pool ($pool)"
285 return [lindex $rows 0]
289 body tlc
::DSchan_backend::item_count {pool
} { #<<<1
291 return [$db onecolumn
{
302 body tlc
::DSchan_backend::id_list {pool
} { #<<<1
315 body tlc
::DSchan_backend::announce_pool {pool
} { #<<<1
316 log debug
"general_info_jmid exists: [info exists general_info_jmid]"
317 if {[info exists general_info_jmid
]} {
318 $auth jm
$general_info_jmid [list new_pool
$pool]
323 body tlc
::DSchan_backend::announce_new {pool id item
} { #<<<1
324 log debug
"pool exists: [info exists pool_jmids($pool)]"
325 if {[info exists pool_jmids
($pool)]} {
326 $auth jm
$pool_jmids($pool) [list new
$id $item]
331 body tlc
::DSchan_backend::announce_changed {pool id olditem newitem
} { #<<<1
332 log debug
"pool exists: [info exists pool_jmids($pool)]"
333 if {[info exists pool_jmids
($pool)]} {
334 $auth jm
$pool_jmids($pool) [list changed
$id $olditem $newitem]
339 body tlc
::DSchan_backend::announce_removed {pool id item
} { #<<<1
340 log debug
"pool exists: [info exists pool_jmids($pool)]"
341 if {[info exists pool_jmids
($pool)]} {
342 $auth jm
$pool_jmids($pool) [list removed
$id $item]
347 body tlc
::DSchan_backend::check_pool {pool
} { #<<<1
348 if {![info exists pools
($pool)]} {
349 return -code error "No such pool: ($pool)"
354 body tlc
::DSchan_backend::req_handler {auth user seq rest
} { #<<<1
355 switch -- [lindex $rest 0] {
357 set extra
[lindex $rest 1]
360 foreach {pool check_cb
} [array get pools
] {
362 # Provide a place for the check_cb callback to scribble into
363 # via an upvar command. We send the contents to the client
364 array unset pool_meta
365 array set pool_meta
{}
367 log debug
"check_cb is: ($check_cb)"
370 ||
[eval $check_cb [list $user $pool $extra]]
372 lappend userpools
$pool
375 set pool_meta_all
($pool) [array get pool_meta
]
376 log debug
"Saving pool_meta for ($pool):\n$pool_meta_all($pool)"
378 log
error "error calling check_cb:\n$::errorInfo"
382 if {![info exists general_info_jmid
]} {
383 set general_info_jmid
[$auth unique_id
]
384 $auth chans register_chan
$general_info_jmid \
385 [code
$this general_info_chan_cb
]
387 $auth pr_jm
$general_info_jmid $seq [list general
[list \
389 id_column
$id_column \
392 foreach pool
$userpools {
393 if {![info exists pool_jmids
($pool)]} {
394 set pool_jmids
($pool) [$auth unique_id
]
395 $auth chans register_chan
$pool_jmids($pool) \
396 [code
$this pool_chan_cb
$pool]
399 log debug
"Contents of pool_meta array:\n[array get pool_meta]"
400 set all_items
[$db eval {
408 $auth pr_jm
$pool_jmids($pool) $seq [list datachan
$pool $all_items $pool_meta_all($pool)]
415 set new_pool
[lindex $rest 1]
416 set extra
[lindex $rest 2]
418 if {![info exists pools
($new_pool)]} {
419 $auth nack
$seq "No such pool: ($new_pool)"
422 set check_cb
$pools($new_pool)
425 # Provide a place for the check_cb callback to scribble into
426 # via an upvar command. We send the contents to the client
427 array set pool_meta
{}
431 ||
[eval $check_cb [list $user $new_pool $extra]]
433 if {![info exists pool_jmids
($new_pool)]} {
434 set pool_jmids
($new_pool) [$auth unique_id
]
435 $auth chans register_chan
$pool_jmids($new_pool) \
436 [code
$this pool_chan_cb
$new_pool]
439 set all_items
[$db eval {
447 $auth pr_jm
$pool_jmids($new_pool) $seq [list datachan
$new_pool $all_items [array get pool_meta
]]
448 log debug
"Added user ([$user name]) to new pool ($new_pool), with ([llength $all_items]) initial items"
452 log debug
"User ([$user name]) is not a viewer of pool ($new_pool)"
456 log
error "error calling check_cb:\n$::errorInfo"
457 $auth nack
$seq "Internal error"
463 log
error "invalid req type: [lindex $rest 0]"
464 $auth nack
$seq "Invalid req type: ([lindex $rest 0])"
470 body tlc
::DSchan_backend::pool_chan_cb {pool op data
} { #<<<1
473 log debug
"all destinations disconnected"
474 array unset pool_jmids
$pool
478 foreach {seq prev_seq msg
} $data break
479 $auth nack
$seq "Requests not allowed on this channel"
483 log
error "unexpected op: ($op)"
489 body tlc
::DSchan_backend::general_info_chan_cb {op data
} { #<<<1
492 log debug
"all destinations disconnected"
493 if {[info exists general_info_jmid
]} {
494 unset general_info_jmid
499 foreach {seq prev_seq msg
} $data break
500 $auth nack
$seq "Requests not allowed on this channel"
504 log
error "unexpected op: ($op)"
510 body tlc
::DSchan_backend::pool_exists {pool
} { #<<<1
511 return [info exists pools
($pool)]
515 body tlc
::DSchan_backend::init_db {} { #<<<1
516 set exists
[$db onecolumn
{
523 and name
= 'pool_data'
526 if {!($persist) && $exists} {
528 drop table pool_data
;
529 drop table recently_deceased
;
534 # recently_deceased.dbid == -1 gives the last cleanout time
537 create table pool_data
(
538 autoid integer primary key autoincrement
,
541 last_updated integer
,
544 create index pool_data_pool_idx on pool_data
(pool
);
545 create index pool_data_id_idx on pool_data
(id
);
547 create table recently_deceased
(
548 dbid integer not null
,
549 timeofdeath integer not null
551 create index recently_deceased_timeofdeath_idx
552 on recently_deceased
(timeofdeath
);
558 body tlc
::DSchan_backend::id_exists {pool id
} { #<<<1
559 return [$db onecolumn
{
571 body tlc
::DSchan_backend::start_init {} { #<<<1
573 set in_init_transaction
1
577 body tlc
::DSchan_backend::end_init {} { #<<<1
578 $db eval {commit
; analyze
}
579 set in_init_transaction
0
583 body tlc
::DSchan_backend::abort_init {} { #<<<1
585 set in_init_transaction
0