# ------------------------------------------------------------ # Copyright (c) 2019-2022 # Q.W.Page Associates Inc. # www.qwpage.com # All rights reserved. # ------------------------------------------------------------ ::namespace eval ::qw::odb2util {}; ::proc ::qw::odb2util::database_backup_log_record {sargs} { /* { 2.34.0 Usage: ::set Logrecord [::qw::odb2util::database_backup_log_record \ .database $_database \ .backup_file $BackupFile \ .command.name "backup_download" \ .command.result "succeeded" \ ]; Creates the log record for database_backup.qw_script and database_download.qw_script. Caller will write the record to /odb/database_backup but that's not the job of this proc. */ } ::set Database [::sargs::get $sargs .database]; ::if {![::qw::command_exists $Database]} { ::qw::bug 314120191210093917 "[::qw::procname] - invalid .database \"$Database\"."; } ::set CommandName [::sargs::get $sargs .command.name]; ::if {$CommandName eq ""} { ::qw::bug 314120191210093918 "[::qw::procname] - invalid .command.name .database \"$CommandName\"."; } ::set BackupFile [::sargs::get $sargs .backup_file]; ::if {$CommandName eq ""} { ::qw::bug 314120191210093919 "[::qw::procname] - invalid .backup_file .database \"$BackupFile\"."; } ::set UniqueId [::clock seconds]_$::qw::odb::log_file_next_id; ::incr ::qw::odb::log_file_next_id; ::set Epoch [::clock seconds]; ::set LogRecord [::sargs \ .program.nameofexecutable [::string tolower [::file normalize [::info nameofexecutable]]] \ .program.version "$::qw_version.$::qw_patch_level.$::qw_build" \ .computer.hostname [::string tolower [::info hostname]] \ .date.hmsdmy [::clock format $Epoch -format "%H:%M:%S %d-%b-%Y"] \ .date.ymdhms [::clock format $Epoch -format %Y%m%d%H%M%S] \ .date.epoch $Epoch \ .log_record_id $UniqueId \ ]; ::sargs::var::set LogRecord .backup_file $BackupFile; ::sargs::var::set LogRecord .command.name "database_download"; ::sargs::var::set LogRecord .command.result "succeeded"; ::sargs::var::set LogRecord .database.file [$Database cpp_database_path]; ::sargs::var::set LogRecord .database.id [$Database cpp_database_id]; ::sargs::var::set LogRecord .database.type [$Database cpp_database_type]; ::sargs::var::set LogRecord .database.size [$Database cpp_database_size]; ::set User [$Database cpp_user]; ::if {$User ne ""} { ::sargs::var::set LogRecord .user.name [$User.name odb_get] ::sargs::var::set LogRecord .user.object_id $User; } ::return $LogRecord; } ::proc ::qw::odb2util::class_path_list {sargs} { # 2.36.0 20220707 ::set Database [::sargs::get $sargs .database]; ::if {![::qw::command_exists $Database]} { ::qw::bug 314120220707104415 "[::qw::procname] - invalid .database argument."; } ::set ObjectId [::sargs::get $sargs .object_id]; ::if {$ObjectId eq ""} { /* { Originally called with an empty object_id and when that happens we start the traversal on the root object. */ } ::set OS [$Database cpp_object_structure_load .path "/OBJECT"]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set ClassPathList [[::qw::procname] $sargs .object_id $ObjectId]; ::return $ClassPathList; } ::set OS [$Database cpp_object_structure_load .object_id $ObjectId]; ::set ClassPathList [::sargs::get $OS .system.class_path]; ::foreach KidObjectId [::sargs::get $OS .system.class_kids] { /* { Recusively process the class object's class kids obtained from .system.class_kids. */ } ::set ClassPathList [::concat $ClassPathList [[::qw::procname] $sargs .object_id $KidObjectId]]; } ::return $ClassPathList; } ::proc ::qw::odb2util::get_list_of_interactive_indexes {sargs} { /* { We just used this during developement to check which indexes are interactive in an application database and in a window database. Just need to be sure we know what is interactive and what isn't. This is convenient for the implementation of active indexes. We just get the index where it is introduced, i.e. not all the deriveds. Traverses all class object structures using .class_kids. We only add the path to the place the collection was introduced. Application Database -------------------- InteractiveIndexes[0]==/OBJECT.odb_deriveds.index/interactive InteractiveIndexes[1]==/OBJECT/SYSTEM/USER.access_tos.index/interactive InteractiveIndexes[2]==/OBJECT/SYSTEM/USER.access_tos.index/interactive/folder InteractiveIndexes[3]==/OBJECT/NEWVIEWS.attachments.index/interactive InteractiveIndexes[4]==/OBJECT/NEWVIEWS/SYSTEM/EMPLOYEE.accounts.index/interactive InteractiveIndexes[5]==/OBJECT/NEWVIEWS/REPORT.accounts.index/interactive InteractiveIndexes[6]==/OBJECT/NEWVIEWS/ACCOUNT.total.kids.index/interactive InteractiveIndexes[7]==/OBJECT/NEWVIEWS/ACCOUNT.addresses.index/interactive InteractiveIndexes[8]==/OBJECT/NEWVIEWS/PAYROLL.employees.index/interactive InteractiveIndexes[9]==/OBJECT/NEWVIEWS/PAYROLL.payruns.index/interactive Workstation Database -------------------- InteractiveIndexes[0]==/OBJECT.odb_deriveds.index/interactive InteractiveIndexes[1]==/OBJECT/SYSTEM/WINDOW.owner.kids.index/interactive InteractiveIndexes[2]==/OBJECT/SYSTEM/WINDOW.clipper.kids.index/z InteractiveIndexes[3]==/OBJECT/SYSTEM/WINDOW.clipper.kids.index/tile InteractiveIndexes[4]==/OBJECT/SYSTEM/WINDOW.focus.kids.index/tab InteractiveIndexes[5]==/OBJECT/SYSTEM/WINDOW.pick.kids.index/interactive Server Database --------------- InteractiveIndexes[1]==/OBJECT/SYSTEM/WINDOW.owner.kids.index/interactive InteractiveIndexes[2]==/OBJECT/SYSTEM/WINDOW.clipper.kids.index/z InteractiveIndexes[3]==/OBJECT/SYSTEM/WINDOW.clipper.kids.index/tile InteractiveIndexes[4]==/OBJECT/SYSTEM/WINDOW.focus.kids.index/tab InteractiveIndexes[5]==/OBJECT/SYSTEM/WINDOW.pick.kids.index/interactive InteractiveIndexes[6]==/OBJECT/SERVER/PORT.database_groups.index/interactive InteractiveIndexes[7]==/OBJECT/SERVER/PORT.databases.index/interactive InteractiveIndexes[8]==/OBJECT/SERVER/DATABASE_GROUP.databases.index/interactive */ } ::set Database [::sargs::get $sargs .database]; ::set ObjectId [::sargs::get $sargs .object_id]; ::if {$ObjectId eq ""} { /* { Originally called with an empty object_id and when that happens we start the traversal on the root object. */ } ::set Milli [::clock clicks -milliseconds]; ::set OS [$Database cpp_object_structure_load .path /OBJECT]; ::set ObjectId [::sargs::get $OS .system.object_id]; ::set InteractiveList [[::qw::methodname] $sargs .object_id $ObjectId]; ::if {0} { ::set Milli [::expr {[::clock clicks -milliseconds]-$Milli}]; ::puts "rwb__debug,build_root_collection_list completed in $Milli milliseconds."; ::set Count 0; ::foreach Item $InteractiveList { ::puts "_root_collection_list($Count)==$Item"; ::incr Count 1; } } ::return $InteractiveList; } ::set OS [$Database cpp_object_structure_load .object_id $ObjectId]; ::set Master [::qw::odb::address_from_object_id .database $Database .object_id $ObjectId]; ::set Meta [::sargs::get $OS .meta]; ::set ClassPath [::sargs::get $OS .system.path]; /* { Note that only the class that introduces a collection is derived directly from ::QW::ODB::COLLECTION, or ::QW::GUI::COLELCTION in the case of a window database. That's why we have no repetition in the list. */ } ::set IndexFieldList [::sargs::select_field_value .structure $Meta .field .odb.type .value "index"]; ::set InteractiveList [::list]; ::foreach IndexField $IndexFieldList { /* { Prefix the collection field path with it's class path. */ } ::set IsInteractive [$Master$IndexField odb_is_interactive]; ::if {$IsInteractive} { ::lappend InteractiveList $ClassPath$IndexField; ::if {[::string first "/interactive" $ClassPath$IndexField]<0} { ::puts "warning: interactive index that does not contain /interactive,index==$ClassPath$IndexField."; } } else { ::if {[::string first "/interactive" $ClassPath$IndexField]>=0} { ::puts "warning Encountered non-interactive index that does contain /interactive,index==$ClassPath$IndexField."; } } } ::foreach KidObjectId [::sargs::get $OS .system.class_kids] { /* { Recursively process the class object's class kids obtained from .system.class_kids. */ } ::set InteractiveList [::concat $InteractiveList [[::qw::methodname] $sargs .object_id $KidObjectId]]; } ::return $InteractiveList; /* { demo_canada: _root_collection_list(0)==/OBJECT.odb_deriveds _root_collection_list(1)==/OBJECT.access_froms _root_collection_list(2)==/OBJECT/SYSTEM/USER.sessions _root_collection_list(3)==/OBJECT/SYSTEM/USER.access_tos _root_collection_list(4)==/OBJECT/SYSTEM/USER.audit_trail _root_collection_list(5)==/OBJECT/SYSTEM/SESSION.audit_trail _root_collection_list(6)==/OBJECT/NEWVIEWS.attachments _root_collection_list(7)==/OBJECT/NEWVIEWS/SYSTEM/TRANSACTION/PAYROLL/PAYCHECK.timecards _root_collection_list(8)==/OBJECT/NEWVIEWS/SYSTEM/PAYRUN.paychecks _root_collection_list(9)==/OBJECT/NEWVIEWS/SYSTEM/PAYRUN.timecards _root_collection_list(10)==/OBJECT/NEWVIEWS/SYSTEM/EMPLOYEE.accounts _root_collection_list(11)==/OBJECT/NEWVIEWS/SYSTEM/EMPLOYEE.paychecks _root_collection_list(12)==/OBJECT/NEWVIEWS/SYSTEM/EMPLOYEE.timecards _root_collection_list(13)==/OBJECT/NEWVIEWS/SYSTEM/EMPLOYEE/CANADA.roes _root_collection_list(14)==/OBJECT/NEWVIEWS/REPORT.accounts _root_collection_list(15)==/OBJECT/NEWVIEWS/ACCOUNT.postings _root_collection_list(16)==/OBJECT/NEWVIEWS/ACCOUNT.total.kids _root_collection_list(17)==/OBJECT/NEWVIEWS/ACCOUNT.addresses _root_collection_list(18)==/OBJECT/NEWVIEWS/ACCOUNT/AR.prices _root_collection_list(19)==/OBJECT/NEWVIEWS/ACCOUNT/SALES.prices _root_collection_list(20)==/OBJECT/NEWVIEWS/JOURNAL.transactions _root_collection_list(21)==/OBJECT/NEWVIEWS/PAYROLL.employees _root_collection_list(22)==/OBJECT/NEWVIEWS/PAYROLL.payruns _root_collection_list(23)==/OBJECT/NEWVIEWS/PAYROLL.paychecks _root_collection_list(24)==/OBJECT/NEWVIEWS/PAYROLL.payroll_reports _root_collection_list(25)==/OBJECT/NEWVIEWS/PAYROLL.timecards _root_collection_list(26)==/OBJECT/NEWVIEWS/PAYROLL/CANADA.roes ws0: _root_collection_class_list(0)==/OBJECT.odb_deriveds _root_collection_class_list(1)==/OBJECT.access_froms _root_collection_class_list(2)==/OBJECT/SYSTEM/WINDOW.owner.kids _root_collection_class_list(3)==/OBJECT/SYSTEM/WINDOW.clipper.kids _root_collection_class_list(4)==/OBJECT/SYSTEM/WINDOW.focus.kids _root_collection_class_list(5)==/OBJECT/SYSTEM/WINDOW.pick.kids */ } } ::proc ::qw::odb2util::odb_path_from_object_structure {sargs} { /* { 2.36.1 - moved to here from database_utilities.qw_script. The reason is that we now correct bad odb_path's in database_reorganize so we needed to cut this method out. Builds object path recursively by appending .system.id to base's path. Goes up to base object to get it's path recursively until root object reached. Used by check/repair object paths. For efficiency we are duplicating the functionality of the odb_path object method without having to load the objects. */ } ::set Database [::sargs::get $sargs .database]; ::set OS [::sargs::get $sargs .object_structure]; ::set BaseObjectId [::sargs::get $OS .system.base_object_id]; ::set BasePath ""; ::if {$BaseObjectId ne ""} { ::set BaseOS [$Database cpp_object_structure_load .object_id $BaseObjectId]; ::set BasePath [[::qw::procname] $sargs .object_structure $BaseOS]; } ::return "$BasePath[::sargs::get $OS .system.id]"; }