# ------------------------------------------------------------ # Copyright (c) 2005-2013 # Q.W.Page Associates Inc. # www.qwpage.com # All rights reserved. # ------------------------------------------------------------ ::namespace eval ::qw::newviews::collection_enable {}; ::itcl::class ::qw::newviews::collection_enable::manager { /* { ::qw::newviews::collection_enable::manager class */ } protected variable _database ""; protected variable _database_id ""; protected variable _database_path ""; # protected variable _semiloops_are_allowed ""; protected variable _metas; protected variable _posting_fields; protected variable _postings_indexes; protected variable _postings_indexes_with_closure_enabled; protected variable _transactions_indexes; protected variable _tag_list ""; protected variable _accounts_by_object_id; protected variable _posting_account_object_id_array; protected variable _visited_account_object_id_array; method constructor {sargs} { ::array set _metas {}; ::array set _posting_fields {}; ::array set _postings_indexes {}; ::array set _postings_indexes_with_closure_enabled {}; ::array set _transactions_indexes {}; ::array set _accounts_by_object_id {}; ::array set _posting_account_object_id_array {}; ::array set _visited_account_object_id_list {}; ::set _database [::sargs::get $sargs .database]; ::if {$_database eq ""} { ::set Window [::sargs::get $sargs .odb.object]; ::if {$Window eq ""} { ::qw::bug 314120090428110837 "The database was not specified."; } ::set _database [$Window odb_database application]; } ::set _database_id [$_database cpp_database_id_get]; ::set _database_path [$_database cpp_database_path]; ::set DatabaseType [$_database cpp_database_type_get]; ::switch -- $DatabaseType { application { } default { ::qw::bug 314120090428110855 "Expected database type \"application\" but encountered \"$DatabaseType\"."; } } ::return $this; # ::set _semiloops_are_allowed [$_database cpp_semiloops_are_allowed]; } method destructor {} { } method get_enabled_accounts_below {sargs} { /* { Gets all the accounts below that we need to build the current collection's indexes. That is, we get a cross-section of enabled accounts. We traverse the total-to hierachy below, but we only go as deep as necessary to hit an enabled account. We return the complete list of masters. */ } ::set Result ""; ::set ParentAccount [::sargs::get $sargs .parent_account]; ::if {$ParentAccount ne ""} { ::if {[::qw::command_exists $ParentAccount]} { ::if {[$ParentAccount.postings_collection_is_enabled odb_get]} { ::return $ParentAccount; } } else { ::set OS [$_database cpp_object_structure_load .address $ParentAccount]; ::if {[::sargs::boolean_get $OS .data.postings_collection_is_enabled]} { ::return $ParentAccount; } } } else { ::set ParentAccount [::sargs::get $sargs .account]; } ::set PrimaryIndexField ".index/name"; ::if {$::qw::control(active_index_is_enabled)} { ::set PrimaryIndexField ".index/id"; } ::if {[::qw::command_exists $ParentAccount]} { ::set Kids [$ParentAccount.total.kids$PrimaryIndexField odb_masters]; } else { ::set OS [$_database cpp_object_structure_load .address $ParentAccount]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::set Kids [$_database cpp_file_odb_masters \ .path /odb/index$ClassPath.total.kids$PrimaryIndexField \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ]; } ::foreach Kid $Kids { ::set Result [::concat $Result [get_enabled_accounts_below .parent_account $Kid]]; } ::return $Result; } method build_list_of_posting_accounts_below {sargs} { /* { We are getting the posting accounts because we have semiloops. We need a global list of posting accounts that have already been visited. list of */ } ::set Account [::sargs::get $sargs .account]; ::if {$Account eq ""} { ::qw::bug 314120090813080709 "Encountered empty \".account\"."; } ::set ObjectId [::qw::odb::object_id_extract .address $Account]; ::if {[::info exists _visited_account_object_id_array($ObjectId)]} { ::return; } ::set PrimaryIndexField ".index/name"; ::if {$::qw::control(active_index_is_enabled)} { ::set PrimaryIndexField ".index/id"; } ::set _visited_account_object_id_array($ObjectId) 1; ::if {[::qw::command_exists $Account]} { ::set Kids [[$Account .total.kids$PrimaryIndexField] odb_masters]; ::set ClassPath [$Account odb_class_path]; } else { ::set OS [$_database cpp_object_structure_load .address $Account]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::set Kids [$_database cpp_file_odb_masters \ .path /odb/index${ClassPath}.total.kids$PrimaryIndexField \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ]; } ::if {[::llength $Kids]==0} { ::if {[::string first "/OBJECT/NEWVIEWS/ACCOUNT/TOTAL" $ClassPath]!=0} { ::set _posting_account_object_id_array($ObjectId) 1; } ::return; } ::foreach Kid $Kids { build_list_of_posting_accounts_below .account $Kid; } } method get_enabled_journals_below {sargs} { /* { Gets cross-section of enabled descendants. We will copy their index records into the current journals indexes. */ } ::set Result ""; ::set ParentJournal [::sargs::get $sargs .parent_journal]; ::if {$ParentJournal ne ""} { ::if {[::qw::command_exists $ParentJournal]} { ::if {[[$ParentJournal .transactions_collection_is_enabled] odb_get]} { ::return $ParentJournal; } } else { ::set OS [$_database cpp_object_structure_load .address $ParentJournal]; ::if {[::sargs::boolean_get $OS .data.transactions_collection_is_enabled]} { ::return $ParentJournal; } } } else { ::set ParentJournal [::sargs::get $sargs .journal]; } ::if {[::qw::command_exists $ParentJournal]} { ::set Kids [[$ParentJournal .odb_deriveds.index/id] odb_masters]; } else { ::set OS [$_database cpp_object_structure_load .address $ParentJournal]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::set Kids [$_database cpp_file_odb_masters \ .path /odb/index$ClassPath.odb_deriveds.index/id \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ]; } ::foreach Kid $Kids { ::set Result [::concat $Result [get_enabled_journals_below .parent_journal $Kid]]; } ::return $Result; } method get_enabled_journals_below_save {sargs} { ::set Result ""; ::set Journal [::sargs::get $sargs .journal]; ::set OS [$_database cpp_object_structure_load .address $Journal]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::set Kids [$_database cpp_file_odb_masters \ .path /odb/index$ClassPath.odb_deriveds.index/id \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ]; ::foreach Kid $Kids { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::if {[::sargs::boolean_get $OS .data.transactions_collection_is_enabled]} { ::lappend Result $Kid; ::continue; } ::set Result [::concat $Result [get_enabled_journals_below .journal $Kid]]; } ::return $Result; } /* { Turned out we don't need this. pgq can do it in newviews definition.qw_tcl method repair_postings_collection_is_enabled {sargs} { /* { We have a repair_postings_collection_is_enabled in database_utilies but we also make the check here. Whenever we turn on postings_collection_is_enabled we make an on the fly check/repair. Assuming we are turning on the ledger indexes, there should currently be none. If we find any we delete them. */ } ::set rwb1_debug 0; ::set Account [::sargs::get $sargs .account.address]; ::set Database [$Account odb_database]; ::set AccountObjectId [$Account odb_object_id]; ::if {[[$Account.postings_collection_is_enabled] odb_get]} { ::qw::bug 314120200131141951 "repair_postings_collection_is_enabled expected .postings_collection_is_enabled to be off."; } ::set AccountOS [$Database cpp_object_structure_load .object_id $AccountObjectId]; ::set AccountClassPath [$Account odb_class_path]; ::set AccountObjectId [$Account odb_object_id]; ::set BadIndexCount 0; /* { Kludge alert. Needs for general call to get the indexes. We may come op with an odbutil method for it. */ } ::set IndexList { .postings.index .postings.index/id .postings.index/date .postings.index/date/debit .postings.index/date/credit .postings.index/date/closed .postings.index/date/open .postings.index/reconcile .postings.index/reference .postings.index/description .postings.index/date1 .postings.index/reference1 } ::foreach Field $IndexList { # ------------------------------------------------------------ # Iterate over all postings indexes, kill all record. # ------------------------------------------------------------ ::set IndexField ".postings$Field"; ::set RecordCount [$Database cpp_file_record_count \ .path /odb/index$AccountClassPath$IndexField \ .range.begin [::list string $AccountObjectId] \ .range.end [::list string $AccountObjectId] \ ]; ::if {$RecordCount!=0} { ::incr BadIndexCount 1; ::if {$rwb1_debug} {::puts "rwb1_debug,account==[::sargs::get $AccountOS .data.name],index==$IndexField,RecordCount==$RecordCount";} $Database cpp_file_delete_all_records \ .path /odb/index$AccountClassPath$IndexField \ .range.begin [::list string $AccountObjectId] \ .range.end [::list string $AccountObjectId] \ ; } } } */ } method postings_collection_index_enable {sargs} { /* { Called when print accounts needs to build an temporary index for a disabled total account in order to print it's ledger items. Gathers up all records from enabled accounts below and inserts them into the specified index of the specified account. usage ::set Manager [::qw::newviews::collection_enable::manager #auto .database [odb_database]]; $Manager postings_collection_index_enable \ .account.address $Account \ .index.path .postings.index/date \ .tag financial \ .date.begin XXX \ .date.end YYY \ ; ::itcl::delete object $Manager; The dates can be empty or not even sepcified (i.e. can be full range). Later the caller might want to delete the index records: ::switch -- $EndDate { "" { [odb_database] cpp_delete_all_records \ .path /odb/index[$Account odb_class_path]$IndexPath \ .range.begin [::list string [$Account odb_object_id]] \ .range.end [::list string [$Account odb_object_id] date $EndDate] \ ; } default { [odb_database] cpp_delete_all_records \ .path /odb/index[$Account odb_class_path]$IndexPath \ .range.begin [::list string [$Account odb_object_id] date $BeginDate] \ .range.end [::list string [$Account odb_object_id] date $EndDate] \ ; } } */ } #::set DestroySkip [::sargs::get $sargs .destroy_skip]; #::set NotifySkip [::sargs::get $sargs .notify_skip]; ::set Account [::sargs::get $sargs .account.address]; ::set IndexField [::sargs::get $sargs .index.path]; ::set Tag [::sargs::get $sargs .tag]; ::set BeginDate [::sargs::get $sargs .date.begin]; ::set EndDate [::sargs::get $sargs .date.end]; ::if {$Account eq ""} { ::qw::bug 314120090623121227 "Expected an \".account.address\" argument."; } ::if {$Account eq ""} { ::qw::bug 314120090623121228 "Expected an \".index.path\" argument."; } ::if {$Tag eq ""} { ::qw::bug 314120090623121229 "Expected a \".tag\" argument."; } ::set EnabledAccountList [get_enabled_accounts_below .account $Account]; ::if {[$_database cpp_semiloops_are_allowed]} { ::set EnabledAccountList [::lsort -unique $EnabledAccountList]; } $_database cpp_gatgun_hashtable_indexes_flush; #nv2.17.0 #::if {$::qw::control(print_account_ledgers_historic_aging)} {} ::switch -glob -- $IndexField { .postings.index/date* { ::set ProgressLimit 0; ::foreach Kid $EnabledAccountList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; ::switch -- $BeginDate { "" { ::incr ProgressLimit [$_database cpp_file_record_count \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId tag $Tag] \ .range.end [::list string $KidObjectId tag $Tag date $EndDate] \ ]; } default { ::incr ProgressLimit [$_database cpp_file_record_count \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId tag $Tag date $BeginDate] \ .range.end [::list string $KidObjectId tag $Tag date $EndDate] \ ]; } } } ::set Progress ""; #2.34.8 ::set ProgressMinimum 2000; ::set ProgressMinimum 10000; ::if {$ProgressLimit>=$ProgressMinimum} { ::set Progress [::QW::PROGRESS::OPERATION [::qw::progress::auto] \ .database_id [$_database cpp_database_id_get] \ .database_path [$_database cpp_database_path] \ .user [$_database cpp_user_name_get] \ .limit $ProgressLimit \ .resolution 119 \ .destroy_skip 0 \ .operation "building index records" \ .status "[$_database cpp_database_path] building postings index records." \ ]; ::qw::finally [::list ::itcl::delete object $Progress]; } ::set SrcFile [$_database cpp_file_factory]; ::qw::finally [::list $SrcFile cpp_destroy]; ::set DstFile [$_database cpp_file_factory]; ::qw::finally [::list $DstFile cpp_destroy]; ::set OS [$_database cpp_object_structure_load .address $Account]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::switch -- $BeginDate { "" { $DstFile cpp_file_open \ .path /odb/index$ClassPath$IndexField \ .range.begin [::list string $ObjectId tag $Tag] \ .range.end [::list string $ObjectId tag $Tag date $EndDate] \ ; } default { $DstFile cpp_file_open \ .path /odb/index$ClassPath$IndexField \ .range.begin [::list string $ObjectId tag $Tag date $BeginDate] \ .range.end [::list string $ObjectId tag $Tag date $EndDate] \ ; } } ::foreach Kid $EnabledAccountList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; ::switch -- $BeginDate { "" { $SrcFile cpp_file_open \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId tag $Tag] \ .range.end [::list string $KidObjectId tag $Tag date $EndDate] \ ; } default { $SrcFile cpp_file_open \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId tag $Tag date $BeginDate] \ .range.end [::list string $KidObjectId tag $Tag date $EndDate] \ ; } } ::for {::set Record [$SrcFile cpp_record_first];} {$Record ne ""} {::set Record [$SrcFile cpp_record_next $Record];} { ::set After $Record; ::set Key [::sargs::get $Record .key]; ::lset Key 1 $ObjectId; ::sargs::var::set After .key $Key; $DstFile cpp_record_insert .after $After; ::if {$Progress ne ""} { $Progress increment; } } } } .postings.index/reconcile { ::set ProgressLimit 0; ::foreach Kid $EnabledAccountList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; ::incr ProgressLimit [$_database cpp_file_record_count \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId tag $Tag] \ .range.end [::list string $KidObjectId tag $Tag] \ ]; } ::set Progress ""; #2.34.8 ::set ProgressMinimum 2000; ::set ProgressMinimum 10000; ::if {$ProgressLimit>=$ProgressMinimum} { ::set Progress [::QW::PROGRESS::OPERATION [::qw::progress::auto] \ .database_id [$_database cpp_database_id_get] \ .database_path [$_database cpp_database_path] \ .user [$_database cpp_user_name_get] \ .limit $ProgressLimit \ .resolution 119 \ .destroy_skip 0 \ .operation "building index records" \ .status "[$_database cpp_database_path] building postings index records." \ ]; ::qw::finally [::list ::itcl::delete object $Progress]; } ::set SrcFile [$_database cpp_file_factory]; ::qw::finally [::list $SrcFile cpp_destroy]; ::set DstFile [$_database cpp_file_factory]; ::qw::finally [::list $DstFile cpp_destroy]; ::set OS [$_database cpp_object_structure_load .address $Account]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; $DstFile cpp_file_open \ .path /odb/index$ClassPath$IndexField \ .range.begin [::list string $ObjectId tag $Tag] \ .range.end [::list string $ObjectId tag $Tag] \ ; ::foreach Kid $EnabledAccountList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; $SrcFile cpp_file_open \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId tag $Tag] \ .range.end [::list string $KidObjectId tag $Tag] \ ; ::for {::set Record [$SrcFile cpp_record_first];} {$Record ne ""} {::set Record [$SrcFile cpp_record_next $Record];} { ::set After $Record; ::set Key [::sargs::get $Record .key]; ::lset Key 1 $ObjectId; ::sargs::var::set After .key $Key; $DstFile cpp_record_insert .after $After; ::if {$Progress ne ""} { $Progress increment; } } } } } } method postings_collection_enable {sargs} { /* { We just enabled or disabled a collection. This method is called iff there are no semiloops, the .postings_collection_is_enabled field of an account changed, and it was the only field that changed. Because it is the only field that changed, we know that no dependencies will be processed and therefore we can use this shortcut. */ } ::set rwb1_debug 0; ::if {$rwb1_debug} {::puts "rwb1_debug,postings_collection_enable,1000.0";} ::set DestroySkip [::sargs::get $sargs .destroy_skip]; ::set NotifySkip [::sargs::get $sargs .notify_skip]; ::set Account [::sargs::get $sargs .account.address]; ::if {$Account eq ""} { ::qw::bug 314120090429131205 "Expected an \".account.address\" argument."; } ::set OS [$_database cpp_object_structure_load .address $Account]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::set Before [[$Account .postings_collection_is_enabled] odb_get_before]; ::set After [[$Account .postings_collection_is_enabled] odb_get]; ::if {$Before eq $After} { ::return; } $_database cpp_gatgun_hashtable_indexes_flush; ::if {$Before} { # ------------------------------------------------------------ # Collection becoming disabled case. # ------------------------------------------------------------ /* { Before was true so we are disabling an enabled collection. We really only need to delete the records from indexes with closure enabled but we do it for the rest anyway. This is to allow for cleaning up a bug prior to 2.15.4 where we build them all, including the indexes for which closure was not enabled. This operation is so fast that we have removed the progress bars. */ } ::if {$rwb1_debug} {::puts "rwb1_debug,postings_collection_enable,1000.1";} ::if {$::qw::control(collection_enable_ping)} { ::qw::finally [::list $_database cpp_ping]; } ::set IndexFieldList [get_postings_indexes .class_path $ClassPath]; ::foreach IndexField $IndexFieldList { /* { If the collection was enabled there must be some active indexes. Even if not, and even if the AIS is empty, the primary will be populated and we depopulate it here. We may be deleting records for inactive indexes here but since the indexes should be empty it doesn't matter. */ } $_database cpp_file_delete_all_records \ .path /odb/index$ClassPath$IndexField \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ; } ::if {$::qw::control(active_index_is_enabled)} { ::set BeforeAIS [$Account cpp_active_index_structure_get]; ::if {[::sargs::size $BeforeAIS]==0} { # AIS is empty. Nothing more to do. ::return; } # ------------------------------------------------------------ # Unset .postings in AIS and write or delete the AIS. # ------------------------------------------------------------ ::set AfterAIS $BeforeAIS; ::sargs::var::unset AfterAIS .postings; ::set AfterAIS [::sargs::prune .structure $AfterAIS]; ::set BeforeRecord [::sargs .key [::list string $ObjectId] .amounts [::list .count 1.0] .data $BeforeAIS]; ::if {[::sargs::size $AfterAIS]==0} { $Database cpp_file_record_delete .path /odb/active_index .before $BeforeRecord; } else { ::set AfterRecord [::sargs .key [::list string $ObjectId] .amounts [::list .count 1.0] .data $AfterAIS]; $Database cpp_file_record_write .path /odb/active_index .before $BeforeRecord .after $AfterRecord; } $Account cpp_active_index_structure_set .active_index_structure $AfterAIS; } ::return; } # ------------------------------------------------------------ # Collection becoming enabled case. # ------------------------------------------------------------ ::set EnabledAccountList [get_enabled_accounts_below .account $Account]; ::if {$::qw::control(active_index_is_enabled)} { /* { When an account .postings collection becomes enabled we only build the primary. The secondary indexes then use the primary to populate if/when they become active. */ } ::set IndexFieldList [::list ".postings.index/id"]; } else { ::set IndexFieldList [get_postings_indexes_with_closure_enabled .class_path $ClassPath]; } ::if {$rwb1_debug} {::puts "rwb1_debug,postings_collection_enable,1000.2,IndexFieldList==$IndexFieldList";} /* { 2.37.2 Added a second progress box. We were iterating to count the number of records that need to be processed but this took about a minute on a large database. So the operation that counts the progress limit also needs a progress box. This is all so we can determine if a process is responding and red progress box help with that be updating the process record. */ } ::set Progress1 ""; ::set ProgressLimit1 [::llength $EnabledAccountList]; ::set ProgressMinimum1 200; ::if {$ProgressLimit1>=$ProgressMinimum1} { # ------------------------------------------------------------ # Create progress box which accumulates the second progress limit. # ------------------------------------------------------------ ::set Progress1 [::QW::PROGRESS::OPERATION [::qw::progress::auto]1 \ .database_id [$_database cpp_database_id_get] \ .database_path [$_database cpp_database_path] \ .user [$_database cpp_user_name_get] \ .limit $ProgressLimit1 \ .destroy_skip $DestroySkip \ .operation "preparing to create index records" \ .status "[$_database cpp_database_path] preparing to create postings index records." \ ]; ::qw::finally [::list ::itcl::delete object $Progress1]; } ::set ProgressAccumulator1 0; ::set ProgressLimit2 0; ::foreach Kid $EnabledAccountList { # ------------------------------------------------------------ # Add up the total number of records to obtain progress limit. # ------------------------------------------------------------ ::if {$Progress1 ne ""} { ::incr ProgressAccumulator1 1; ::if {$ProgressAccumulator1>=17} { $Progress1 increment $ProgressAccumulator1; ::set ProgressAccumulator1 0; } } ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; ::foreach IndexField $IndexFieldList { ::incr ProgressLimit2 [$_database cpp_file_record_count \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId] \ .range.end [::list string $KidObjectId] \ ]; } } ::if {$Progress1 ne ""} { ::if {$ProgressAccumulator1!=0} { $Progress1 increment $ProgressAccumulator1; ::set ProgressAccumulator1 0; } } ::set Progress2 ""; ::set ProgressMinimum2 30000; ::if {$rwb1_debug} {::puts "rwb1_debug,postings_collection_enable,1000.3";} ::if {$ProgressLimit2>=$ProgressMinimum2} { # ------------------------------------------------------------ # Create the progress box. # ------------------------------------------------------------ ::set Progress2 [::QW::PROGRESS::OPERATION [::qw::progress::auto]2 \ .database_id [$_database cpp_database_id_get] \ .database_path [$_database cpp_database_path] \ .user [$_database cpp_user_name_get] \ .limit $ProgressLimit2 \ .destroy_skip $DestroySkip \ .operation "creating index records" \ .status "[$_database cpp_database_path] creating postings index records." \ ]; ::qw::finally [::list ::itcl::delete object $Progress2]; } ::if {$rwb1_debug} {::puts "rwb1_debug,postings_collection_enable,1000.4";} ::set SrcFile [$_database cpp_file_factory]; ::qw::finally [::list $SrcFile cpp_destroy]; ::set DstFile [$_database cpp_file_factory]; ::qw::finally [::list $DstFile cpp_destroy]; ::set ProgressAccumulator2 0; ::foreach IndexField $IndexFieldList { $DstFile cpp_file_open \ .path /odb/index$ClassPath$IndexField \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ; ::foreach Kid $EnabledAccountList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; $SrcFile cpp_file_open \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId] \ .range.end [::list string $KidObjectId] \ ; ::for {::set Record [$SrcFile cpp_record_first];} {[::sargs::size $Record]!=0} {::set Record [$SrcFile cpp_record_next $Record];} { ::set After $Record; ::set Key [::sargs::get $Record .key]; ::lset Key 1 $ObjectId; ::sargs::var::set After .key $Key; $DstFile cpp_record_insert .after $After; ::if {$Progress2 ne ""} { ::incr ProgressAccumulator2 1; ::if {$ProgressAccumulator2>=2111} { $Progress2 increment $ProgressAccumulator2; ::set ProgressAccumulator2 0; } } } } } ::if {$Progress2 ne ""} { ::if {$ProgressAccumulator2!=0} { $Progress2 increment $ProgressAccumulator2; ::set ProgressAccumulator2 0; } } ::if {$rwb1_debug} {::puts "rwb1_debug,postings_collection_enable,1000.5";} ::if {$::qw::control(collection_enable_ping)} { $_database cpp_ping; } ::if {$rwb1_debug} {::puts "rwb1_debug,postings_collection_enable,1000.99";} } method postings_collection_enable_semiloops {sargs} { /* { Same as postings_collection_enable except database has semiloops. That is, only the one field changed. Also the shortcut we use to get the list of enabled accounts instead gets only posting accounts as getting enabled branches becomes intractible with semiloops. */ } ::set DestroySkip [::sargs::get $sargs .destroy_skip]; ::set NotifySkip [::sargs::get $sargs .notify_skip]; ::set Account [::sargs::get $sargs .account.address]; ::if {$Account eq ""} { ::qw::bug 314120090813075825 "Expected an \".account.address\" argument."; } ::set OS [$_database cpp_object_structure_load .address $Account]; ::set AccountObjectId [::sargs::get $OS .system.object_id]; ::set AccountClassPath [::sargs::get $OS .system.class_path]; ::set Before [[$Account .postings_collection_is_enabled] odb_get_before]; ::set After [[$Account .postings_collection_is_enabled] odb_get]; ::if {$Before eq $After} { ::return; } $_database cpp_gatgun_hashtable_indexes_flush; ::if {$Before} { /* { Before was true so we are disabling an enabled collection. See comment for method without semiloops. */ } ::if {$::qw::control(active_index_is_enabled)} { $Account cpp_active_index_make_inactive; ::if {$::qw::control(collection_enable_ping)} { $_database cpp_ping; } ::return; } /* { 2.38.0 - not used so eliminated ::set ProgressLimit 0; # 2.34.11 looks like we had an undefined variable ::foreach IndexField $IndexFieldList { ::incr ProgressLimit [$_database cpp_file_record_count \ .path /odb/index$AccountClassPath$IndexField \ .range.begin [::list string $AccountObjectId] \ .range.end [::list string $AccountObjectId] \ ]; } */ } ::foreach IndexField $IndexFieldList { ::if {$::qw::control(active_index_is_enabled)} { $Account$IndexField cpp_active_index_make_inactive; } else { $_database cpp_file_delete_all_records \ .path /odb/index$AccountClassPath$IndexField \ .range.begin [::list string $AccountObjectId] \ .range.end [::list string $AccountObjectId] \ ; } } ::if {$::qw::control(collection_enable_ping)} { $_database cpp_ping; } ::return; } /* { We should not be putting up an operation window on the server. Just like the red progress, the server should pop it up on the relevant workstation. */ } ::array unset _posting_account_object_id_array; ::array unset _visited_account_object_id_array; build_list_of_posting_accounts_below .account $Account; ::set EnabledAccountList ""; ::foreach ObjectId [::array names _posting_account_object_id_array] { ::lappend EnabledAccountList [::qw::odb::address_from_disk .database $_database .address /$ObjectId]; } ::array unset _posting_account_object_id_array; ::array unset _visited_account_object_id_array; # ::set EnabledAccountList [build_list_of_posting_accounts_below .account $Account]; ::set IndexFieldList [get_postings_indexes_with_closure_enabled .class_path $AccountClassPath]; ::set ProgressLimit 0; ::foreach Kid $EnabledAccountList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; ::foreach IndexField $IndexFieldList { ::incr ProgressLimit [$_database cpp_file_record_count \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId] \ .range.end [::list string $KidObjectId] \ ]; } } ::set Progress ""; #2.34.8 ::set ProgressMinimum 1000; ::set ProgressMinimum 10000; ::if {$ProgressLimit>=$ProgressMinimum} { ::set Progress [::QW::PROGRESS::OPERATION [::qw::progress::auto] \ .database_id [$_database cpp_database_id_get] \ .database_path [$_database cpp_database_path] \ .user [$_database cpp_user_name_get] \ .limit $ProgressLimit \ .resolution 119 \ .destroy_skip $DestroySkip \ .operation "creating index records" \ .status "[$_database cpp_database_path] creating postings index records." \ ]; ::qw::finally [::list ::itcl::delete object $Progress]; } ::set SrcFile [$_database cpp_file_factory]; ::qw::finally [::list $SrcFile cpp_destroy]; ::set DstFile [$_database cpp_file_factory]; ::qw::finally [::list $DstFile cpp_destroy]; ::foreach IndexField $IndexFieldList { $DstFile cpp_file_open \ .path /odb/index$AccountClassPath$IndexField \ .range.begin [::list string $AccountObjectId] \ .range.end [::list string $AccountObjectId] \ ; ::foreach Kid $EnabledAccountList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; $SrcFile cpp_file_open \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId] \ .range.end [::list string $KidObjectId] \ ; ::for {::set Record [$SrcFile cpp_record_first];} {$Record ne ""} {::set Record [$SrcFile cpp_record_next $Record];} { ::set After $Record; ::set Key [::sargs::get $Record .key]; ::lset Key 1 $AccountObjectId; ::sargs::var::set After .key $Key; $DstFile cpp_record_insert .after $After; ::if {$Progress ne ""} { $Progress increment; } } } } ::if {$::qw::control(collection_enable_ping)} { $_database cpp_ping; } } method transactions_collection_disable {sargs} { /* { Low level. Deletes all transaction index records in all indexes for a given journal. Assumes journal has just been disabled, independent of the before/after values of .transactions_collection_is_enabled field. */ } $_database cpp_gatgun_hashtable_indexes_flush; ::if {$::qw::control(collection_enable_ping)} { ::qw::finally [::list $_database cpp_ping]; } ::set DestroySkip [::sargs::get $sargs .destroy_skip]; ::set NotifySkip [::sargs::get $sargs .notify_skip]; ::set Journal [::sargs::get $sargs .journal.address]; ::if {$Journal eq ""} { ::qw::bug 314120090609142539 "Expected \".journal.address\" argument."; } ::set OS [$_database cpp_object_structure_load .address $Journal]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::set IndexFieldList [get_transactions_indexes .class_path $ClassPath]; ::foreach IndexField $IndexFieldList { $_database cpp_file_delete_all_records \ .path /odb/index$ClassPath$IndexField \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ; } ::if {$::qw::control(active_index_is_enabled)} { ::set BeforeAIS [$Journal cpp_active_index_structure_get]; ::if {[::sargs::size $BeforeAIS]==0} { # AIS is empty. Nothing more to do. ::return; } # ------------------------------------------------------------ # Unset .transactions in AIS and write or delete the AIS. # ------------------------------------------------------------ ::set AfterAIS $BeforeAIS; ::sargs::var::unset AfterAIS .transactions; ::set AfterAIS [::sargs::prune .structure $AfterAIS]; ::set BeforeRecord [::sargs .key [::list string $ObjectId] .amounts [::list .count 1.0] .data $BeforeAIS]; ::if {[::sargs::size $AfterAIS]==0} { $Database cpp_file_record_delete .path /odb/active_index .before $BeforeRecord; } else { ::set AfterRecord [::sargs .key [::list string $ObjectId] .amounts [::list .count 1.0] .data $AfterAIS]; $Database cpp_file_record_write .path /odb/active_index .before $BeforeRecord .after $AfterRecord; } $Journal cpp_active_index_structure_set .active_index_structure $AfterAIS; } } method transactions_collection_enable {sargs} { $_database cpp_gatgun_hashtable_indexes_flush; ::set DestroySkip [::sargs::get $sargs .destroy_skip]; ::set NotifySkip [::sargs::get $sargs .notify_skip]; ::set Journal [::sargs::get $sargs .journal.address]; ::if {$Journal eq ""} { ::qw::bug 314120090609142521 "Expected \".journal.address\" argument."; } ::set EnabledJournalList [get_enabled_journals_below .journal $Journal]; ::set OS [$_database cpp_object_structure_load .address $Journal]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPath [::sargs::get $OS .system.class_path]; ::if {$::qw::control(active_index_is_enabled)} { /* { In the active_index version we only build the primary .index/id index. Then the secondary indexes will automatically populate from the primary as they become active. */ } ::set IndexFieldList [::list .index/id]; } else { ::set IndexFieldList [get_transactions_indexes .class_path $ClassPath]; } ::set ProgressLimit 0; ::foreach Kid $EnabledJournalList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; ::foreach IndexField $IndexFieldList { ::incr ProgressLimit [$_database cpp_file_record_count \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId] \ .range.end [::list string $KidObjectId] \ ]; } } ::set Progress ""; #2.34.8 ::set ProgressMinimum 1000; ::set ProgressMinimum 10000; ::if {$ProgressLimit>=$ProgressMinimum} { ::set Progress [::QW::PROGRESS::OPERATION [::qw::progress::auto] \ .database_id [$_database cpp_database_id_get] \ .database_path [$_database cpp_database_path] \ .user [$_database cpp_user_name_get] \ .limit $ProgressLimit \ .destroy_skip $DestroySkip \ .operation "creating index records" \ .status "[$_database cpp_database_path] creating transactions index records." \ ]; ::qw::finally [::list ::itcl::delete object $Progress]; } ::set SrcFile [$_database cpp_file_factory]; ::qw::finally [::list $SrcFile cpp_destroy]; ::set DstFile [$_database cpp_file_factory]; ::qw::finally [::list $DstFile cpp_destroy]; ::set ProgressAccumulator 0; ::foreach IndexField $IndexFieldList { $DstFile cpp_file_open \ .path /odb/index$ClassPath$IndexField \ .range.begin [::list string $ObjectId] \ .range.end [::list string $ObjectId] \ ; ::foreach Kid $EnabledJournalList { ::set OS [$_database cpp_object_structure_load .address $Kid]; ::set KidObjectId [::sargs::get $OS .system.object_id]; ::set KidClassPath [::sargs::get $OS .system.class_path]; $SrcFile cpp_file_open \ .path /odb/index$KidClassPath$IndexField \ .range.begin [::list string $KidObjectId] \ .range.end [::list string $KidObjectId] \ ; ::for {::set Record [$SrcFile cpp_record_first];} {$Record ne ""} {::set Record [$SrcFile cpp_record_next $Record];} { ::set After $Record; ::set Key [::sargs::get $Record .key]; ::lset Key 1 $ObjectId; ::sargs::var::set After .key $Key; $DstFile cpp_record_insert .after $After; ::if {$Progress ne ""} { ::incr ProgressAccumulator 1; ::if {$ProgressAccumulator>=2111} { $Progress increment $ProgressAccumulator; ::set ProgressAccumulator 0; } } } } } ::if {$Progress ne ""} { ::if {$ProgressAccumulator!=0} { $Progress increment $ProgressAccumulator; ::set ProgressAccumulator 0; } } ::if {$::qw::control(collection_enable_ping)} { $_database cpp_ping; } } method get_meta {sargs} { ::set ClassPath [::sargs::get $sargs .class_path]; ::if {$ClassPath eq ""} { ::qw::bug 314120090108170056 "Encountered empty class path."; } ::if {![::info exists _metas($ClassPath)]} { ::set _metas($ClassPath) [$_database cpp_object_meta_load .path $ClassPath]; } ::return $_metas($ClassPath); } method get_posting_fields {sargs} { ::set ClassPath [::sargs::get $sargs .class_path]; ::if {$ClassPath eq ""} { ::qw::bug 314120090108170055 "Encountered empty class path."; } ::if {![::info exists _posting_fields($ClassPath)]} { /* { We get the list of posting fields for each class and store them in an array (indexed by class path) for efficiency. */ } ::set Meta [get_meta .class_path $ClassPath]; ::set Paths [::sargs::select_field .structure [::sargs::get $Meta .posting] .field .account]; ::if {$Paths eq ""} { ::qw::bug 314120081216124913 "Could not find posting fields"; } ::set _posting_fields($ClassPath) ""; ::foreach Path $Paths { /* { We reverse the order of the (top-down) list so we can use a foreach and work bottom-up. */ } ::set _posting_fields($ClassPath) [::linsert $_posting_fields($ClassPath) 0 .posting$Path]; } } ::return $_posting_fields($ClassPath); } method get_postings_indexes_with_closure_enabled {sargs} { /* { 2.15.4 We used to process all indexes. We should have skipped indexes that have .is_closure set to 0. These should not be gathered ip. This problem existed in 2.15.0 to 2.15.3 and was detected when nvcheck_index_record_references failed after deleting a transaction from the tutorial database. */ } ::set ClassPath [::sargs::get $sargs .class_path]; ::if {$ClassPath eq ""} { ::qw::bug 314120091014110947 "Encountered empty class path."; } ::if {![::info exists _postings_indexes_with_closure_enabled($ClassPath)]} { /* { We get the list of index fields for each class and store them in an array (indexed by class path) for efficiency. */ } ::foreach Path [get_postings_indexes $sargs] { ::switch -glob -- $Path { */reconcile/* { #*/; } */reference/* { #*/; } default { ::lappend _postings_indexes_with_closure_enabled($ClassPath) $Path; } } } } ::return $_postings_indexes_with_closure_enabled($ClassPath); } method get_postings_indexes {sargs} { ::set ClassPath [::sargs::get $sargs .class_path]; ::if {$ClassPath eq ""} { ::qw::bug 314120090401102721 "Encountered empty class path."; } ::if {![::info exists _postings_indexes($ClassPath)]} { /* { We get the list of index fields for each class and store them in an array (indexed by class path) for efficiency. */ } ::set Meta [get_meta .class_path $ClassPath]; ::set Paths [::sargs::select_field_value .structure [::sargs::get $Meta .postings] .field .odb.type .value index]; ::if {$Paths eq ""} { ::qw::bug 314120090401102957 "Could not find index fields."; } ::foreach Path $Paths { ::lappend _postings_indexes($ClassPath) .postings$Path; } } ::return $_postings_indexes($ClassPath); } method get_transactions_indexes {sargs} { ::set ClassPath [::sargs::get $sargs .class_path]; ::if {$ClassPath eq ""} { ::qw::bug 314120090504103553 "Encountered empty class path."; } ::if {![::info exists _transactions_indexes($ClassPath)]} { /* { We get the list of index fields for each class and store them in an array (indexed by class path) for efficiency. */ } ::set Meta [get_meta .class_path $ClassPath]; #2.28.2 ::set Paths [::sargs::select_field_value .structure [::sargs::get $Meta .transactions] .value .odb.type .value index]; ::set IndexFieldList [::sargs::select_field_value .structure [::sargs::get $Meta .transactions] .field .odb.type .value index]; ::if {$IndexFieldList eq ""} { ::qw::bug 314120090504103636 "Could not find index fields."; } ::foreach IndexField $IndexFieldList { ::lappend _transactions_indexes($ClassPath) .transactions$IndexField; } } ::return $_transactions_indexes($ClassPath); } method account_object_structure_get {sargs} { ::set Address [::sargs::get $sargs .address]; ::if {![::info exists _accounts_by_object_id($Address)]} { ::set _accounts_by_object_id($Address) [$_database cpp_object_structure_load .address $Address]; } ::return $_accounts_by_object_id($Address); } }