nsmcp::AccessRecordStoreSQLite method prepare (public)

 <instance of nsmcp::AccessRecordStoreSQLite[i]> prepare file

Defined in /usr/local/ns/tcl/nsmcp/log-traffic-record-store.tcl

Resolve or initialize the stored generation for a discovered log file. Validate the source prefix and committed-range anchors before reusing a generation. Writers retire or replace changed generations; readers only accept an existing active generation.

Parameters:
file (required)
Discovered source descriptor, including path, identity, size and modification time.
Returns:
The stored file identifier, or -1 when a reader cannot use the generation.

Testcases:
No testcase defined.
Source code:
file stat [dict get $file path] actual
set identity [dict get $file identity]
set policy [:dict getdef ${:settings} policy {}]
set key [ns_crypto::md string -digest sha256 -- [list schema1 $identity $policy]]
set fid {}
set old [${:db} execute {SELECT * FROM main.files WHERE k=:key}]
set f [open [dict get $file path] rb]
try {
    set reset false
    if {$old ne {}} {
        set old [lindex $old 0]
        set fid [dict get $old fid]
        set prefix  [ns_crypto::md string -digest sha256 -binary -- [read $f [dict get $old plen]]]
        if {[dict get $old high]>$actual(size) || $prefix ne [dict get $old prefix]
            || ([dict get $old high]==$actual(size) && [dict get $old stamp]!=$actual(mtime))} {
            set reset true
        }
        if {!$reset} {
            # A small boundary anchor per contiguous imported range
            # detects rewrites near committed cursors before reuse.
            foreach span [${:db} execute  {SELECT hi,anchor FROM main.ranges WHERE fid=:fid LIMIT 4097}] {
                set hi [dict get $span hi]
                seek $f [expr {max(0,$hi-64)}]
                if {[ns_crypto::md string -digest sha256 -binary --  [read $f [expr {min(64,$hi)}]]] ne [dict get $span anchor]} {
                    set reset true
                    break
                }
            }
        }
    }
    if {${:mode} eq "reader"} {
        return [expr {$old eq {} || $reset || ![dict get $old active] ? -1 : $fid}]
    }
    if {$old eq {} || $reset} {
        set plen [expr {min(64,[dict get $file size])}]
        seek $f 0
        set prefix [ns_crypto::md string -digest sha256 -binary -- [read $f $plen]]
        set high 0
        set stamp [dict get $file mtime]
        set now [clock seconds]
        ${:db} transaction {
            if {[:dict getdef ${:settings} unified false] && $fid ne {}} {
                set retired "${key}:retired:$fid"
                set oldIdentity "${identity}:retired:$fid"
                ${:db} execute {
                    UPDATE main.files SET k=:retired,identity=:oldIdentity WHERE fid=:fid
                }
            } else {
                if {$fid ne {}} {
                    ${:db} execute {DELETE FROM main.rows WHERE fid=:fid}
                }
                ${:db} execute {
                    DELETE FROM main.ranges WHERE fid=:fid;
                    DELETE FROM main.files WHERE k=:key
                }
            }
            ${:db} execute {
                INSERT INTO main.files(k, identity, prefix, plen, high, stamp, touched) VALUES(:key,
                                                                                                        :identity, :prefix, :plen, :high, :stamp, :now)
            }
        }
    }
} finally {
    close $f
}
set fid [${:db} scalar {SELECT fid FROM main.files WHERE k=:key}]
set now [clock seconds]
set touchBefore [expr {$now-max(1,min(3600,int([dict get ${:settings} retention]/4)))}]
${:db} execute  {UPDATE main.files SET touched=:now WHERE fid=:fid AND touched<:touchBefore}
${:db} execute {
    UPDATE main.files SET active=0 WHERE identity=:identity AND fid<>:fid AND active=1;
    UPDATE main.files SET active=1 WHERE fid=:fid
}
return $fid
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: