#! /usr/bin/env perl # -*- coding: utf-8 -*- # I � Unicode. my $license = << 'EOL'; Copyright (c) 2023, Perforce Software, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. EOL ; use utf8; use 5.014; use strict; use Config; # http://www.nntp.perl.org/group/perl.perl5.porters/2015/01/msg225235.html use warnings; # FATAL => 'all'; use diagnostics '-traceonly'; BEGIN { # todo: Re-enable this check when par_svr_replay is fixed for Windows. # print "Need at least Perl version v5.22.0. Found $^V.\n" and exit 1 # if $^O eq 'MSWin32' and ( ! $^V or $^V lt v5.22 ); print "Need at least Perl version v5.14.0. Found $^V.\n" and exit 1 if ! $^V or $^V lt v5.14; $Carp::MaxArgLen = 1000; $Carp::MaxEvalLen = 1000; $Carp::Verbose = 1; eval 'use Win32::Process qw / NORMAL_PRIORITY_CLASS STILL_ACTIVE /' if $^O eq 'MSWin32'; die "The Win32::Process module must be installed.\n" if $@; eval 'use Win32::Console' if $^O eq 'MSWin32'; die "The Win32::Console module must be installed.\n" if $@; eval 'use Win32::Console::ANSI' if $^O eq 'MSWin32'; die "The Win32::Console::ANSI module must be installed.\n" if $@; eval 'use Win32::ShellQuote "quote_native"' if $^O eq 'MSWin32'; die "The Win32::ShellQuote module must be installed.\n" if $@; return if $ENV{ PERFORCE_JNLTOOL_PLAIN_OUTPUT }; eval { require Pod::Text::Ansi }; { no warnings 'once'; $Pod::Usage::Formatter = $@ ? 'Pod::Text::Termcap' : 'Pod::Text::Ansi' if $^O ne 'MSWin32' } } use autodie; use DB_File; use Pod::Man; use Pod::Html; use DirHandle; use IO::Socket; use File::Copy; use File::Spec; use File::Temp; use IO::Handle; use IO::Select; use Pod::Usage; use Time::Piece; use Data::Dumper; use MIME::Base64; use Scalar::Util; use Getopt::Long; use Sys::Hostname; use File::Basename; use Carp 'longmess'; use charnames ':full'; # For Perl 5.14 without autoload of "\N{}" use Unicode::Normalize; use POSIX ':sys_wait_h'; use Digest::MD5 'md5_hex'; use File::Path 'make_path'; #use Win32::Unicode::Native; use List::Util qw / reduce sum /; #use encoding::warnings;# 'FATAL'; use Encode qw / decode encode from_to /; use FindBin qw / $RealScript $RealBin /; use IO::Compress::Gzip qw / gzip $GzipError /; use IO::Uncompress::Gunzip qw / gunzip $GunzipError /; use Fcntl qw / O_RDONLY SEEK_SET SEEK_CUR :flock /; use Time::HiRes qw / tv_interval gettimeofday usleep /; use Storable qw / dclone retrieve store store_fd fd_retrieve /; use sigtrap qw / handler cleanup_err normal-signals error-signals /; ################################################################################ # CLI opts, globals. my ($bug_check, $crpt_map, $crpt_rcvr, $crpt_rpt, $dashE, $dump_range, $dump_table, $cfg_file, $clientmap, $cluster_preview, $debug, $delim, $demo, $depot_cleaner, $depot_root, $depot_root_cs, $detect_input_type, $dump_file, $dump_format, $dump_lines, $env_report, $explode_ckp, $filter, $from_ckp, $gen_config, $gzip, $help, $iconv, $man, $mk_man, $output_dir, $parallel, $parallel_ckp_replay, $parallel_helper, $parmap, $parse_test, $print_ckp_sizes, $print_ckp_summary, $progress, $quiet, $server_id, $show_encs, $show_schema, $show_schema_rel, $split, $srv_host, $store_schema, $store_schema_fast, $strict_parse, $tablemap, $test_assist, $to_cluster, $uniconv, $uniconv_check, $uniconv_gui, $verify_ckp_chk, $version, $version_num, ); my ( $min_rel, $min_ver ) = qw / 2014.2 38 /; # Minimum compatible server ckp. my ( $max_rel, $max_ver ); # The version of the schema stored in this file. my ( $schema, $d, $jnl_name, $jnl, $output_fmt, $ckp_ver ); my ( $dump_buffsize, $parent ) = ( 65_536, $$ ); my ( @tab_locs_order, %tab_locs, @pids, %fmts, %groups, %term ); my ( $status_tag, $status_start_time, $prog_fh, $status_steps, $status_iter, $status_last ); my $status_child_wait = $ENV{ P4JNLTOOL_CHILDWAIT } // 50_000; my $sort_dumps = $ENV{ P4JNLTOOL_SORT_DUMPS }; my ( $has_pigz, $has_gzip ); my $output_fh; my $ext_shell; my $orig_progname = $0; my $needs_cleanup = 1; my %cluster_types = map { $_, 1 } qw / depot-server workspace-server /; #$Data::Dumper::Deparse = 1; # For debugging eval-compiled functions. my $console; ################################################################################ # Utils, misc my $global_log = sub { }; # A routine error that skips the die-handler's stacktrace printer. sub ERR { say STDERR @_; $global_log->( @_ ); exit 1 } # Doesn't use warn() since the WARN handler would be called. sub WARN { my $msg = "WARNING: $_[ 0 ]\n"; print STDERR $msg; $global_log->( $msg ); 1 } sub uDumper { local $Data::Dumper::Sortkeys = 1 if $sort_dumps; local $Data::Dumper::Indent = 0; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Useqq = 1; local $Data::Dumper::Terse = 1; Dumper @_ } sub get_self { File::Spec->catfile( $RealBin, $RealScript ) } sub slurp($) { # todo: check for errors - io::file isn't affected by autodie my $fh = IO::File->new( shift, '<:raw' ); do { local $/ = undef; <$fh> } } # Normalize to unix line-endings to match the checksum in the depot. sub self_checksum { uc md5_hex slurp( get_self ) =~ s/\R/\012/gr } sub _readlink(_) { my $src = ( $_[ 0 ] // $_ ) =~ s,/,\\,gr; # 09/09/2015 11:01 AM a [b] `dir "$src" 2>&1` =~ /\s+.*?\[(.*)\]\n/; $1 ? $1 =~ s,\\,/,gr : $1 } { no warnings 'redefine', 'prototype'; # proto for Perl 5.14. *_readlink = \ &CORE::readlink unless $^O eq 'MSWin32'; } sub version { $schema //= init_schema(); "$schema->{ SVR_RELEASE } " . join '@', '$File: //depot/main/p4-tools/server/jnltool.pl $$Change: 2543652 $' =~ /\$\S+: (.*?) \$/g; } # 2016.2 + 1404537 = release + 0-padded 20 digit 64bit change number: # 2016200000000000001404537 # Allows for simple numeric comparison (if you have bignums). Assumes there # are no more than 9 releases in a year. sub version_num { $schema //= init_schema(); sprintf '%u%020u', $schema->{ SVR_RELEASE } =~ s/\.//r, '$Change: 2543652 $' =~ /\$\S+: (.*?) \$/ } sub rel_to_ver($) { ${ { qw / 38 2014.2 39 2015.1 40 2015.2 41 2016.1 42 2016.2 43 2017.1 44 2017.2 45 2018.1 46 2018.2 48 2019.1 49 2019.2 50 2020.1 51 2020.2 52 2021.1 53 2021.2 54 2022.1 / } }{ $_[ 0 ] } # list via p4tagl.cc } sub ver_to_rel($) { ${ { qw / 2014.2 38 2015.1 39 2015.2 40 2016.1 41 2016.2 42 2017.1 43 2017.2 44 2018.1 45 2018.2 46 2019.1 48 2019.2 49 2020.1 50 2020.2 51 2021.1 52 2021.2 53 2022.1 54 / } }{ $_[ 0 ] } # todo: dedupe from rel_to_ver } sub rel_to_ver_short { substr rel_to_ver $_[ 0 ], 2, 4 } sub uri_escape($) { $_[ 0 ] =~ s/([^^A-Za-z0-9\-_.!~*'()])/ uc sprintf "%%%02x", ord $1 /egr } sub uri_escape_mailto { # https://tools.ietf.org/html/rfc6068#section-5 uri_escape( $_[ 0 ] ) =~ s/(? /grm } sub bytes_length($) { use bytes; length $_[ 0 ] } sub bytes_lc ($) { use bytes; lc $_[ 0 ] } sub dbg { return if 1; my @c = caller 0; say STDERR "$$ $c[3]: $c[2]: @_" } sub dbg_log { my $fn = shift; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Terse = 1; { local $SIG{ __DIE__ }; eval 'use Data::Printer colored => 1'; } my $fmt = !$ENV{ PERFORCE_JNLTOOL_PLAIN_OUTPUT } && $Data::Printer::VERSION ? Data::Printer::np( \ @_ ) : Dumper \ @_; eval 'no Data::Printer'; $fn->( \ @_, "\n${\ scalar localtime }:\n$fmt" ); # $fn->( [ 'time', ''.scalar( localtime ), 'PID', $$, @_ ], # "\n${\ scalar localtime }, PID $$:\n$fmt" ); } # todo: figure out how to use Pod::Text's 'nourls' option. sub help { my $jt = basename $orig_progname; my $fname = 'B journal and checkpoint processing tool'; << "ABIBLIOPHOBIA" =pod =encoding UTF-8 =head1 NAME F<$jt>; The $fname. =head1 VERSION ${\version} =head1 SYNOPSIS For assistance: =over =item B<--env-report> Execution environment printout. =item B<--help> Basic runtime help. =item B<--man> Long-form assistance. =item B<--version> Print the program version. =item B<--version-num> Print the numeric program version. =back General form: F<$jt> B<--option>[=I] F|F =head1 DESCRIPTION This is a utility program providing commonly-needed functions on checkpoint and journal files. There are major and minor modes of operation. The B major mode aids in converting a stand-alone server checkpoint into a set of checkpoints suitable for creating a B. The B major mode converts non-ASCII data in a free-form non-B checkpoint into B data suitable for making an installation B. =head2 B conversion *** DEPRECATED *** Standard stand-alone B servers contain every piece of data regarding an installation. Some replica types are full copies of the source server, others (like the B and B servers) only have a subset of the data. A B installation combines portions of both approaches, with B/B servers having just the global data like revisions/integrations/jobs, and the Bs holding all client data such as have-lists and pending edits. Refer to the B I L for complete server-configuration topologies. In order to move from a stand-alone server to a B installation, this tool processes a configuration file defining the servers that will initially participate in the cluster and creates a set of checkpoints to be replayed into running servers. See the L section for further specifics. =head2 B conversion B servers by default are not in B, meaning the data inserted into the database is not of a uniform charset. A B server is configured to require that the client connected to it have a charset specified so the server can convert incoming data into B before it is accepted. Converting a server into B means translating the database checkpoint into uniform valid B. While it's possible to perform this charset-conversion with a tool like B and manual edits, it becomes a non-trivial effort the less-uniform the charset usage is and as the size of the checkpoint increases. Prior to F<$jt>'s existence, the conversion process was documented L. To make this process easier, this tool provides a number of ways to cope with large checkpoints and inconsistent charset usage. See the L conversion"> section for further details. =head1 OPTIONS Flags are split into three categories: =head2 For B conversion =over =item B<--to-cluster> B conversion mode. Used with the B<--gen-config>, B<--cfg-file>, B<--server-id>, B<--gzip-output>, B<--output-dir>, B<--cluster-preview> and B<--from-ckp> flags. See L for combined usage. =item B<--gen-config> Prints a sample cluster configuration file based off of the data in the supplied checkpoint. =item B<--cfg-file>=I B config file for B definitions. Defaults to F<\$ckp_name.yaml>. =item B<--server-id>=I Target B to create, as defined in the config file. =item B<--gzip-output> By default, output checkpoint files are written uncompressed. Use this option to have them gzipped. While F<$jt> can do this itself, it can do it faster if the B (L) or B programs are installed. =item B<--output-dir>=I Place all output files in the specified directory unless overridden by files with absolute paths in the config B section. By default, files are put in the same directory as the source checkpoint with the checkpoint name as a prefix. E.g. F would have F for the F table dump. =item B<--cluster-preview> Skip creating the output checkpoint during B processing. Useful for iterating over the configuration mapping rules. =item B<--from-ckp>=I Converting a checkpoint and creating the initial cluster databases may take a while, so if the source server has remained active the journals created since the imported checkpoint will need to be converted and imported into the cluster nodes. The I<--from-ckp> flag is given the name of the original checkpoint and lets F<$jt> find the cached data from the checkpoint import. The value must be the same (full path if previously specified) as was used for the checkpoint conversion. =back =head2 B conversion =over =item B<--uniconv-check> Scan the input checkpoint to locate non-ASCII data. =item B<--uniconv-gui> After non-ASCII data has been located, one or more charsets must be assigned to each record found via B<--uniconv-check>. The Unicode converter GUI is an HTML interface requiring a modern graphical browser. =item B<--uniconv> Perform the final B B conversion, after all records have been processed via B<--uniconv-gui>. =back =head2 Utility functions =over =item B<--corrupt-map> Survey a journal/checkpoint for corruption and print the byte offsets where problems were found. =item B<--corrupt-recover> Chop-out any unparseable sections of the input file and create a new output journal (to STDOUT or B<--dump-file>=I>) =item B<--corrupt-report> Print the byte offsets of any problem sections as full F<$jt> B<--dump-range> invocations. =item B<--dump-file>=I Output file for various --dump-* functions. =item B<--dump-lines>=I Dump the specified line number range. Omit the high number of to go to EOF. =item B<--dump-range>=I Dump the specified byte-range. =item B<--dump-table>=I Dump the specified table from a checkpoint. =item B<--explode-ckp> Split a checkpoint into separate files by table. =item B<-E> Like I<'perl -E'>; eval the supplied string. =item B<--filter>=I B<--delimiter>=I Basic filtering and printout of table data. In the filter string, specify the comma-separated list of tables and comma-separated list list of columns to display. The B<--delimiter> defaults to a space. See L for usage. =item B<--iconv>=I Similar to the F utility. Decodes I off of I and encode I to I. Dies on encoding error. =item B<--parallel>=I Use I CPUs for applicable operations. Defaults to the number reported by the system. =item B<--par-ckp-replay>=I> Performs the equivalent of 'F -jr' for the given checkpoint in the specified P4ROOT, but using N (B<--parallel>) CPUs. Uses the P4D in the PATH. Any symlinks in P4ROOT pointing to database files on other partitions will be honored. =item B<--print-ckp-sizes> Print the size of each table in a checkpoint. Give it a value of 1 to print the output in human-readible format instead of a byte count. =item B<--print-ckp-summary> Print some interesting facts about the checkpoint. Data such as the number of users, age of the installation, etc. =item B<--progress>=I Realtime status. Enabled by default when connected to a terminal. =item B<--show-schema>=I Print a table's keys and types. With just the table name, it uses the latest version. Note that there are holes in the schema where table versions skip a number. An error will be displayed in these cases. =item B<--show-schema-rel>=I Print all the table:version pairs for the given release. The release version is a string like '2017.1'. Optionally one can use the internal version number of a release, like '43'. =item B<--split>=I Chop a journal into I separate pieces. This is done at record boundaries so it's safe to replay them into a server. =item B<--verify-ckp-chksum> Make sure the checkpoint data matches the checksum written when it was created. This is not a replacement for 'p4d -jv' since it performs a subset of p4d's checks. =back =head1 EXAMPLES =over =item Create a template B config using the source checkpoint. F<$jt> B<--to-cluster> B<--gen-config> F > F =item Example B invocation using the default F config: F<$jt> B<--to-cluster> B<--server-id>=I<\$svr_id> F =item B journal conversion: F<$jt> B<--to-cluster> B<--server-id>=I<\$svr_id> B<--from-ckp>=I> F =item Dump the triggers table from a checkpoint. F<$jt> B<--dump-table>=I F =item Print the B server's schema for the first version of the users table, then the latest. F<$jt> B<--show-schema> db.user:0 F<$jt> B<--show-schema> db.user =item Show the table differences between two releases: diff -y <(F<$jt> B<--show-schema-rel> 2016.1) <(F<$jt> B<--show-schema-rel> 2016.2) | colordiff =item Show the list of users and their email in the checkpoint, comma-separated. F<$jt> B<--filter>=I<"tables=db.user fields=USuser,USemail"> B<--delim>=I<,> F =item Break up the given journal into twenty pieces. F<$jt> B<--split>=I<20> F =item Show the human-readable sizes of the individual tables in the checkpoint. F<$jt> B<--print-ckp-sizes>=I<1> F =back =head1 RETURN VALUE 0 on success, non-zero on error. =head1 PREREQUISITES For checkpoint-related functions, the checkpoint must have been created with $min_rel server. It may be possible that a checkpoint made with as early as a 2013.2 server would work (this tool requires C<\@nx@> records,) but it has not been tested. The checkpoint must be uncompressed. This program needs to randomly seek into the file, and that is not possible to do efficiently with a B file. There is no server version limitation for journal file processing. The journal must also be uncompressed. Using compressed journals should be possible for some operations, but has not been implemented. The journals must not have been split into pieces smaller than what the server created (since some records like db.view can be ambiguous for a while and a split in that period would cause data loss) in B mode. Host OS support is the same as what the B server works on, which is currently limited to 64-bit Linux, although it would probably work fine anywhere else with a minimal amount of porting. Perl version 5.14 or later is required. While there isn't much depending on that release's features, note that 5.16 has been end-of-lifed since March 2013, and 5.18 is in maintenance mode as of October 2014. 5.10 is probably the earliest release a backport can easily be done for. See the release history L for more Perl version info. There are mostly no non-core Perl module dependencies. For B conversion, the L module is required. It's probably already installed along with most Perls since it's a CPAN dependency. The L module is used if available to provide colorized manpage (B<--man>) output. One step in the B converter requires a modern HTML5-compliant graphical browser. =head1 B configuration =head2 Getting started: While the B server product documentation has full details on the creation of a cluster, here's a subset as it relates to this tool's part of the task: =over =item Originating-server checkpoint Much of the processing is I/O bound, so a fast disk is useful. A disk for reading and one for writing could improve things. The checkpoint must be uncompressed. While this has an initial space-consuming cost, it enables optimizations that reduce time downstream. Generally, any of the resulting checkpoints will not be as big as the source checkpoint, but they could be so the tool attempts to pre-allocate that much space to help avoid running out of room. =item Workflow =over =item Check for database inconsistencies Run the following command on an existing checkpoint to see if any metadata patches need to be included in the database before cluster creation. =over =item $jt I<--bug-check>=I F =back =item Reserve enough disk space You'll need at most double the space as the source checkpoint takes to create a single node in the cluster. =item Take an uncompressed checkpoint The largest piece of data is the checkpoint. It gets converted into many separate checkpoints, one for each node in the cluster. Journals created by the existing master server will later be converted to catch the cluster up to the current state of the world. =item Run I<--gen-config> to create a sample config file to work with =over =item $jt I<--gen-config> F =back Fill in the blanks. Node names (the B) for the cluster may not reuse existing server names in the source checkpoint. The config file lists any that may exist. Any B servers will be filtered. =item Perform a trial conversion =over =item $jt I<--to-cluster> I<--server-id>=I<\$serverID> F =back Check that the mappings in the config file did what you wanted, otherwise fix the config, delete intermediate files (sans F<\$ckp_file-cache.*> files) and re-run the tool. =item Create the rest of the nodes in the cluster You must run the tool once per server in the cluster you want to create. The main output is the result checkpoint to be imported into the new server. =item Replay the checkpoints into the new servers and bring up the cluster It's Important to remember that after bringing the cluster up for the first time, (that assuming the existing server the seed checkpoint came from has still be operational,) that you shouldn't start creating new data on the cluster until the original master's journals have been converted. Also note that while it's safe to delete the source checkpoint, it's necessary to keep the cache files and the name of the checkpoint to use with the I<--from-ckp> flag when converting journals. Note that if the owner of a client changes during journal processing, that the original workspace server assignment is kept. Note that configurables/triggers/servers conflicting with B settings should not be added to the source server after the initial checkpoint conversion, as those settings are not filtered during journal processing. =item Convert master journals For each journal created by the source server and for each node in the cluster, run the converter: =over =item $jt I<--to-cluster> I<--server-id>=I<\$serverID> I<--from-ckp>=I> F =back Note the speed F<$jt> converts your journals, as if there is a lot of journal data created it may be necessary to start journal conversion immediately after the first checkpoint conversion is complete so F<$jt> keeps up with the incoming data and downtime is minimized. Near the end of journal conversion when F<$jt> has caught up to near realtime, shut off the master server and prepare to switch over to the cluster. =back =item Details about B-to-B mappings Most of the work involved in creating a B is defining the mappings for which target server the originating server's client workspaces will be part of. There are two sections in the configuration file that are expected to have workspaces. Under the B section are the definitions for the servers holding the bulk of client data (have lists, opened files, etc.) Since each server is independent from the others, it's a good idea to divvy up workspaces according to use and load. For example, isolating automation on its own set of servers will improve the performance of servers intended for interactive use. The B type is mostly intended to be the source of revision data for the Bs, so while it's possible to map workspaces there, it's not preferable. There are three precedence levels for specifying which workspaces are placed on what server. From highest to lowest: =over =item - clients/clients-wild =item - users/users-wild =item - groups/groups-wild =back So in other words, you start off with the workspaces owned by users with direct membership in the list of B on a server, then the workspaces owned the users in B, then the actual workspace names in B. For workspaces that might have been mapped to multiple servers, the precedence levels are used to decide where they go. For a tie, the first definition (alphabetically by serverID wins.) See the L section for a full example configuration file. =item Table transformations There are a few changes F<$jt> makes to tables in the input checkpoint: =over =item depot map changes Since a B assumes a shared filesystem, it's likely that the mapping paths of depot definitions (C>) will need to change. The B section of the config file has a list of depot names and the new filesystem path to use. I.e. the entries under B are key/value pairs, where the key is the name of the depot whose map field is to be updated with the value. B sets the I configurable, which means that the cluster it creates needs relative depot maps. =item shelf promotion All shelves are unconditionally changed to be promoted so any member of the cluster can access them. =item building F This database table is created to track exclusive file locks across the cluster. =item assigning serverIDs to workspaces Client workspaces have their C field filled in according to the mappings in the config file. =back =item Output files For an invocation like this, F<$jt> B<--to-cluster> B<--server-id>=I<\$serverID> F Starting with these files, =over =item F =item F =back The following files are created: A copy of the input checkpoint's B table, B and B: =over =item F A list of I<\$serverID>s and the clients assigned to them. See the F<'-debug'> version of the file for some hints about how F<$jt> interpreted the mappings. =item F =item F =item F =item F The list of clients not mapped to any specific server (defaulting to the commit server.) =item F Cached data extracted from the checkpoint. =back And lastly the resulting checkpoint used to create the node in the cluster: =over =item F<\$serverID> =back F<$jt> must be run once per serverID in the config file. =back =head2 Defining the cluster configuration The specification of the nodes in a B are defined via a B (L) document. See the L configuration> section for details on each section. The following is an example: cluster: cluster.id: name # Must follow the same Perforce domain naming convensions preserve-config: yes path-translation: depot: /this_is_converted/depot/... # depot name and new map path unload: /converted/backup/unload/... workspace-server: # Cluster member type automation-wksp_svr: # serverID of instance groups: # List of groups whose user's workspaces go here - g_automation # Group name users-wild: - ^a.* # Wildcard matching user name hardware-wksp_svr: groups: - g_hardware users: # List of users whose workspaces are assigned here - git-fusion_u0 software-wksp_svr: groups: - g_software groups-wild: # Wildcard matching group names - g_git-fusion.* clients: # List of workspace names to map - c_operator-software depot-server: commit-depot_server_svr: clients: - c_admin clients-wild: # Wildcard matching client name - ^floater Use the I<--gen-config> flag to create a sample configuration. =head1 B conversion =head2 Requirements =over =item GUI The B<--uniconv-gui> needs a modern HTML5-compliant graphical browser. Recent versions of Firefox/Chrome are fine as well as L or L. =item storage Disk space usage is proportional to the amount of non-ASCII data found in the checkpoint. At the most, it could be 3x the size of the checkpoint for case-insensitive servers, or 2x for the case-sensitive when the checkpoint is composed entirely of non-ASCII data needing conversion. =item P4D server access The converter must either be run on the server machine itself or on another machine with local file paths matching that of the server (i.e. to run on a different machine, all of the depot roots in the server must be accessible via remote filesystem mounts.) This is because the converter needs to access various depot files in addition to the checkpoint to prepare for conversion. =item input An uncompressed checkpoint from a $min_rel+ server. =item server access Various steps must be run on the server machine itself. E.g. archive file renaming. =back =head2 Output The B converter creates a number of intermediate files along the way. By default they're placed in a directory next to the input checkpoint, named F-jnltool-uniconv>. This may be changed by specifying an alternate directory with B<--output-dir>. The results of conversion are a journal file to be replayed into the existing server as well as a variety of Perl scripts to rename archive files and perform related tasks. =head2 Using the converter =over =item 1. Before starting: =over =item * Upgrade client programs (optional). Starting with 2014.2, client programs have automatic charset detection which avoids the need for some manual configuration when using a Unicode-mode server. I.e. one no longer needs to set L explicitly. See the L for further discussion of this. =item * Check triggers for output text needing re-encoding. =item * Validate existing data integrity. Make sure these come back clean: =over =item * p4 verify -q EEE//... # L in depot file content =item * p4 verify -qU //... # check L =item * p4 verify -qA //... # check L =item * p4d -xv EEEEEEEEEEEEEE # database L =item * p4d -xx EEEEEEEEEEEEEE # L =back =item * Take an uncompessed L. =back =item 2. Inspect the checkpoint for non-ASCII data F<$jt> B<--uniconv-check> F [--depot-root=I> --depot-root-cs=I] This step processes the subset of tables with data sourced from users, skipping tables with data derived from these (this makes it faster, leaving the other data for the full B<--uniconv> step.) Processing progress is output to the screen to give an indication of how much work is left to do. This step can take advantage of multiple cores on a CPU and will complete faster the more cores are available (up until it starts using all of the system I/O.) At the end of this step, the converter will either say that it didn't find any non-ASCII data, or will indicate that it did find some and that you should proceed to the next step. It's necessary to supply the path to the L via the I<--depot-root> flag if the source checkpoint is in a separate directory than the I. Otherwise the B is assumed to be the directory containing the checkpoint. If the depot root path has non-ASCII characters in it, supply the charset with the I<--depot-root-cs> flag so it may be properly converted. =item 3. Inspect checker output, assign charsets to data. F<$jt> B<--uniconv-gui> F Once non-ASCII data has been located, the converter presents it via an HTML GUI served up from F<$jt>. The console output of B<--uniconv-gui> is the address to point the web browser to. It defaults to listening on localhost, but if the machine F<$jt> is running on is different from where the browser is, the public IP address may be specified via the B<--srv-host> flag. The data is passed via unencrypted HTTP. If encryption over the LAN is required, consider using one of these programs to tunnel the B<--uniconv-gui> traffic to the browser: stunnel, ssh -L, spiped or sslwrap. Once the GUI is loaded in the browser, the task is to go through the list of database records containing non-ASCII data, find the correct encoding for them and save the choice. Continue until all records are processed then continue on to the B<--uniconv> step. Since seeing a proper rendering of the encoded data is crucial to choosing the proper charset, having the right fonts installed on the OS running the web browser viewing the conversion GUI is important. If one sees the "not defined" glyph (L<.notdef|https://www.microsoft.com/typography/otspec/recom.htm>, usually looks like an empty box, or a box with a cross through it) then installing another font that has coverage for the data in the record being converted is a good idea. The L or L fonts are good choices that have coverage in multiple language scripts. =item 4. Full checkpoint scan and transcoding F<$jt> B<--uniconv> F Once character sets have been assigned to all the records presented in B<--uniconv-gui>, the full-checkpoint scan may be started. If the server installation being converted has any files stored in tiny.db (files with the +T modifier), first checkpoint this database file and save the path to it in a file named F under the conversion's working directory. F -xf 857 > tiny.ckp # L<-xf|http://answers.perforce.com/articles/KB/2420> =back =head2 Post-conversion steps I. It's best to first perform a dry-run of these steps against a full copy (archives/metadata) of the to-be-converted server so the conversion can be validated and because the conversion is not reversible. =over =item 1. Unprocessed data / manual corrections. In various situations B<--uniconv> can't determine a charset for a record, or requires a person to decide what to do. In these circumstances, a file is created with a list of records. =over =item * F A list of files with non-ASCII names stored in archive triggers. An archive trigger is likely using the file name as a key to look up content, and when file names are changed to UTF-8, the archive trigger will not be able to find the content. Manual recoding of the archive trigger data is necessary here. =item * F This file contains a list of records that were not processed. =item * F This file contains a list of files that could not be processed. =over =item * Orphaned F Records in F might not match any file in the depot if a B was run with the B<-h> flag. These records are generally cleaned up as part of a B on a workspace, but they potentially still exist in a checkpoint and when discovered, will not be included in the conversion. =item * Orphaned F A B might not remove all maps from F. If a record is found there that doesn't match a depot file, it is not included in the conversion. =back =item * F If F has records of integrations coming from remote depots, then the depot file won't have a known charset assigned to it. The assumption is that the charsets are the same, but that might not be true. Inspect these files to make sure there are no errors. Changing the charset of a file on a remote depot hosted on a non-Unicode-mode server will cause errors during integration. =back =item 2. Rotate logs and journals. =item 3. Journal replay The journal file the converter created will delete all the original non-ASCII records it detected and insert UTF-8 versions of them. Replaying the journal into the source server will get it ready for the switch to B. Don't forget to include the B<-s> flag to have the server add the changed records to its journal. p4d -jr -s ckp_file-jnltool-uniconv/out.jnl =item 4. Switch the database to B This command tells the server to perform its own scan of the database to ensure everything is UTF-8. Once it has completed, the server will be in B. p4d -xi =item 5. Archive/partition file renames and RCS keyword digest rewriting =over =item 1. Converted depot file names require renaming on the host OS filesystem. The converter creates a standalone Perl program named F to rename the top-level depot directories, and one called F for the archive files. Note that on Windows, the output files F<$jt> creates must not reside under the B while the renaming script is being run. =item 2. Files with non-ASCII names that contain L need to have their checksums recomputed. The generated F program will do this (it requires B L.) =item 3. Files stored in an L might not be attached to the host computer, so the rename script for them is separate, named F. =item 4. The undocumented F stores archive content within a database file and requires separate handling than standard archives. If F exists, it may be replayed into the server via F -xf 857 F. Any of those files with B will have their checksums fixed with F. =item 5. Partitioned/L clients have their own F database files separate from the main database in B. These files must be renamed when they contain non-ASCII characters in their names. Run the F script to do this. =item 6. L data contains journal records that must be processed. F<$jt> creates a translated copy of the necessary unloaded files as part of the conversion process. It stores them under the conversion's working directory, so they must be put into place under the unload depot's root via the F script, followed by running F to recompute the files checksums. The F expects the rewritten unloaded files to be in a directory relative to its location, so if it is moved it won't be able to find them. =back =item 6. Archive file checksum verification ('p4 verify'). Turn the server on using a private, localhost port so these commands may be run. After running all the journal imports and conversion scripts, it's recommended to re-run the server's built-in consistency checks to validate file and metadata: p4 verify -q //... p4 verify -qU //... p4 verify -qS //...@=shelved_change... p4 verify -qA //... p4d -xx =item 7. Inspect environmental variables, system paths. If server-side settings such as P4ROOT contain non-ASCII data, they must be re-set encoded as UTF-8. =item 8. If the server license file has a 'Customer' field with non-ASCII data in it, request a copy encoded in UTF-8 to use. =item 9. Switch it on! Put the original P4PORT back in place and open it up to your adoring Unicode-loving users and exclaim: '##::::'##::'#######:::'#######::'########:::::'###::::'##:::'##:'####: ##:::: ##:'##.... ##:'##.... ##: ##.... ##:::'## ##:::. ##:'##:: ####: ##:::: ##: ##:::: ##: ##:::: ##: ##:::: ##::'##:. ##:::. ####::: ####: #########: ##:::: ##: ##:::: ##: ########::'##:::. ##:::. ##::::: ##:: ##.... ##: ##:::: ##: ##:::: ##: ##.. ##::: #########:::: ##:::::..::: ##:::: ##: ##:::: ##: ##:::: ##: ##::. ##:: ##.... ##:::: ##::::'####: ##:::: ##:. #######::. #######:: ##:::. ##: ##:::: ##:::: ##:::: ####: ..:::::..:::.......::::.......:::..:::::..::..:::::..:::::..:::::....:: =back =head2 Current limitations There may not be depot maps (or server.depot.root or client.readonly.dir) directories that are absolute paths that point inside of the P4ROOT. There may not be two depots that convert to the same string. Long file names (256+ chars) on Windows. Unloaded archive files do not have updated nx records written with them. Non-ASCII depot root paths are not supported (P4ROOT must be ASCII.) The archive renamer scripts do not preserve permissions. renamer script does not handle site-specific modifications to archive structure (e.g. manual deduplication via symlinks) Perforce additions to the shiftjis charset are not supported. The GUI does not have a way to specify multiple charsets within a single record. Traits, or L may be binary or texual. The distinction is not stored in the database and no conversion is performed on the data. The flavors of EBCDIC like cp1047 are not supported. The converter does not work on journal files, just a checkpoint. Partioned/readonly db.have's for clients/etc are not processed. db.ckphist (p4 journals) is not processed. No assistance for graceful replica conversion is provided. Spec depot suffixes must be the default '.p4s' string. Partitioned/readonly client db.have data is not processed. Unicode data in shelved (db.revsh) adds are not converted. No normalization is performed on the data, but be aware that exact byte sequences for things like passwords can change. Spec depot 'edited by' comments and body and views are not translated. Multiple records encoded in different charsets that encode to the same UTF-8 data are not supported. =head2 Support/diagnostics/logs F and its structed equivalent F have a transcript of operations performed during conversion. =head2 Other reference documentation L L L L =head1 SEE ALSO C (when the L package is installed) L L L L L L =head1 ERRATA The checkpoints consumed by this tool must have been created by the B server. This tool's B output may not be re-processed. This is because the resulting checkpoint does not consistently have journal notes (C<\@nx@> records) and some client table data is out of order (not in contiguous blocks.) The progress meter is not yet implemented for B. Certain record types are not visible to the parser (e.g. dl/mx/ex/nx.) =head1 DEVELOPMENT NOTES F<$jt> must track Perforce Server development and be updated to reflect any changes to the server's schema since the schema is stored inline with F<$jt> in the C<__DATA__> section. To store a server's schema, ensure the latest p4d binary is first in your \$PATH and run: F<$jt> B<--store-schema> During testing it may be worth adding the B<--store-schema-fast> flag to that command to store the schema as a L blob instead of a formatted L string since it reduces initial program load time, making tests run faster. F<$jt> doesn't know the set of table versions that go with a particular server release. When processing records it emits the same version that it got. It has no infrastructure to do upgrades of any sort. In cases where it needs to fabricate new records (i.e. F) it uses the latest version available. This does mean that some logic is hard-coded to a particular record version, and there are some attempts to make sure that those are detected when a schema change happens (see C.) The field names for tiny.db are made up since they're not defined by the server schema. For those externally wishing to modify this tool, a test suite is available. =head1 LICENSE $license =head1 AUTHOR Jason Gibson / L =head1 AVAILABILITY The $fname can be installed via B packages at L, with the F name. It is also available at L. If F<$jt> has been customized and added to a local server, be sure to add it as type I so the B<--version> string reflects the new depot path and changelist number. =head1 END ===================== = ======== === = ==== ====== === = ==== ===== === = ==== ==== = === = ==== == === = ======== === === = ======== = = ============= === = ============= === ===================== =cut ABIBLIOPHOBIA ; } sub fh_from_str($) { open my $fh, '<', \ $_[ 0 ]; $fh } sub mk_man { my $type = shift; my %types = ( html => 1, man => 1, pdf => 1 ); ERR "Unknown manual type '$type', must be one of: " . join ' ', sort keys %types unless exists $types{ $type }; # Without the autoflushing, we randomly find half of the content unwritten. my ( $in, $out ) = map { $_->autoflush( 1 ); $_ } map File::Temp->new, 0, 0; print $in help; # Beware that things like 'p4d(1)' will go to some random page. pod2html "--infile=$in", "--outfile=$out", '--quiet' if $type eq 'html'; unlink 'pod2htmd.tmp' if -e 'pod2htmd.tmp'; Pod::Man->new( release => version, section => 1, center => 'jnltool doc', name => 'Perforce journal tool' ) ->parse_from_file ( "$in", "$out" ) if $type eq 'man'; if( $type eq 'pdf' ) { eval 'use Pod::Pdf'; ERR "Must have 'Pod::Pdf' installed to create PDFs." if $@; # todo: instead of using a temp file, use a temp dir and a file named # 'jnltool' so the PDF title is set to that. Note that this leaves # the file in "/tmp/$in.pdf" and calls exit(). pod2pdf( $in ); } slurp $out } # Format some runtime data about Perl and some modules that might help Support. sub env_report { my ( $tv, $tc ) = ( 'Tool version', 'Tool checksum' ); my %cfg = ( $tv => version, $tc => self_checksum, DB_File => $DB_File::VERSION, Encode => $Encode::VERSION, Storable => $Storable::VERSION, 'Unicode::Normalize' => $Unicode::Normalize::VERSION, 'TERM' => $ENV{ TERM } // 'termtype unknown', map { $_ => $Config{ $_ } } qw / usethreads version osname osvers perlpath myuname / ); $cfg{ pigz } = $has_pigz ? `pigz -V 2>&1` =~ s/\n//r : 'none'; $cfg{ gzip } = $has_gzip ? ${ [ `gzip -V` ] }[ 0 ] =~ s/\n//r : 'none'; my $l = 0; map { $l = length $_ if length $_ > $l } keys %cfg; sprintf "%s\n", join "\n", map { "$_: " . ' ' x ( $l - length $_ ) . ($cfg{ $_ } // 'UNDEF')} $tv, $tc, grep { $_ ne $tc and $_ ne $tv } sort { lc( $a ) cmp lc( $b ) } keys %cfg; } sub diskspace_avail { my $dir = shift; if( $^O eq 'MSWin32' ) { my @df = `dir "$dir" 2>&1`; # 'dir e:' 9 Dir(s) 22,471,057,408 bytes free my ( $bytes ) = $df[ -1 ] =~ /((?:\d+,?)+) bytes free/; return $bytes =~ s/,//gr / 1024 } my $P = $^O eq 'solaris' ? '' : '-P'; my @df = `df -k $P "$dir" 2>&1`; return 1 unless @df and "@df" !~ /not found/; # no df output? no df? # Normalize Solaris and FreeBSD's output to what Linux and Darwin use. # # df -P -k . # Filesystem 1024-blocks Used Available Capacity Mounted on # /dev/mapper/ubuntu--vg-root 889094212 411090540 432817216 49% / $df[ 0 ] = $df[ 0 ] =~ s/(?:Avail|avail)\s+/Available /r =~ s/Mounted on/Mounted_on/r =~ s/kbytes/1024-blocks/r =~ s/used/Used/r =~ s/capacity/Capacity/r; my %dfh; @dfh{ split /\s+/, $df[ 0 ] } = $df[ 1 ] =~ /(.*?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%\s+(.*)$/; die "Parse error reading 'df' output." unless $dfh{ Available } =~ /^\d+$/ or $dfh{ Mounted_on } =~ m,^/, or @df > 2 or grep { not defined } values %dfh; $dfh{ Available } } # Try to ensure we don't run out of disk space midway through a large # operation. fallocate should be on most modern linuxes, but only # functions on a small number of filesystems. Still, it's an easy # way to guarantee we've got enough space. sub pre_allocate_unix { my ( $bytes, $file ) = @_; my $kilos = int $bytes / 1024; # First try this since it actually allocates the file. my $fao = `fallocate -l ${kilos}K "$file" 2>&1`; if( $fao =~ /fallocate: \Q$file\E: fallocate failed:/ ) { unlink $file; return 0; } return 1 if ! length $fao and -s $file == $bytes; # todo: may need to use a different dir based off of the Cluster config file. return 0 if $kilos >= diskspace_avail dirname $file; # Then do a sparse allocation. It doesn't guarantee anything though. open my $fh, "+>", $file; seek $fh, $bytes, SEEK_SET; truncate $fh, $bytes; seek $fh, 0, SEEK_CUR; 1 } sub pre_allocate_win { my ( $bytes, $file ) = @_; # Using fsutil would be nice, but the 'setvaliddata' flag requires # administrative access and it's the only one that seems to actually # do a real allocation - the sparse flag didn't appear to work. # Should be available everywhere we run: # https://technet.microsoft.com/en-us/library/cc753059.aspx # return 1 if `fsutil file setvaliddata "$file" $bytes` # =~ /File .* is created/; # `fsutil sparse setflag "$file"`; # `fsutil sparse setrange "$file" 0 $bytes`; require Win32API::File; require Win32::API; my $fh = Win32API::File::createFile( $file, 'w' ) or die "No createFile '$file': $^E\n"; Win32API::File::setFilePointer( $fh, $bytes, 0 ) or die 'No setFilePointer: ' . fileLastError(); Win32::API->Import( 'kernel32', 'BOOL WINAPI SetEndOfFile( HANDLE hFile )' ); SetEndOfFile( $fh ) or die "no SetEndOfFile: $?"; Win32API::File::CloseHandle( $fh ) or die 'No closehandle' . regLastError(); } sub pre_allocate { $^O eq 'MSWin32' ? pre_allocate_win @_ : pre_allocate_unix @_ } # Connected to a terminal? Used to set the default for the progress bar. sub interactive { -t STDOUT && -t STDERR ? 1 : 0 } # Try to discover control sequences used to animate the progress bar. sub init_term_control { # todo: do we need term fns even without progress? return unless $progress; goto WIN if $^O eq 'MSWin32'; $progress = 0, warn "Progress bar unavailable (no 'tput' program.)\n" and return if `tput -V 2>&1` !~ /ncurses/; sub tput(_) { my $o =`$ext_shell "tput $_" 2>&1`; ERR "Unsupported terminal for --progress. Couldn't read '$_' capacity." if $o =~ /unknown terminfo capability/; warn "Warning: terminal support for --progress is incomplete (no '$_'.)" if $o eq '' && ( $ENV{ TERM } // '' ) ne 'dumb'; # ignore Emacs shell 'dumb' term. $o } @term{ qw / width invis clr down up norm / } = map tput, qw / cols civis el cud1 cuu1 cnorm /; return; WIN: $console = Win32::Console->new( STD_OUTPUT_HANDLE() ); @term{ qw / width invis clr down up norm / } = ( [ $console->Info ]->[ 10 ], "\e[?25l", "\e[2K", "\e[1E", "\e[1F", "\e[?25h" ); } sub term_width { my $w = $term{ width }; $w = 80 unless $w && $w =~ /^\d+$/; $w > 80 ? 80 : $w } sub status; # Note that the tag must not contain newlines. sub status_start { return 0 unless $progress; ( $status_last, $status_tag, $status_start_time ) = ( 0, shift, scalar localtime ); ( $status_steps, $status_iter ) = ( 1, 1 ) unless $status_steps; print $prog_fh $term{ invis }; status 1, 100; } sub status_elapsed { ( scalar( localtime ) - $status_start_time )->pretty } # Draw a status bar. Must be run after status_start and before status_end. # Takes two values as input; the current position and the max value. sub status { return unless $progress; my ( $st, $tag, $pos, $max ) = ( $status_start_time, $status_tag, @_ ); return syswrite $prog_fh, "$pos $max\n" if $parent != $$ || $parallel_helper; die "bad status_unix usage: $pos, $max: " . longmess if $pos > $max; my $dtp = ( scalar( localtime ) - $st )->pretty; my $pct = ( $pos / $max ) / $status_steps + ( $status_steps - $status_iter ) / $status_steps; return if $pct <= $status_last; $status_last = $pct; my $hdr = sprintf "% 3u%% ", $pct * 100; state $w = term_width; my $bar = '|' x ( $w * $pct ); my $pad = ' ' x ( $w - length $bar ); print $prog_fh "\r$term{clr}$tag $dtp\n" . "\r$term{clr}$hdr [ $bar $pad ]$term{up}" } sub reap { my $fn = shift; my %procs = map { $_->{ PID } => $_ } @_; for my $pid ( keys %procs ) { next if $procs{ $pid }->{ alive }->(); my ( $rc, $sig, $core ) = $procs{ $pid }->{ ret }->(); die "Error: child process exited on signal $sig!" if $sig; die "Error: child process dumped core!" if $core; die "Error: child process had a non-zero exit!" if $rc; delete $procs{ $pid }; @pids = grep { $pid != $_->{ PID } } @pids; } $progress ? $fn->( map { $_->{ pfh } } values %procs ) : usleep $status_child_wait; values %procs } # Collect status from child processes. The par() function sets up a file handle # to them that they send the pos/max pair back to us. # todo: allow --parallel=0 sub status_agg { state $last = [ gettimeofday ]; my $r = [ 1, 1 ]; my $fn = sub { my @args = @_; $r = reduce { [ $$a[ 0 ] + $$b[ 0 ], $$a[ 1 ] + $$b[ 1 ] ] } map { [ ( $$_[ 0 ] / $$_[ 1 ] ) * 100, 100 ] } grep { @$_ } ( ( [ 100, 100 ] ) x ( $parallel - @args ) ), map { [ /^(\d+) (\d+)\n/ ] } grep defined, map { $_->seek( 0, SEEK_SET ); # Ensure there's something written. [ <$_> ]->[ -1 ] } @args; $last = [ gettimeofday ], status $$r[ 0 ], $$r[ 1 ] if $r && .01 < tv_interval $last; }; reap $fn, @_ } # todo: do this regardless of $progress? sub status_heading { say $prog_fh @_; $global_log->( @_ ); 1 } sub status_warn { my $op = $progress; $progress = 1; status_heading "**** NOTICE: @_"; $progress = $op; } sub status_end { return 0 unless $progress; status 1, 1; ( $status_steps, $status_iter ) = ( 1, 1 ); print $prog_fh "$term{down}$term{clr}\r$term{norm}"; } sub set_console_title_win { Win32::Console::ANSI::Title( shift ) } sub set_console_title { $^O eq 'MSWin32' ? set_console_title_win( shift ) : '' } # Wrap up a function in progress. sub status_run { my ( $hdr, $fn, @args ) = @_; my $title = set_console_title $hdr; status_start $hdr; my @ret = $fn->( @args ); status_end; set_console_title $title; @ret > 1 ? @ret : $ret[ 0 ] } ################################################################################ # Schema functions # Parse the server's schema. Fills in gaps with a MISSING placeholder. sub get_schema { my ( $recurse, $table, $version ) = @_; return if ( $version // 0 ) == -1; $recurse //= 0; sub arg { ( $_[ 0 ] ? "$_[0]:$_[1]" : '' ), @_ } sub dbschema { scalar `p4 dbschema $_[0] 2>&1`, splice @_, 1 } sub err_chk { $_[ 0 ] =~ /client error/ ? die $_[ 0 ] : @_ } sub fix_missing { $_[ 0 ] ? $_[ 0 ] : "... table $_[1]\n... version $_[2]\n" . "... type0 MISSING\n" } sub sep_tabs($) { split "\n\n", shift } sub sep_cols(_) { split "\n" } sub filt_cols { grep { /\.\.\. (:?table|version|name|type|key)/ } @_ } sub get_ids { map { /(\w+) (.*)/ } @_ } sub add_count { @_, 'tab_count', $_[ -2 ] =~ /type(\d+)/ } sub collect { my %h = @_; \ %h } sub vers { $_[ 0 ] ? ( $_[ 1 ], get_schema( 1, $_[ 1 ]->{ table }, $_[ 1 ]->{ version } - 1 ) ) : $_[ 1 ] } map { vers $recurse, $_ } map { collect add_count get_ids filt_cols sep_cols } sep_tabs fix_missing err_chk dbschema arg $table, $version } # Rearrange the schema into a hierarchy. sub xform_schema { my %s; map { my $tab = delete $_->{ table }; my $ver = delete $_->{ version }; $s{ $tab }->{ $ver } = $_ } @_; \ %s } sub get_svr_version { `p4 -v rpc=5 help u 2>&1` =~ /Rpc.*server2 = (\d+)/s; $1 } sub get_svr_release { `p4 -ztag info` =~ m!verVersion P4D/[^/]+/(\d+.\d)!; $1 } sub add_extra_metadata { $_[ 0 ]->{ SVR_VERSION } = get_svr_version; $_[ 0 ]->{ SVR_RELEASE } = get_svr_release; $_[ 0 ]->{ 'tiny.db' }->{ 0 } = { tab_count => 1, key0 => 'yes', # lbrfile/lbrRev name0 => 'TIkey', type0 => 'key', name1 => 'TIvalue', type1 => 'octets' }; # dbjournal.h DbJournalOp dbjournal2.cc fetchlbr/putlbr # todo: lbrrev not rcs? # @dl@ @//depot/f@ @1.1@ 3 @submit@ @@ $_[ 0 ]->{ dl }->{ 0 } = { other => 1, table => 'dl', tab_count => 4, version => 0, name0 => 'DLlbrFile', type0 => 'key', key0 => 'yes', name1 => 'DLlbrRev', type1 => 'key', key1 => 'yes', name2 => 'DLlbrType', type2 => 'int', name3 => 'DLcommand', type3 => 'key', name4 => 'DLfilespec', type4 => 'key' }; # @ex@ 3599 1457655653 $_[ 0 ]->{ ex }->{ 0 } = { other => 1, table => 'ex', tab_count => 1, version => 0, name0 => 'EXpid' , type0 => 'int64', name1 => 'EXtime', type1 => 'int64' }; $_[ 0 ]->{ mx }->{ 0 } = { other => 1, table => 'mx', tab_count => 1, version => 0, name0 => 'MXpid' , type0 => 'int64', name1 => 'MXtime', type1 => 'int64' }; # @pk@ @graph@ @//graph/simple@ 1 @receive-pack@ @//graph/objects/50/583dbd1ab2dea55a150323560d91825e68afd1@ $_[ 0 ]->{ pk }->{ 0 } = { other => 1, table => 'pk', tab_count => 4, version => 0, name0 => 'PKdepotName', type0 => 'key', key0 => 'yes', name1 => 'PKrepoName' , type1 => 'key', key1 => 'yes', name2 => 'PKaccess' , type2 => 'int', name3 => 'PKcommand' , type3 => 'key', name4 => 'PKfilespec' , type4 => 'key' }; # @nx@ 7 1457659841 @38@ 0 0 0 0 0 @rsh@ @1@ @@ @@ @@ # @vv@ 1 @db.counters@ @journal@ @2@ # todo: 'value' should be of type text? vv is only used for change counter so this is ok. $_[ 0 ]->{ vv }->{ 0 } = { other => 1, table => 'vv', tab_count => 3, version => 0, name0 => 'VVversion', type0 => 'int', name1 => 'VVtable' , type1 => 'key', name2 => 'VVkey' , type2 => 'key', name3 => 'VVvalue' , type3 => 'key' }; $_[ 0 ]->{ SVR_RELTAB_MAP } = dclone ( $schema->{ SVR_RELTAB_MAP } // {} ); $_[ 0 ]->{ SVR_RELTAB_MAP }->{ $_[ 0 ]->{ SVR_VERSION } } = { map { chomp; s/^\.\.\. \w+ //r } grep { /^\.\.\. (?:table|version)/ } `p4 dbschema 2>&1` }; $_[ 0 ] } sub check_DATA { my $self = shift; ERR "Script '$orig_progname' must be writable." if $self and not -W $self; eval { my $pos = fileno *DATA }; ERR "The __DATA__ section is missing." if $@; } sub todo_check_schema { exists( $_[ 0 ]->{ 'db.excl' }->{ 2 } ) ? 'db.excl schema change' : undef } sub has_p4d { ( `p4d -V 2>&1` // '' ) =~ m,Rev\. P4D/, } sub mk_tmp_svr { ERR "Must have p4d in path." unless has_p4d; $ENV{ P4CONFIG } = tmpnam; # Make sure we don't get unintended configuration. $ENV{ P4CHARSET } = 'none'; # Don't pollute ~/.p4enviro $ENV{ P4PORT } = "rsh:p4d -r \"${\ File::Temp::tempdir CLEANUP => 1 }\" -L log.txt -i"; } # Save the schema metadata into our __DATA__ section so we can run without # a connection to the server. # todo: keep full server version used to store the schema in the DATA section. sub store_schema { my $fast = shift; mk_tmp_svr; my $schema = add_extra_metadata xform_schema get_schema 1; ERR "Can't store schema for unsupported server version." if $schema->{ SVR_VERSION } < $min_ver; my $me = todo_check_schema $schema; ERR "Maintenance required: $me" if $me; my $self = get_self; check_DATA $self; my $data_pos = tell *DATA; open *DATA, '+<', $self; truncate *DATA, $data_pos; close *DATA; open *DATA, '+<', $self; seek *DATA, $data_pos, SEEK_SET; local $Data::Dumper::Indent = 3; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Terse = 1; local $Data::Dumper::Useperl = 1; # Avoid unnecessary integer quoting. local $Data::Dumper::Sortkeys = sub { my %dat = %{ $_[ 0 ] }; # lexical sort for the top level tables return [ sort keys %dat ] if exists $dat{ 'db.depot' }; # descending numeric sort for the table versions return [ sort { $b <=> $a } keys %dat ] if not exists $dat{ 'tab_count' }; # Order each record with its metadata followed by field name/type my %keys = map { /key(\d+)/ => 1 } grep { /key\d+/ } keys %dat; my @fields = map { ( "name$_", "type$_", exists( $keys{ $_ } ) ? "key$_" : () ) } 0 .. $dat{ tab_count }; delete @dat{ @fields }; delete $dat{ tab_count }, return [ 'tab_count', sort( keys %dat ), @fields ] if exists $dat{ tab_count }; [ sort( keys %dat ), @fields ] }; local $Storable::canonical = 1; $fast ? store_fd $schema, \ *DATA : print DATA Dumper $schema; close *DATA; } sub init_schema { check_DATA; my $data_pos = tell *DATA; my $s = eval { local $SIG{ __DIE__ } = undef; fd_retrieve *DATA }; seek *DATA, $data_pos, SEEK_SET and $s = eval do { local $/; } unless $s; die "init_schema eval: $@" if $@; seek *DATA, $data_pos, SEEK_SET; $s }; sub types { map { $_[ 0 ]->{ "type$_" } } 0 .. $_[ 0 ]->{ tab_count } } sub names { map { $_[ 0 ]->{ "name$_" } } 0 .. $_[ 0 ]->{ tab_count } } sub keyns { my $ts = $_[ 0 ]; grep defined, map { $ts->{ "key$_" } ? $ts->{ "name$_" } : undef } 0 .. $ts->{ tab_count } } sub show_schema { my $tab = shift // ''; my ( $ver ) = $tab =~ /:(\d+)/; $tab =~ s/:.*//; ERR "Unknown table '$tab'." unless exists $schema->{ $tab }; $ver //= -1 + keys %{ $schema->{ $tab } }; ERR "Unknown version '$ver'." unless exists $schema->{ $tab }{ $ver }; ERR "Specified version does not exist." if $schema->{ $tab }{ $ver }{ type0 } eq 'MISSING'; my $tabfmt = sub { my ( $tab, $ver ) = @_; my ( $ln, $lt ) = ( 0, 0 ); my $ts = $schema->{ $tab }{ $ver }; map { $ln = length $_ > $ln ? length $_ : $ln } names $ts; map { $lt = length $_ > $lt ? length $_ : $lt } types $ts; my $txt = join "\n", map { my $np = ' ' x ( 2 + $ln - length $ts->{ "name$_" } ); my $tp = ' ' x ( 2 + $lt - length $ts->{ "type$_" } ); my $k = $ts->{ "key$_" } ? $tp . ' # key' : ''; $ts->{ "name$_" } . $np . $ts->{ "type$_" } . $k } 0 .. $ts->{ tab_count } }; say $tabfmt->( $tab, $ver ); } sub show_schema_rel { my $rel = $_[ 0 ] =~ /\./ ? ver_to_rel $_[ 0 ] : $_[ 0 ]; my $map = $schema->{ SVR_RELTAB_MAP }; ERR "Server version '$rel' unknown." unless exists $map->{ $rel }; say join "\n", map { "$_: $map->{ $rel }->{ $_ }" } sort keys %{ $map->{ $rel } }; } ################################################################################ # Parsing =comment Different approaches... =cut # unquoted field, could be negative integer sub parse_unq { ${ $_[ 1 ] } =~ /(?<= )(-?\p{PosixGraph}+) /gc ? $1 : undef } sub parse_octets { parse_unq( @_ ), parse_unq @_ } # Most of the time keys don't have @ in them, but some do like db.ixtext. # Those columns are treated as text fields in the record compiler. sub parse_key { ${ $_[ 1 ] } =~ /\G@([^\0@]*?)@ /gc ? $1 : '' } sub parse_text { my $p = pos( ${ $_[ 1 ] } ) // 0; pos( ${ $_[ 1 ] } ) += 3 and return '' if $p == index( ${ $_[ 1 ] }, '@@ ' , $p ) || $p == index( ${ $_[ 1 ] }, "@@\n", $p ); MATCH: goto READ unless ${ $_[ 1 ] } =~ /\G@((?:[^@]++|@@)+)@[^@]/gc; return $1 =~ s/@@/@/gr if defined $1; READ: my $line = readline $_[ 0 ]; ${ $_[ 1 ] } .= $line; die "text field corruption, line $., offset " . tell $jnl if $line =~ /^@[a-z]{2}@/; goto READ if -1 == index( $line, '@' ) && ! eof $_[ 0 ]; pos( ${ $_[ 1 ] } ) = $p; goto MATCH; } # p4 dbschema | grep '\.\.\. type' | cut -d ' ' -f 3 | sort | uniq # todo: prior table versions could have other types? my %parsern = ( int => 'parse_unq', int64 => 'parse_unq', int8 => 'parse_unq', intv => 'parse_unq', key => 'parse_key', octet => 'parse_unq', octet20 => 'parse_unq', octets => 'parse_octets', RCS => 'parse_key', text => 'parse_text', MISSING => "die 'U+1F4A9 U+274A U+260F'" ); # todo: test no-backtracking additions (?>...) my $unq_re = '(-?[^ \012\015\0]+)'; my $octs_re= '(\d+) ([^ \012\015\0]+)'; my $key_re = '@([^\0@]*?)@'; #my $key_re = '@([^\n]*?)@'; #my $key_re = '@((?:[^@]++|@@)+)@'; # [^@] #my $key_re = '@((?:[^@]++|@@)+)@[^@]'; my %parserr = ( int => $unq_re, int64 => $unq_re, int8 => $unq_re, intv => $unq_re, key => $key_re, octet => $unq_re, octet20 => $unq_re, octets => $octs_re, RCS => $key_re, text => "die 'U+1F4A9 U+274A U+260F'", MISSING => "die 'U+1F4A9 U+274A U+260F'" ); my ( %ps, %to_jnl_fmt_fns ); sub sniff_jnl_lineending { state $le; $le = $^O eq 'MSWin32' ? '"\\r\\n"' : '"\\n"' unless $jnl; # 'jnltool -E' return $le if defined $le; seek $jnl, 0, SEEK_SET; ( $le ) = readline( $jnl ) =~ /(\R+)$/; local $Data::Dumper::Useqq = 1; local $Data::Dumper::Terse = 1; $le = Dumper $le; chomp $le; $le } sub compile_matcher { # Some tables have key types that should actually be text since they're # multi-line, or are an exception to the general rule that keys don't # contain @@s. todo: could instead whitelist a portion of tables like rev* # or have. my %key_is_text = map { ( "db.$_" => 1 ) } qw / ixtext message trigger uxtext /; foreach my $tab ( grep { ! /^SVR/ } sort keys %$schema ) { foreach my $ver ( sort keys %{ $schema->{ $tab } } ) { my $ts = dclone $schema->{ $tab }->{ $ver }; map { $ts->{ $_ } = 'text' if $key_is_text{ $tab } and $ts->{ $_ } eq 'key' } keys %$ts; my @types = map { $parserr{ $_ } ? $_ : die "Unknown database type: $_" } types $ts; my $one_line = ! grep { $_ eq 'text' } @types; my %name_is_octets = map { $ts->{ "name$_" }, 1 } grep { $ts->{ "type$_" } eq 'octets' } 0 .. $ts->{ tab_count }; my @names = map { $name_is_octets{ $_ } ? ( "_$_", $_ ) : $_ } map { $_ ? $_ : 'MISSING' } names $ts; # Note: only done for general journal parsing. my $check = $strict_parse ? 'die "parse count mismatch on line $., ' . 'offset " . tell $jnl if grep { ! defined } values %r;' . "\n" : ''; if( $one_line ) { my $fn_pre = ''; my $rec_re = join( ' ', map { $parserr{ $_ } } @types ) . ( ( $tab eq 'mx' || $tab eq 'ex' ) ? '\R' : ' \R' ); $ps{ $tab }{ $ver } = eval 'sub { local *__ANON__ = ' . "'parse-${tab}_$ver';" . 'my %r; @r{ qw / ' . "@names" . '/ } = ${ $_[ 1 ] } =~ /' . "\\G$rec_re/gc; # $tab $ver\n" . " $check \\ %r\n}"; die "eval error on $tab:$ver: $@" if $@; } else { $ps{ $tab }{ $ver } = eval 'sub { my %r; @r{ qw / ' . "@names" . ' / } = ( ' . join( ', ', map { "$parsern{ $_ }( \@_ )" } @types ) . " ); $check;" . '\ %r }' } die "eval error on $tab:$ver: $@" if $@; my $fn = $ts->{ other } ? "'\@' . \$_[ 0 ]->{ op } . '\@ ' . " : "'\@' . \$_[ 0 ]->{ op } . '\@ ' . \$_[ 0 ]->{ version } . " . "' ' . '\@' . \$_[ 0 ]->{ table } . '\@ ' . "; my $name_offset = 0; for( my $i = 0; $i < @types; $i++ ) { my ( $type, $name ) = ( $types[ $i ], $names[ $i + $name_offset ] ); $fn .= '$_[ 0 ]->{ ' . "$name } . " . ( $ts->{ other } && ( $i == @types - 1 ) && ( $tab eq 'mx' || $tab eq 'ex' ) ? "''": "' '" ) if 'int' eq substr( $type, 0, 3 ) or $type =~ 'octet(?:20)*$'; $fn .= "'\@' . \$_[ 0 ]->{ $name } =~ s/\@/\@\@/gr . '\@ '" if $type eq 'key' or $type eq 'RCS' or $type eq 'text'; $name_offset++ if $type eq 'octets'; $fn .= "\$_[ 0 ]->{ $name } . ' ' . \$_[ 0 ]->{ $names[ $i + $name_offset ] } . ' '" if $type eq 'octets'; $fn .= ' . ' if $i != @types - 1; } $fn .= ' . ' . sniff_jnl_lineending; # todo: should recalculate octets size for cases when we create the record # from scratch and track if we're creating a pre-base64 octets server version # journal. Also, a single byte octet encodes as two characters in hex and two # characters in base64. In that case, and zero length, use hex. $to_jnl_fmt_fns{ $tab }{ $ver } = eval "sub { $fn }" unless $names[ 0 ] eq 'MISSING'; die "eval error on $tab:$ver: $@: $fn" if $@; } } } # Make a field_name -> data hash out of the list of fields in a parsed record sub mk_rec_hash { my ( $op, $ver, $tab ) = @_; my $ts = $schema->{ $tab }->{ $ver }; my %gh = ( op => $op, version => $ver, table => $tab, map { $ts->{ "name$_" } => $_[ $_ + 3 ] } 0 .. $ts->{ tab_count } ); \ %gh } sub parse { my ( $fh, $cb, $range, $full_parse ) = @_; seek $fh, $range ? $$range[ 0 ] : 0, SEEK_SET; my ( $cbr, $prog ); my %others = map { $_, 1 } qw / dl ex mx pk nx vv /; until( $range ? ( $$range[ 1 ] == tell $fh ) : eof $fh ) { my ( $op, $ver, $tab, $rec ); my $line = readline $fh; say "line '$line'" if $d > 1; # ( $op, $ver, $tab ) = grep defined, $line =~ /^@(\w+)@ (?:$key_re|$unq_re) (?:$key_re|$unq_re) */gc; $op = substr $line, 1, 2; next if $op eq 'nx'; pos( $line ) = 1 + index( $line, ' ' ), $ver = 0, $tab = $op, goto PARSE if $full_parse && $others{ $op }; next if $others{ $op }; $ver = substr $line, 5, -5 + index $line, ' ', 5; my $taboff = 5 + 2 + length $ver; $tab = substr $line, $taboff, - $taboff + index $line, '@', $taboff; pos( $line ) = $taboff + 2 + length $tab; PARSE: die "bazooka $$range[0],$$range[1] '$tab' '$ver': '$line'" . longmess unless $ps{ $tab }{ $ver }; $rec = $ps{ $tab }{ $ver }( $fh, \ $line ); @$rec{ qw / op version table / } = ( $op, $ver, $tab ); $cbr = $cb->( $rec ); return $cbr if $cbr; status tell( $fh ) - $$range[ 0 ], $$range[ 1 ] - $$range[ 0 ] if ++$prog % 10_000 == 0; } $cbr; } # todo: use inotify or somesuch. add a test. sub parse_follow { my ( $fh, $cb, $range, $full_parse ) = @_; START: flock $fh, LOCK_EX; # Lock to coordinate with writing p4d processes. my $r = parse $fh, $cb, $range, $full_parse; flock $fh, LOCK_UN; sleep 1, $range = [ tell( $fh ), -s $fh ], goto START if ! defined( $r ) || $r != -1; $r } sub tab_is_empty { my ( $fh, $range ) = @_; seek $fh, $$range[ 0 ], SEEK_SET; my $line = readline $fh; die "tab_is_empty did not start at the right place." if $line !~ /^\@nx@/; $line = readline $fh; $line =~ /^\@ex@ / ? 1 : 0 } sub parse_tablesummary_nx { my ( $fh, $line ) = @_; # @nx@ 4 1425590593 @40@ 2 0 0 0 0 @db.changex@ @@ @@ @@ @@ my $op = parse_key $fh, $line; my $type = parse_unq $fh, $line; die "Unsupported nx record in table summary: $$line" if $type != 4; my $ts = parse_unq $fh, $line; my $svrver = parse_key $fh, $line; my $tabver = parse_unq $fh, $line; my $tabover = parse_unq $fh, $line; my $uncnt = parse_unq $fh, $line; my $xxx = parse_unq $fh, $line; my $yyy = parse_unq $fh, $line; my $tabname = parse_key $fh, $line; my %h = ( op => $op, type => $type, timestamp => $ts, serverLevel => $svrver, tableVersion => $tabver, tableOldVersion => $tabover, unlockCount => $uncnt, tableName => $tabname ); \ %h } # todo: parse more of the fields sub parse_ckp_nx { my $fh = shift; seek $jnl, 0, SEEK_SET; my $line = readline $jnl; # DbJournalNoteType # @nx@ 2 1440099002 @40@ 1 1 0 0 0 @.@ @-@ @@ @@ @@ # @nx@ 0 1515090486 @45@ 1 0 0 0 0 @/ram/t4w2@ @journal@ @@ @@ @@ # ver dbflag jnltype x x x svrroot jnlName # @nx@ 0 1515090486 @45@ 1 0 0 0 0 @/ram/t4w2@ @journal@ @@ @@ @@ my $op = parse_key $fh, \ $line; my $type = parse_unq $fh, \ $line; die "Unsupported nx record ($type) in ckp header: $line" unless $type == 0 || $type == 2; my $ts = parse_unq $fh, \ $line; my $svrver = parse_key $fh, \ $line; my $dbflag = parse_unq $fh, \ $line; my $jnlType = parse_unq $fh, \ $line; parse_unq $fh, \ $line; parse_unq $fh, \ $line; parse_unq $fh, \ $line; my $p4root = parse_key $fh, \ $line; my $jnlName = parse_key $fh, \ $line; my %h = ( op => $op, type => $type, timestamp => $ts, serverLevel => $svrver, dbFlags => $dbflag, p4root => $p4root, jnlName => $jnlName ); $h{ jnlType } = $jnlType if $type == 2; \ %h } sub sniff_ckp_case { my $h = parse_ckp_nx $jnl; return 0 if $h->{ dbFlags } & 0x0001; return 1 if $h->{ dbFlags } & 0x0002; return 2 if $h->{ dbFlags } & 0x0004; } sub sniff_tabver { my ( $fh, $range ) = @_; seek $fh, $$range[ 0 ], SEEK_SET; my $line = readline $fh; my $nx = parse_tablesummary_nx $fh, \ $line; $nx->{ tableVersion }, $nx->{ tableName } } ################################################################################ # Data-dumping functions operating on a checkpoint/jnl range sub systell { my $pos = sysseek $_[ 0 ], 0, SEEK_CUR; $pos eq '0 but true' ? 0 : $pos } sub sysreadline { my $fh = $_[ 0 ]; my ( $pos, $buf, $line ) = systell $fh; use bytes; MORE: my $nr = sysread $fh, $buf, 1024; die 'sysreadline eof' if $nr == 0; # todo: other endings? todo: broken for mixed/midline LE's? $line .= $buf and goto MORE unless $buf =~ /\R+/g; $line .= substr $buf, 0, pos $buf; sysseek $fh, $pos + length( $line ), SEEK_SET; $line } # todo: fix ctrl-z short read and sigpipe errors sub dump_range_fn { my ( $ifh, $l, $h, $ofh, $fn, $full_line ) = ( $_[ 0 ], $_[ 1 ][ 0 ], $_[ 1 ][ 1 ], @_[ 2 .. 4 ] ); my ( $loc, $n, $m, $buff, $ret ) = ( $l, 0, int( ( $h - $l ) / $dump_buffsize ) || 1 ); die "dump_range_fn invalid range: $l is not greater than $h" if $h <= $l; use bytes; do { sysseek $ifh, $loc, SEEK_SET; my $bs = ( $h - $loc < $dump_buffsize ) ? $h - $loc : $dump_buffsize; my $nr = sysread $ifh, $buff, $bs; # todo: eotab error here? $buff .= sysreadline $ifh if $full_line && -1 == index $buff, "\n", -1 + length $buff; my $blen = length $buff; $ret = $fn->( $ofh, \ $buff, $loc, $blen, $ifh ); $loc += $blen; status $n - 1, $m if ++$n % POSIX::ceil( $m / 100 ) == 0; } while $loc < $h; die "l $l, loc $loc, h $h" if $loc > $h; $ret; } sub dump_range($$$) { dump_range_fn @_, sub { my $r = syswrite $_[ 0 ], ${ $_[ 1 ] }; # todo: retry on short write die "syswrite short $r: $!" if !defined( $r ) or $r != length ${ $_[ 1 ] }; } } sub dump_lines { my ( $l, $h ) = split ',', shift; $h //= 2 ** 64 - 1; while( <$jnl> ) { print if $. == $l .. $. == $h; last if $. > $h; } 0 } sub jnl_fmt_parse { parse $jnl, sub { my $rec = shift; say "$rec->{ op } $rec->{ version } $rec->{ table } " . join ' ', map { $rec->{ $_ } } names $schema->{ $rec->{ table } }->{ $rec->{ version } }; 0 }, @_, 1 } sub perlout_fmt_parse { local $Data::Dumper::Terse = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Indent = 0; print '['; parse $jnl, sub { print Dumper( $_[ 0 ] ) . ','; 0 }, @_, 1; print ']'; } # Make explicit the boundaries of the dumped regions. sub dump_range_sep($$) { local $Data::Dumper::Terse = 1; local $Data::Dumper::Quotekeys = 0; print '['; # todo: don't do this via ENV. dump_range_fn @_, sub { print Dumper( ${ $_[ 1 ] } ) . ',' }, $ENV{ P4JNLTOOL_LINEDUMP }; print ']'; } sub storable_fmt_parse { parse $jnl, sub { store_fd $_[ 0 ], $output_fh; 0 }, @_, 1; } sub explode_ckp { my $ckp_name = shift; map { status_run "exploding $_", sub { dump_range $jnl, $tab_locs{ $_ }, IO::File->new( "$ckp_name-$_", '>:raw' ) } } sort keys %tab_locs; } ################################################################################ # Functions operating on parsed records # Slurp an entire table. sub get_tab_recs { my @gs; parse $jnl, sub { push @gs, @_; 0 }, $tab_locs{ $_[ 0 ] }; @gs } sub check_job037630 { # null-keyed group # @pv@ 7 @db.group@ @@ @@ 8062592 0 8951072 0 1 seek $jnl, $tab_locs{ 'db.group' }->[ 0 ], SEEK_SET; readline $jnl; # nx readline( $jnl ) =~ /\@pv@ \d+ \@db.group@ @@ @@ / } sub check_job066050 { # null-keyed db.have # @pv@ 2 @db.have@ @@ @@ 0 0 seek $jnl, $tab_locs{ 'db.have' }->[ 0 ], SEEK_SET; readline $jnl; # nx readline( $jnl ) =~ /\@pv@ \d+ \@db.have@ @@ @@ 0 0 / } sub check_job002522 { # depot file name with backslash parse $jnl, sub { $_->{ RXdfile } =~ m,\\, }, $tab_locs{ 'db.revcx' } } sub bug_check { my $bug = shift; my %bugs = ( all => [ sub { }, 'foo' ], job037630 => [ \ &check_job037630, 'null-keyed db.group' ], job066050 => [ \ &check_job066050, 'null-keyed db.have' ], ); ERR "Bug check '$bug' unknown. Valid checks are: " . join ', ', sort keys %bugs unless exists $bugs{ $bug }; my @r; push @r, [ "$_: $bugs{ $_ }->[ 1 ]", $bugs{ $_ }->[ 0 ]->() ] for $bug eq 'all' ? sort keys %bugs : $bug; status_warn "Found bug $$_[ 0 ]" for grep { $$_[ 1 ] } @r; } sub xform_group_recs { my %gs; map { $gs{ $_->{ GRgroup } }->{ $_->{ GRuser } } = $_->{ GRtype } } get_tab_recs 'db.group'; %gs } # Note that this function only returns the data first loaded into %groups # for first-level queries. It caches it as an optimization for expand_cdmaps. sub users_in_group { my ( $group, $subs ) = @_; %groups = xform_group_recs unless %groups; sub is_user { $_[ 0 ] & 0x0001 || $_[ 0 ] & 0x0004 } sub is_subgroup { $_[ 0 ] & 0x0002 } state %uc; $uc{ $group } //= [ sort grep { is_user $groups{ $group }{ $_ } } keys %{ $groups{ $group } } ]; return @{ $uc{ $group } } unless $subs; return sort grep { is_user $groups{ $group }{ $_ } } keys %{ $groups{ $group } } unless $subs; my %seen; my @us; my $find_subs; $find_subs = sub { my $g = shift; push @us, grep { is_user $g->{ $_ } } keys %$g; map { $seen{ $_ } = 1; $find_subs->( $groups{ $_ } ) } grep { is_subgroup( $g->{ $_ } ) && ! exists $seen{ $_ } } keys %$g; }; $seen{ $group } = 1; $find_subs->( $groups{ $group } ); my %uus; $uus{ $_ } = 1 for @us; sort keys %uus } sub to_jnl_fmt { $to_jnl_fmt_fns{ $_[ 0 ]->{ table } }{ $_[ 0 ]->{ version } }->( $_[ 0 ] ) } sub dump_range_parsed { my $table = shift; my @rs; my ( $ver, $tab ) = sniff_tabver $jnl, $tab_locs{ $table }; my $fn = $to_jnl_fmt_fns{ $tab }{ $ver }; parse $jnl, sub { push @rs, $fn->( @_ ); 0 }, $tab_locs{ $table }, 1; @rs } # todo: have a syntax highlighting mode? sub filter { my $filter = shift; my $fn = shift // $output_fmt; my $is_jnl = shift; # xxx todo: $fn can't be optional now sub mk_re { qr/$_[0]="([^"]+)"|$_[0]=(\S+)/ } sub extract { map { split ',' } grep defined, $_[ 0 ] =~ mk_re $_[ 1 ] } my @tables = extract $filter, 'tables'; my @fields = extract $filter, 'fields'; my @filter = extract $filter, 'filter'; sub col_to_tabs { # todo: use SVR_RELTAB_MAP. my %tab_to_maxver = map { # Assume that the latest ver has all the possible columns. $_ => ( sort { $a <=> $b } keys %{ $schema->{ $_ } } )[ -1 ] } grep /^db\./, keys %$schema; my %cols; map { my $tab = $_; map { push @{ $cols{ $_ } }, $tab } # Column names aren't unique. names $schema->{ $tab }->{ $tab_to_maxver{ $tab } }; } keys %tab_to_maxver; } col_to_tabs; # = != =~ !~ # --filter='filter="CHdesc=~"' # --filter='filter="CHdesc=~/foo/ || REdfile=~,//depot/foo/.*,' # --filter=tables=db.change,db.rev # --filter=cols=CHdesc,REdfile # add implicit table list if filter includes colnames my $tab_re_str = join '|', @tables; my $tab_re = qr/$tab_re_str/; my $flt_re_str = join '|', @filter; my $flt_re = qr/$flt_re_str/; =comment my $ckp_tabs = sub { my $fn = shift; map { $fn->( $tab_locs{ $_ } ) } @tables }; my $flt = sub { parse $jnl, sub { my $r = shift; return if $is_jnl and $r->{ table } !~ $tab_re; $fn->( @fields ? grep defined, map $$r{ $_ }, @fields : @filters ? : to_jnl_fmt $r ); 0 }, @_, 1 }; ! $is_jnl ? $ckp_tabs->( $flt ) : $flt->( [ 0, -s $jnl_name ] ); =cut #=comment # --filter='tables=db.user filter=USuser=~abc' my $op = $progress; $progress = 0; map { parse $jnl, sub { my $r = shift; return if $is_jnl and $r->{ table } !~ $tab_re; $fn->( @fields ? grep defined, map $$r{ $_ }, @fields : to_jnl_fmt $r ); 0 }, $_, 1 } @tables ? map $tab_locs{ $_ }, @tables : [ 0, -s $jnl_name ]; $progress = $op; #=cut 0 } sub filter_return { my @r; filter shift, sub { push @r, [ @_ ]; 0 }, shift; \ @r } sub range_size { my $sz; map { $sz += $$_[ 1 ] - $$_[ 0 ] } @_; $sz } sub tabs_by_size { map { [ $_, range_size $tab_locs{ $_ } ] } sort { range_size( $tab_locs{ $a } ) <=> range_size( $tab_locs{ $b } ) || $a cmp $b } keys %tab_locs } sub fmt_num_IEC { my $n = shift; my @pfx = qw / bytes KiB MiB GiB TiB /; map { return sprintf '%.2f %s', $n / $$_[ 1 ], $$_[ 0 ] if $n > $$_[ 1 ] } map { [ $pfx[ $_ ], 1024 ** $_ ] } reverse 1 .. 4; "$n bytes" } sub print_ckp_sizes { my $fmt = shift; my $total; my $tlenmax = 0; map { my $l = length; $tlenmax = $l if $l > $tlenmax } keys %tab_locs; map { my ( $tab, $sz ) = @$_; $total += $sz; say "$tab: " . ( ' ' x ( $tlenmax - length $tab ) ) . ( $fmt ? fmt_num_IEC $sz : $sz ); } tabs_by_size; say "\ntotal: " . ( $fmt ? fmt_num_IEC $total : $total ); } # table, low, high range, position in buffer of char, location between l&h, size of buffer containing char sub find_rec_from_pos { use bytes; my ( $jnl, $c ) = @_; my ( $rsa, $loc, $buf, $bounce, $rec_start, $rec_end ) = ( 256, $c->{ pos }, '', 0 ); START: my $rs = $rsa; $loc -= $rs; if( $loc <= $c->{ b } ) { $bounce++; $loc = $c->{ b }; $rs = $c->{ pos } - $loc; } die "find_rec_from_pos fell down a pit: ${\ Dumper $c }\n'$buf'" if $bounce > 1; sysseek $jnl, $loc, SEEK_SET; my $tbuf; sysread $jnl, $tbuf, $rs; # todo: guard against short reads. $buf = $tbuf . $buf; # todo: what about beginning of table or near mx. don't want nx/mx. # todo: dl/ex/mx/nx/pk/vv my $mkr = $loc == $c->{ l } ? '@pv@ ' : "\n\@pv@ "; $rec_start = rindex $buf, $mkr, length $buf; goto START if $rec_start == -1; my $rec; my $offset = $loc == $c->{ l } ? 0 : 1; $rec_start += $loc + $offset; my $r = parse $jnl, sub { $rec = $_[ 0 ] }, [ $rec_start, 0 ], 1; die "no pg?" unless $r; $rec_end = tell $jnl; # not systell. matches parse usage. die "augh: $rec_start, $rec_end, " . Dumper $c if $rec_start > $c->{ pos } ; $rec, $rec_start, $rec_end; } sub check_ckp_ver; sub is_client; sub print_ckp_summary { # todo: number of uses traits, uses swarm, uses commons, etc. todo: use # todo: account for TZ? $progress = 0; my $date_pretty = sub { my ( $r, $q ) = @_; my @is = ( 60 * 60 * 24 * 365, 'years' , 60 * 60 * 24 * 30 , 'months', 60 * 60 * 24 , 'days' , 60 * 60 , 'hours' ); join ', ', map { "$_->[ 1 ] $_->[ 0 ]" } grep { $_->[ 1 ] } map { ( $q, $r ) = ( int $r / $is[ $_ ], $r % $is[ $_ ] ); [ $is[ $_ + 1 ], $q ] } grep { $_ % 2 == 0 } 0 .. @is - 1; }; # Not using Time::Seconds::pretty() because it doesn't do years right for dates # like 973021334. "Age of installation: 5609 days, 23 hours, 32 minutes, 37 seconds" # Age of installation: ${\ (Time::Seconds->new( time ) - # Time::Seconds->new( [ get_tab_recs 'db.change' ]-> # [ -1 ]->{ CHdate } ) )->pretty } my %rpl; $rpl{ $_->{ CFsname } }++ for get_tab_recs 'db.config'; my $tab_after_change; map { $tab_after_change = $tab_locs_order[ $_ + 1 ] if $tab_locs_order[ $_ ]->[ 0 ] eq 'db.change' } 0 .. @tab_locs_order - 1; my $c = { l => $tab_locs{ 'db.change' }->[ 0 ], pos => $tab_after_change->[ 1 ] }; $c->{ b } = $c->{ l }; my $age = range_size( $tab_locs{ 'db.change' } ) > 100 ? $date_pretty->( time - [ find_rec_from_pos $jnl, $c ]->[ 0 ]->{ CHdate } ) : 'unknown'; # todo: include actual date, not just the duration in 'Age of installation' print << "EOS" Server release: ${\ rel_to_ver check_ckp_ver $jnl, $min_rel, $min_ver, $max_rel, $max_ver } Depot count: ${\ scalar get_tab_recs 'db.depot' } User count: ${\ scalar get_tab_recs 'db.user' } Protections count: ${\ scalar get_tab_recs 'db.protect' } Trigger count: ${\ scalar get_tab_recs 'db.trigger' } Client count: ${\ scalar grep { is_client $_->{ DOtype } } get_tab_recs 'db.domain' } Replica count: ${\ scalar grep { $_ ne 'any' } keys %rpl } Security level: ${\ ( join( '', map { $_->{ CFvalue } } grep { $_->{ CFname } eq 'security' } get_tab_recs 'db.config' ) or 'unset' ) } Case: ${\ ${ { 0 => 'sensitive', 1 => 'insensitive', 2 => 'hybrid' } }{ &sniff_ckp_case } } Date created: ${\ scalar localtime &parse_ckp_nx->{ timestamp } } Unicode: ${\ ( ( &parse_ckp_nx->{ dbFlags } & 0x0008 ) == 8 ? 1 : 0 ) } Change counter: ${\ ( join( '', map { $_->{ COvalue } } grep { $_->{ COname } eq 'change' } get_tab_recs 'db.counters' ) or '0' ) } Age of installation: $age Line-ending: ${\ sniff_jnl_lineending } Largest four tables: ${\ join ', ', map { "$$_[ 0 ] " . fmt_num_IEC $$_[ 1 ] } splice @{ [ reverse tabs_by_size ] }, 0, 4 } EOS } ################################################################################ # Specific transforms on records sub rec_hash_to_array { my $rec = shift; my ( $op, $ver, $tab ) = delete @{ $rec }{ qw / op version table / }; my $ts = $schema->{ $tab }->{ $ver }; my @id = $ts->{ other } ? $op : ( $op, $ver, $tab ); ( @id, map { $ts->{ "type$_" } eq 'octets' ? ( $rec->{ '_' . $ts->{ "name$_" } }, $rec->{ $ts->{ "name$_" } } ) : $rec->{ $ts->{ "name$_" } } } 0 .. $ts->{ tab_count } ) } # note: keep jnlfns copy up to date. sub wrk_isXOpen { $_[ 0 ] & 0x00040 } sub wrk_Added { $_[ 0 ] == 0 || $_[ 0 ] == 3 || $_[ 0 ] == 5 || $_[ 0 ] == 8 } # TODO: Hardcoded to a specific record version. Must be updated if the # table changes. sub mk_excl { my $rec = shift; return if wrk_Added $rec->{ WOaction } or not wrk_isXOpen $rec->{ WOtype }; my %nrec = ( op => $rec->{ op }, version => 1, table => 'db.excl', EXdfile => $rec->{ WOdfile }, EXclient => $rec->{ WOclient }, EXuser => $rec->{ WOuser } ); \ %nrec } # note: keep jnlfns copy up to date. sub is_client { 'c' eq lc chr $_[ 0 ] } # unloaded C, normal c sub assign_client_serverid { my ( $svr_to_type, $cmap, $rec ) = @_; my $dst = $cmap->{ $rec->{ DOname } } // ''; $rec->{ DOserverid } = $dst if is_client( $rec->{ DOtype } ) && ! length( $rec->{ DOserverid } ) && ( $svr_to_type->{ $dst } // '' ) ne 'depot-server'; $rec } sub promote_shelves { my $rec = $_[ 0 ]; sub is_shelved { $_[ 0 ] & 0x0002 } sub promote { $_[ 0 ] |= 0x0010 } $rec->{ CHstatus } = promote $rec->{ CHstatus } if is_shelved $rec->{ CHstatus }; $rec } sub clients_by_user { # todo: note that the owner might not actually exist in db.user my %cbu; parse $jnl, sub { my $rec = shift; push @{ $cbu{ $rec->{ DOowner } } }, $rec->{ DOname } if is_client $rec->{ DOtype }; 0 }, $tab_locs{ 'db.domain' }; \ %cbu; } sub clients_by_group { } ################################################################################ #todo: '<:raw:bytes'? sub open_ckp($) { open my $jnl, '<:raw', shift; $jnl } sub is_jnl { state $set; return $set if defined $set; $set = 0; # default to ckp seek $jnl, 0, SEEK_SET; $. = 0, return $set = 1 if <$jnl> =~ /^\@nx@ 2/ && <$jnl> =~ /^\@vv@ /; seek $jnl, 0, SEEK_SET; $set = 1 if <$jnl> =~ /^\@[a-z]{2}@ \d+ \@db.|^\@nx@ 4 .* \@tiny\.db@ /; $set = 1 if eof $jnl; # For single-line journals in test suite. $. = 0; $set } sub is_ckp { not is_jnl } sub ckp_readable { my $ckp = shift; my $line = readline $ckp; seek $ckp, 0, SEEK_SET; $line =~ /^@\w\w@ \d+ /; } sub check_ckp_ver { my ( $ckp, $rel_min, $ver_min, $rel_max, $ver_max ) = @_; my $ver_too_old = "Ckp must be made with at least a $rel_min p4d."; seek $jnl, 0, SEEK_SET; # @nx@ 0 1414080373 @41@ 1 0 0 0 0 @ckp@ @journal@ @@ @@ @@ my $line = readline $ckp; my $op = parse_key $ckp, \ $line; ERR $ver_too_old unless $op eq 'nx'; parse_unq $ckp, \ $line; parse_unq $ckp, \ $line; my $ver = parse_key $ckp, \ $line; ERR $ver_too_old if $ver < $ver_min; ERR "Ckp must be made with at most a $rel_max p4d." if $ver > $ver_max; seek $ckp, 0, SEEK_SET; $ver } # reuse ckp? sub check_jnl_ver { } ################################################################################ #sub find_path # search for path without scanning. probe? =comment header: @nx@ 0 1414080547 @39@ 1 0 0 0 0 @ckp@ @journal@ @@ @@ @@ @nx@ 4 1414080547 @39@ 1 0 0 0 0 @db.config@ @@ @@ @@ @@ @ex@ 19779 1414080547 middle: @nx@ 4 1414080547 @39@ 7 0 -516396968 0 0 @db.user@ @@ @@ @@ @@ @pv@ 7 @db.user@ @jgibson@ @jgibson@@jgibson-linux@ @@ 14140805 @ex@ 19779 1414080547 trailer: @nx@ 4 1414080547 @39@ 0 0 0 0 0 @db.message@ @@ @@ @@ @@ @ex@ 19779 1414080547 @nx@ 1 1414080547 @39@ 0 0 0 0 0 @@ @@ @@ @@ @@ @ex@ 19779 1414080547 mx can precede ex before nx @pv@ 0 @db.property@ @key@ 2 1 @user@ @20@ 1414089792 @uadmin asdf@ @mx@ 16004 1414089913 @ex@ 16004 1414089913 @nx@ 4 1414089913 @39@ 0 0 0 0 0 @db.message@ @@ @@ @@ @@ =cut sub find_tables { our ( $ckp_name, $ckp ) = @_; my $sz = -s $ckp_name; sub tab_from_nx { $_[ 0 ] =~ /^\@nx@ 4 .*@(db.*?)@/ ? $1 : undef } # DJN_TABLE_SUMMARY sub tab_from_pv { $_[ 0 ] =~ /^\@pv@ \d+ \@(.*?)@ / ? $1 : undef } sub get_op { $_[ 0 ] =~ /^@([a-z]{2})@ \d+ / ? $1 : undef } sub is_marker { $_[ 0 ] =~ /^\@(?:e|m)x@ \d+ / } # -1 search backwards, 0 forwards. > 1 match and returns file pos sub is_tab_end { my $cur_tab = shift; readline $ckp; # only consider complete lines my $pos = tell $ckp; my $rec = readline $ckp; # pv/mx/ex/nx my $op; # Could still be in the middle of a record # we want the pv before (mx)/ex/nx $rec = readline $ckp until $op = get_op $rec; # mx in table, or mx at end before ex/nx if( $op and $op eq 'mx' ) { $rec = readline $ckp; $op = get_op $rec; # ex in curtab or next, too far either way return -1 if $op eq 'ex'; my $tab = tab_from_pv $rec; # mx in next or curtab return $tab ne $cur_tab ? -1 : 0; } die "Surprise found in checkpoint. 'p4 admin dump'?" unless $cur_tab; # ex,nx or pv after curtab return -1 if $op ne 'pv' or $cur_tab ne tab_from_pv $rec; # Move to the next record, possibly through a single multi-line record. # Could be mx/ex before the nx. $rec = readline $ckp; # we're in the last pv, need to skip mx/ex -> nx do { $pos = tell $ckp; $rec = readline $ckp } until $rec =~ /^@(pv|nx)@/; $op = get_op $rec; return 0 if 'pv' eq $op and $cur_tab eq tab_from_pv $rec; # if we were already in nx or pv, then too we're far return -1 unless 'nx' eq get_op $rec; # $1? $pos } readline $ckp; # ckp nx header my $pos = tell $ckp; # todo: should be $begin_tab_pos my $rec = readline $ckp; my $tab = tab_from_nx $rec; my @ts; my ( $l, $h ) = ( $pos, $sz ); # todo: check $mid+1's until( $sz - $l < 100 ) { # last two lines are 71 bytes my $npos; # we're at nx. empty table if( tab_from_nx $rec ) { $rec = readline $ckp; my $op = get_op $rec; $npos = tell $ckp and goto DONE if $op and $op eq 'ex'; } my $mid = int( ( $l + $h ) / 2 ); die "find_tables l == h" if $l == $h; seek $ckp, $mid, SEEK_SET; $npos = is_tab_end $tab; # next position $h = $mid if $npos == -1; # todo: should be mid - 1? $l = $mid + 1 if $npos == 0; next unless $npos > 0; seek $ckp, $npos, SEEK_SET; DONE: $rec = readline $ckp; my $ntab = tab_from_nx $rec; push @ts, [ $tab, $pos, $npos ]; $tab = $ntab; $h = $sz; $l = $npos; $pos = $npos; } seek $ckp, 0, SEEK_SET; @ts } sub client_from { $_[ 0 ] =~ m,@*//([^/]++), ? $1 : undef } # Depends on all of these records being single-line. sub map_table_by_client { my ( $ckp, $tab_locs, $tab ) = @_; my $range = $tab_locs->{ $tab }; my @cs; seek $ckp, $$range[ 0 ], SEEK_SET; my $rec = readline $ckp; # nx die "Unexpected data while mapping $tab: $rec" unless '@nx@ ' eq substr $rec, 0, 5; my $pos = tell $ckp; # beginning of first client block $rec = readline $ckp; return if '@ex@ ' eq substr $rec, 0, 5; # empty table my $cc = client_from $rec; if( $rec =~ /\@pv@ \d+ \@db.have@ @@ @@ 0 0 / ) { # job066050 $rec = readline $ckp; $cc = client_from $rec; } die "Could not find client name in data: $rec" unless $cc; my ( $l, $h ) = ( $pos, $$range[ 1 ] ); my $st_cnt; until( $l == $$range[ 1 ] ) { my $npos; if( $l == $pos && $h == $$range[ 1 ] ) { my $qpos = tell $ckp; $rec = readline $ckp; $rec = readline $ckp if '@mx@ ' eq substr $rec, 0, 5; my $xbl = client_from $rec; $npos = $qpos and goto DONE if $xbl and $cc ne $xbl; } my $mid = int( ( $l + $h ) / 2 ); seek $ckp, $mid, 0; readline $ckp; # go to beginning of a line $rec = readline $ckp; # todo: don't include this in a pos. could be on the end, middle or beginning of a block. $rec = readline $ckp if '@mx@ ' eq substr $rec, 0, 5; die "Went past the end of its range $$range[0], $$range[1]: $rec" if '@nx@ ' eq substr $rec, 0, 5; if( '@ex@ ' eq substr $rec, 0, 5 ) { # find_tables trails with 'ex' $npos = tell $ckp; # todo: don't include the @ex@ for the last client } else { $npos = -1 if $cc ne client_from $rec; $npos = 0 if $cc eq client_from $rec; my $bpos = tell $ckp; $rec = readline $ckp; $rec = readline $ckp if '@mx@ ' eq substr $rec, 0, 5; my $mcc = client_from $rec; # could be nx $npos = $bpos if $npos == 0 and $mcc and $cc ne $mcc; } $h = $mid if $npos == -1; $l = $mid + 1 if $npos == 0; # todo: could record the eol pos to use die "Could not find data in $$range[0], $$range[1]: $l $h" if $l >= $h; next unless $npos > 0; DONE: push @cs, [ $cc, $pos, $npos ]; $cc = client_from $rec; ( $pos, $l, $h ) = ( $npos, $npos, $$range[ 1 ] ); status tell( $ckp ) - $$range[ 0 ], $$range[ 1 ] - $$range[ 0 ] if ++$st_cnt % 500 == 0; } seek $ckp, 0, SEEK_SET; @cs } sub map_damage { my ( $jeof, $rpos, $rline, @dmg ) = ( -s $jnl_name, 0, 1 ); seek $jnl, 0, SEEK_SET; status_start 'locating unparseable data'; my $op = $progress; PARSE: eval { local $SIG{ __WARN__ } = sub { $@ .= shift }; # Disable the progress function in parse since it resets to zero # every time it's called and we might call parse multiple times here. $progress = 0; parse $jnl, sub { $rpos = tell $jnl; $rline = $.; $progress = 1; status tell( $jnl ), -s $jnl if $. % 100_000 == 0; $progress = 0; 0 }, [ tell $jnl, $jeof ], 1 }; $progress = $op; status_end, return @dmg unless $@; my $line = ''; # todo: better line ID needed: ? /^\@[dpvrme][vlx]@ \d+/ pv dv rv mx ex dl vv $line = readline $jnl while !eof $jnl && $line !~ /^@[pdr]v@/; use bytes; # for length() push @dmg, [ $rpos, tell( $jnl ) - length $line ] if $rpos != $jeof; # Coalese corrupted records all in a row. $dmg[ -1 ] = splice @dmg, -1, 1 if @dmg > 1 and ${ $dmg[ -2 ] }[ 0 ] == ${ $dmg[ -1 ] }[ 0 ]; status_end, return @dmg if eof $jnl; # Don't skip parsing the line we just located after error. seek $jnl, tell( $jnl ) - length $line, SEEK_SET; goto PARSE } sub report_damage { say "$orig_progname --progress=0 \"$jnl_name\" --dump-range=$$_[0],$$_[1]" for map_damage; } sub invert_ranges { my ( $jnl_name, $src_range ) = @_; my ( $l, $h ) = ( 0, -s $jnl_name ); my @dmg = ( [ 0, 0 ], @$src_range, [ $h + 1, $h + 1 ] ); my @ranges = grep { $$_[ 1 ] > 0 && $$_[ 0 ] < $h } map { [ ${ $dmg[ $_ - 1 ] }[ 1 ], ${ $dmg[ $_ ] }[ 0 ] ] } 0 .. @dmg - 1; $ranges[ -1 ][ 1 ]-- if $ranges[ -1 ][ 1 ] > $h; [ grep { $$_[ 0 ] ne $$_[ 1 ] } @ranges ] } sub remove_damage { my $ranges = invert_ranges $jnl_name, [ map_damage ]; status_start 'writing repaired journal'; dump_range $jnl, $_, $output_fh for @$ranges; } sub par; sub split_table; sub parse_test { my ( $jnl, $range, $type ) = @_; seek $jnl, 0, SEEK_SET; if( $type eq 'jnl-perlin' ) { no utf8; binmode *STDOUT; binmode *STDIN; return ( map { print to_jnl_fmt $_ } @{ eval join '', } ), 0 } if( $type eq 'jnl-storablein' ) { binmode *STDIN; print to_jnl_fmt $_ while ! eof( *STDIN ) && ( $_ = fd_retrieve *STDIN ); return 0 } my $cb = sub { say join( "\n", map { "'$_'" } map { defined $_ ? $_ : 'UNDEEEF' } rec_hash_to_array shift ) . "\n" . '-' x 80; 0 }; $cb = sub { state $l++; say "$l: " . join ' ', rec_hash_to_array shift; 0 } if $d == 1; $cb = sub { state $l++; say "$l: " . join ' ', rec_hash_to_array shift if $l % 1000 == 0; 0 } if $d ==0; $cb = sub { 0 } if $quiet; $cb = sub { state $l++; status tell( $jnl ) - $$range[ 0 ], $$range[ 1 ] - $$range[ 0 ] if $l % 100_000 == 0; 0 } if $progress; $cb = sub { print to_jnl_fmt $_[ 0 ]; 0 } if $type eq 'jnl-roundtrip'; if( $type eq 'jnl-perlout' ) { # jnltool.pl --parse-test=jnl-perlout x.jnl > x.pl # perl -E 'use File::Slurp; use Data::Dumper; say Dumper eval read_file "x.pl"' print '['; $cb = sub { local $Data::Dumper::Terse = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Indent = 0; print Dumper( $_[ 0 ] ) . ','; 0 } } binmode( *STDOUT ), $cb = sub { store_fd $_[ 0 ], $output_fh; 0 } if $type eq 'jnl-storableout'; return status_run 'parallel parse test', sub { my $pp = sub { close $jnl; $jnl = open_ckp $jnl_name; parse $jnl, $cb, $_ }; my @procs = status_agg map { par $pp } split_table $jnl, $range, $parallel; usleep $status_child_wait while @procs = status_agg @procs } if $type eq 'parallel'; status_start 'parse test'; my $r = parse $jnl, $cb, $range, 1; status_end; print ']' if $type eq 'jnl-perlout'; $r // 0 # status_run 'parse test', sub { parse $jnl, $cb, $range, 1 } # my $r = 1; # $r &= status_run 'parse test', sub { parse $jnl, $cb, $range, 1 } until eof $jnl; # $r } # todo: is it ok for it to split beginning at @mx@? it currently can. # note: ranges are all at line boundaries sub split_table { my ( $ckp, $l, $u, $n ) = ( $_[ 0 ], @{ $_[ 1 ] }, $_[ 2 ] ); my $range = $u - $l; # todo: need to adjust range size based off of $n return [ $l, $u ] if ! $n || $n == 1 || $range < 100_000; my ( $c, $r ) = ( $range / $n, $range % $n ); my @rs; my $p = $l; for( 1 .. $n ) { push @rs, [ $p, $u ] and last if $p + $c >= $u; seek $ckp, $p + $c, SEEK_SET; readline $ckp; # start at beginning of line my ( $pos, $rec, $op ) = tell $ckp; # todo: swap readline and tell for boundaries? # Find the end of the record. # todo: can this jump the table boundary if the first readline above is # on the last record of the table? $pos = tell $ckp and $rec = readline $ckp until $rec and $op = get_op $rec; push @rs, [ $p, $pos ]; $p = $pos; } die "Went past the end of range: $l $u $n" if $u < tell $ckp; # Assign to $u to get the remainder $rs[ -1 ][ 1 ] = $u; @rs } sub split_jnl { my ( $jnl, $jnl_name, $n ) = @_; my @ns = split_table $jnl, [ 0, -s $jnl_name ], $n; return 0 if @ns == 1; # nothing to do status_run '', sub { my $cnt = 0; $status_steps = $n; for( @ns ) { $status_iter = $n - $cnt; $status_tag = "splitting journal ${\++$cnt}/$n"; dump_range $jnl, $_, IO::File->new( "$jnl_name-split_$$_[0]-$$_[1]", '>:raw' ) } }; 0 } sub par_default; sub batch_ranges { my @ranges = @_; my @work; push @work, [ splice @ranges, 0, $parallel ] while @ranges; @work } sub disk_par_estimate { # my $n = $parallel } sub ckp_header; sub par; # todo: suppress this on every line: #Perforce server info: # Server version 40 is replaying a version 38 journal/checkpoint. sub par_svr_replay { ERR 'Feature not available on Windows.' if $^O eq 'MSWin32'; my %args = @_; $args{ PAR_HELPER } //= ''; ( $args{ PAR_HELPER }, $args{ PAR_HELPER_ARGS } ) = $args{ PAR_HELPER } =~ /([^:]+):(.*)/ if $args{ PAR_HELPER }; my $p4root = $args{ P4ROOT }; ERR "Must have p4d in path." unless has_p4d; ERR "P4ROOT must be a writeable directory." unless -d $p4root && -W $p4root; $p4root = File::Spec->canonpath( File::Spec->rel2abs( $p4root ) ); my @edbs;# = glob "$p4root/db.*" =~ s,\\,/,gr; #"$p4root\\db.*"; # Not using glob since it misses symlinks on Windows. my $p4root_dh = DirHandle->new( $p4root ); push @edbs, $_ while defined( $_ = $p4root_dh->read ); @edbs = grep { $_ ne '.' && $_ ne '..' } @edbs; @edbs = map { File::Spec->catfile( $p4root, $_ ) } grep { /^db\./ } @edbs; # todo: warn if journal or other files exist? ERR "P4ROOT must not have existing database files." if grep { -e( _readlink( $_ ) // $_ ) } @edbs; # Just dangling symlinks in @edbs now. map { my $bn = basename $_; ERR "Table on disk '$bn' not in ckp!" unless $tab_locs{ $bn } } @edbs; map { my ( $ss, $st ) = map { basename $_ } $_, _readlink; # todo: handle case-insensitive filesystems. ERR "Symlink target name '$st' must match source db file '$ss'." if $ss ne $st } @edbs; # todo, check p4d's get_svr_version vs checkpoint my %dbdirs = map { $_ => $p4root } keys %tab_locs; @dbdirs{ map { basename $_ } @edbs } = map { dirname _readlink } @edbs; goto $args{ PAR_HELPER } if $args{ PAR_HELPER }; my %sizes_by_path; map { $sizes_by_path{ $dbdirs{ $_ } } += range_size $tab_locs{ $_ } } keys %dbdirs; map { ERR "Path '$_' does not have at least $sizes_by_path{$_} bytes free." unless diskspace_avail( $_ ) > $sizes_by_path{ $_ } / 1024 } keys %sizes_by_path; my @work = sort { range_size( $tab_locs{ $b } ) <=> range_size( $tab_locs{ $a } ) || $a cmp $b } keys %tab_locs; say "@work" if $debug; PAR_CKP_REPLAY: # Start off each file with @nx@ to preserve Unicode-ness and case-sensitivity. my $nx = ckp_header; my $case = sniff_ckp_case; my $par_fn = sub { my $tab = shift; $jnl = open_ckp $jnl_name; my $template = "${\ basename $orig_progname}-$$-${\ basename $tab}-XXXX"; my $tmp_root = File::Temp->newdir( $template, DIR => $dbdirs{ $tab }, CLEANUP => 1 ); # todo: hide "Server version 41 is replaying a version 39 journal/checkpoint." open my $fh, '|-', 'p4d', '-r', $tmp_root, '-C', $case, qw / -v server=3 -L log.txt -v dbopen.nofsync=1 -q -jr - /; print $fh $nx; dump_range $jnl, $tab_locs{ $tab }, $fh; # todo: error check: http://perldoc.perl.org/functions/close.html close $fh; # todo: any window between the close and move for p4d to still be working? move "$tmp_root/$tab", "$dbdirs{$tab}/$tab"; 0 }; return $par_fn->( $args{ PAR_HELPER_ARGS } ) if $args{ PAR_HELPER } eq 'PAR_CKP_REPLAY'; status_run "replaying ...", sub { my ( $total, $j, @procs ) = ( scalar keys %tab_locs, $parallel ); while( @work || @procs ) { @procs = reap sub {}, @procs, map { my $par_arg = $^O eq 'MSWin32' ? "PAR_CKP_REPLAY:$_ --par-ckp-replay=$p4root" : $par_fn; par $par_arg, $_ } splice @work, 0, $j - @procs; $status_tag = 'replaying ' . ( $total - @work ) . "/$total"; status $total - @work - $j, $total; usleep $status_child_wait; } }; # todo: correct return code? 0 } sub verify_ckp_chksum { my $chksum_name = $jnl_name =~ s/\.gz$//gr . '.md5'; ERR "Checksum file '$chksum_name' missing." unless -e $chksum_name; # MD5 (checkpoint.1) = A8BC468F8EEE4082DA62B5F31DCC6184 my ( $algo, $name, $chksum ) = slurp( $chksum_name ) =~ /^(\S+) \((.+)\) = (\S+)/; die "Unknown checksum algorithm '$algo'." if $algo ne 'MD5'; my $mk_gz_fh = sub { my $pp = $ENV{ P4JNLTOOL_PUREPERL }; return $name, IO::File->new( "pigz -cdk \"$jnl_name\"", '-|:raw' ) if !$pp && $has_pigz; return $name, IO::File->new( "gzip -cdk \"$jnl_name\"", '-|:raw' ) if !$pp && $has_gzip; new IO::Uncompress::Gunzip $jnl_name or die "gunzip failed: $GunzipError\n" }; my $fh = $jnl_name =~ /\.gz$/ ? $mk_gz_fh->() : $jnl; ERR 'Failed to open checkpoint.' unless $fh; my ( $m, $buf, $ret ) = Digest::MD5->new; $m->add( $buf ) while ( $ret = sysread $fh, $buf, $dump_buffsize ) && defined( $ret ) && $ret > 0; die "Error reading file: $!" unless defined $ret; # Would be nice to do this, but it errors-out as described in RT#125981. # Digest::MD5->new->addfile( $fh )->hexdigest ne lc $chksum $m->hexdigest ne lc $chksum ? ERR 'Checksum mismatch.' : 0 } sub scr_preamble_str { my $scr_preamble = << ' EOF'; #! /usr/bin/env perl # -*- coding: binary -*- use 5.014; use autodie; use Carp 'longmess'; use Data::Dumper; use diagnostics '-traceonly'; use File::Spec; use FindBin qw / $RealBin $RealScript /; use IO::File; use Time::Piece; use sigtrap qw / handler sig_log normal-signals error-signals /; use strict; use warnings; use if $^O eq 'MSWin32', 'Win32'; BEGIN { $Carp::MaxArgLen = 1000; $Carp::MaxEvalLen = 1000; $Carp::Verbose = 1; } my $lf = File::Spec->catfile( $RealBin, "$RealScript-log.txt" ); my $lfh = IO::File->new( $lf, '>>' ) or die "Unable to open '$lf': $!"; $lfh->autoflush( 1 ); my $rec_no = 0; my $start = localtime; my $sf = File::Spec->catfile( $RealBin, "$RealScript-completed" ); my $user = $^O ne 'MSWin32' ? getpwuid $< : Win32::LoginName(); say $lfh "\nstarting: $start, user: $user}\n"; sub sig_log { no sigtrap; my $msg = "\nExiting unexpectedly, record number: $rec_no\n\n@_\n\n${\ longmess }"; say STDERR $msg; say $lfh $msg; exit 1 } $SIG{ __DIE__ } = $SIG{ __WARN__ } = \ &sig_log; sub uDumper { local $Data::Dumper::Useqq = 1; local $Data::Dumper::Terse = 1; Dumper @_ } EOF ; $scr_preamble =~ s/^ //gmr; } sub mk_uniconv_db; sub dbscan; =comment The depot cleaner is a tool to remove any files from the server's depot storage that aren't referenced in its metadata. It supports symlinks in the storage hierachy, which are sometimes used by administrators to help distribute a single depot across multiple disks. todo: - dedupe from uniconv() since this function is almost entirely copypasta. - Also look for client.readonly.dir, etc? - Symlinks in the depot pointing elsewhere. Other stuff like junction points too? - doc that this should be done offline. - make the script and processing parallel? - does not find orphaned revs within rcs files - support graph depots - come up with a better name for the auto-generated --target for the script. - should the result script put more in its log file and not just print out? - handle entirely-deleted depots - have separate target directories for moves that don't cross filesystems =cut sub depot_cleaner { my $droot = parse_ckp_nx( $jnl )->{ p4root }; my $ckp_case = sniff_ckp_case; my $needs_case_copy = $ckp_case != 0; ERR "Checkpoint nx record p4root must be an absolute path.\n" . "Re-take the checkpoint like this: p4d -r /full/path/to/p4root -jc" if ! File::Spec->file_name_is_absolute( $droot ); my %cfgs; parse $jnl, sub { $cfgs{ "$_[ 0 ]->{ CFsname }\0$_[ 0 ]->{ CFname }" } = $_[ 0 ]->{ CFvalue }; 0 }, $tab_locs{ 'db.config' }; my $sd_root = $cfgs{ "any\0server.depot.root" }; ERR "server.depot.root is not an absolute path! '$sd_root'" if $sd_root && ! File::Spec->file_name_is_absolute( $sd_root ); my $sd_sep = ( $sd_root && ( $sd_root =~ /\\/ ) ) ? '\\' : '/'; $sd_root =~ s/\Q$sd_sep\E$// if $sd_root; my $scr_name = "$jnl_name-depot_cleaner.pl"; ERR "Target script '$scr_name' already exists!" if -e $scr_name; my $arch_mv = IO::File->new( $scr_name, '>' ) or die "No '$scr_name': $!"; my $archmv_db_cmp = sub { use bytes; my ( $l0, $l1 ) = map length, @_; ( $l0 == $l1 ) ? ( $_[ 0 ] cmp $_[ 1 ] ) : ( $l0 < $l1 ) ? -1 : 1 }; my $armv_fsk = sub { use bytes; return unless $_; $_ = $needs_case_copy ? lc $_ : $_ }; my $armv_fsv = sub { use bytes; if( $needs_case_copy ) { my $h = $_; if( ref $h eq 'HASH' ) { map { $h->{ $_ } = lc $h->{ $_ } } keys %$_; } else { $h = lc $h } $_ = $h } $_ = Storable::freeze( \ $_ ) }; # Change the sort order to sort lengthwise shorted to longest so we start # at the top directory and move our way down, minimizing the amount of # filesystem work we do. $DB_BTREE->{ compare } = $archmv_db_cmp; my $arch_mv_db = File::Temp->new; my ( $armv_dbh, $armv_db ) = mk_uniconv_db $arch_mv_db; delete $DB_BTREE->{ compare }; $armv_dbh ->filter_store_key( $armv_fsk ); $armv_dbh ->filter_store_value( $armv_fsv ); my $arch_mv_scr = scr_preamble_str . << ' EOF'; use File::Basename; use File::Copy 'mv'; use File::Path 'make_path'; use DirHandle; use Getopt::Long; my ( $target, $err, $move, $help, $moved ); GetOptions '--move' => \ $move, '--target=s' => \ $target, '--help' => \ $help, or exit 1; my $usage = << 'EOH'; USAGE: --help --move Perform the actual archive scan and move. When not specified, preview mode is used. --target Optional directory to move orphaned files. EOH ; exit say $usage if $help or @ARGV; exit say 'Already completed, may not run again.' if -e( $sf ) && $move; say 'Preview mode. Run with --move to perform the work.' if !$move; $target = "$0-dfiles" if ! $target; say "Target: '$target'."; die "Error: '$target' is not a directory.\n" if -e( $target ) && ! -d( $target ) ; make_path $target, { error => \ $err } unless -d $target; exit say( $lfh Dumper $err ), say Dumper $err if $err && @$err; while( ! eof DATA && eval '$_ = '. ) { my ( $src, $fs, %disk ) = @$_; my $osrc = $src; $src = File::Spec->rel2abs( readlink $src ) if -l $src; die "Error! '$src' does not exist." unless -e $src; my $dh = DirHandle->new( $src ); $disk{ "$src/$_" } = 1 while defined( $_ = $dh->read ); my %rems = map { $_ => 1 } grep { my $bn = basename $_; $bn ne '.' && $bn ne '..' } keys % disk; delete @rems{ $osrc, $src, map { "$src/$_" } keys %$fs }; map { delete $rems{ readlink $_ } } grep { -l $_ } map { "$osrc/$_" } keys %$fs; say join "\n", sort keys %rems if !$move && keys %rems; next if ! $move or ! keys %rems; my ( $dst_path, $dst ) = ( File::Spec->catdir( $target, $src ) ); make_path $dst_path, { error => \ my $err } unless -d $dst_path; die "make_path error on '$dst_path': " . Dumper $err if $err && @$err; $moved++, ( $dst = -d( $_ ) ? "$target/$_" : $dst_path ), mv $_, $dst or die "($rec_no) Couldn't rename src '$_' to dst '$dst':" . " \$! '$!', \$^E '$^E'" for sort keys %rems; } continue { $rec_no++ } exit say "Nothing to do." if $move && !$moved; say $lfh "\nCompleted in ${\ ( scalar( localtime ) - $start )->pretty }"; IO::File->new( $sf, '>' ) if $move; 0 __DATA__ EOF ; $arch_mv_scr =~ s/^ //gm; print $arch_mv $arch_mv_scr; my ( %depotTypes, %depotMaps ); parse $jnl, sub { $depotTypes{ $_[ 0 ]->{ DPname } } = ${ { 0 => 'local', 1 => 'remote', 2 => 'spec', 3 => 'stream', 4 => 'archive', 5 => 'unload', 6 => 'tangent', 7 => 'graph' , 8 => 'extension' } }{ $_[ 0 ]->{ DPtype } }; $depotMaps{ $_[ 0 ]->{ DPname } } = $_[ 0 ]->{ DPmap } =~ s,\Q/...,,gr; $depotMaps{ bytes_lc $_[ 0 ]->{ DPname } } = $depotMaps{ $_[ 0 ]->{ DPname } } if $needs_case_copy; 0 }, $tab_locs{ 'db.depot' }; my $dr_abspath = sub { my $dmap = $depotMaps{ $_[ 0 ] }; File::Spec->file_name_is_absolute( $dmap ) ? $dmap : File::Spec->catdir( $sd_root // $droot, $dmap ); }; my $arch_file = sub { my ( $dfile, $type, $rev ) = @_; my ( $rcs, $bin, $tiny, $comp, $uncomp, $script ) = map { ( $type & 0x0000F ) == $_ } 0x00000, 0x00001, 0x00002, 0x00003, 0x00007, 0x00008; die "bad arch_file usage $dfile, $type" if $tiny || $script; my ( $depot ) = $dfile =~ m,^//([^/]+),,; $dfile =~ s,^//[^/]+/,,; my $ext = $rcs ? ',v' : ( $comp || $uncomp || $bin ) ? ",d/$rev" : ''; $ext .= '.gz' if !$rcs && $comp; File::Spec->catfile( $dr_abspath->( $depot ), "$dfile$ext" ) =~ s,\\,/,gr; }; my $files = 0; my $fn = sub { my $rec = shift; my $type = $rec->{ REatype }; my $afile = $rec->{ REafile }; my $rev = $rec->{ REarev }; my ( $rcs, $bin, $tiny, $comp, $uncomp, $script ) = map { ( $type & 0x0000F ) == $_ } 0x00000, 0x00001, 0x00002, 0x00003, 0x00007, 0x00008; return 0 if $tiny or $script; $files++; my @dirs = split m,(?( $afile, $type, $rev ); my ( $depot ) = $afile =~ m,^//([^/]+),,; my $dr_abs = $dr_abspath->( $depot ); for( my $i = 0; $i < @dirs; $i++ ) { my $path = join '/', ! $i ? "$dirs[ 0 ]/" : @dirs[ 0 .. $i ]; my $t = ( $i == @dirs - 1 ) ? ( $type // -1 ) : -1; my $npath = ( $i > 0 && $i < ( @dirs - 1 ) ) ? $dirs[ $i + 1 ] : ''; my $key = $t == -1 ? $path : dirname $path; next if length( $key ) < length( $dr_abs ); # Don't go past the depot root my $files = $armv_db->{ $key }; $files->{ basename $path } = 1 if $t != -1; $files->{ $npath } = 1 if $npath; $armv_db->{ $key } = $files; } 0 }; parse $jnl, $fn, $tab_locs{ 'db.rev' }; parse $jnl, $fn, $tab_locs{ 'db.revsh' }; my $amvsub = sub { my $fh = $_[ 0 ]; sub { local $Data::Dumper::Terse = 1; local $Data::Dumper::Indent = 0; local $Data::Dumper::Sortkeys = 1; say $fh Dumper [ $_[ 1 ], $_[ 0 ] ]; 0 } }; dbscan $armv_dbh, undef, $amvsub->( $arch_mv ); close $arch_mv; unlink( $scr_name ), ERR "No depot files?\n" unless $files; say "Result script; '$scr_name'."; } ################################################################################ sub par_fork { my $tfh = File::Temp->new( TEMPLATE => 'jnltool-tmp-XXXXX', DIR => File::Spec->tmpdir, SUFFIX => '.txt' ); my ( $fn, $pid ) = ( shift, fork ); goto CHILD unless $pid; my $pinfo = { pfh => $tfh, kill => sub { kill 'INT', $pid }, alive => sub { 0 == waitpid $pid, WNOHANG }, PID => $pid, ret => sub { $? >> 8, $? & 127, $? & 128 } }; push @pids, $pinfo; return $pinfo; CHILD: $prog_fh = IO::File->new( $tfh->filename, '>' ) or die "$$ par_fork no tfh '${\ $tfh->filename}' $!"; close STDIN; $SIG{ INT } = undef; # eval { local $SIG{ __DIE__ } = sub{ $@ = @_ }; $fn->( @_ ); }; # die "fork proc err $$ '$@'" if $@; # exit 0; exit $fn->( @_ ); } sub par_helper { my $par_helper_str = shift; my $tfh = File::Temp->new( TEMPLATE => 'jnltool-tmp-XXXXX', DIR => File::Spec->tmpdir, SUFFIX => '.txt' ); $ENV{ P4JNLTOOL_PAR_TMPFILE } = $tfh->filename; my @cmd = ( 'perl', $orig_progname, "--progress=$progress", "--parallel-helper=$par_helper_str", $jnl_name ); Win32::Process::Create( my $po, $Config{ perlpath }, join( ' ', map { quote_native $_ } @cmd ), 0, NORMAL_PRIORITY_CLASS(), '.' ) or die "Couldn't create par helper: " . Win32::FormatMessage( Win32::GetLastError() ); my $pid = $po->GetProcessID; my $pinfo = { pfh => $tfh, kill => sub { $po->Kill( 1 ) }, alive => sub { my $r; $po->GetExitCode( $r ); $r == STILL_ACTIVE() }, PID => $po->GetProcessID, ret => sub { my $r; $po->GetExitCode( $r ); $r, 0, 0 } }; push @pids, $pinfo; $pinfo } # Below average. sub par { ref( $_[ 0 ] ) eq 'CODE' ? par_fork @_ : par_helper @_ } sub par_default { state $n = $^O eq 'linux' ? ( scalar grep { /^processor/ } `cat /proc/cpuinfo` ) : $^O eq 'MSWin32' ? $ENV{ NUMBER_OF_PROCESSORS } : $^O eq 'darwin' ? `sysctl -n hw.logicalcpu` =~ s/\R+//gr # PowerShell: Get-WmiObject Win32_Processor NumberOfCores : 1 } ################################################################################ =comment todo: uniconv-ckp_case.t can infiniloop processing C0/checkpoint.1 with this strace. maybe just on Perl syntax error? --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xc} --- rt_sigaction(SIGSEGV, NULL, {0x48efa0, [], SA_RESTORER, 0x7f274c0f04a0}, 8) = 0 rt_sigreturn({mask=[]}) = 48671040 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xc} --- rt_sigaction(SIGSEGV, NULL, {0x48efa0, [], SA_RESTORER, 0x7f274c0f04a0}, 8) = 0 rt_sigreturn({mask=[]}) = 48671040 =cut sub cleanup { my $err = shift; return unless $needs_cleanup and $parent and $parent == $$; $needs_cleanup = 0; print $prog_fh $term{ norm } if $progress && $prog_fh; $SIG{ __DIE__ } = $SIG{ __WARN__ } = undef; my $msg = "$$: Subprocesses still alive in cleanup! PIDs: " . join ' ', map { $_->{ PID } } @pids; my $pids_alive = grep { $_ } map { $_->{ alive }->() } @pids; $err = 1, warn( $msg ), $global_log->( $msg, longmess ) if $pids_alive; map { $_->{ kill }->() } @pids if $pids_alive; $global_log->( "exiting with return code: $err" ); # todo: don't exit from the END block? exit $err } sub cleanup_err { no sigtrap; return unless $needs_cleanup and $parent and $parent == $$; # Add a couple leading newlines to avoid the progress meter output. my $msg = "\n\n\n@_" . longmess; # my $msg = "\n\n\nPID $$\n@_" . longmess; $global_log->( $msg ), say STDERR $msg unless $_[ 0 ] eq 'PIPE'; cleanup 1 } END { no sigtrap; cleanup $? } # todo: enable this so the global log handler works. #$SIG{ __DIE__ } = \ &cleanup_err; $SIG{ __WARN__ } = sub { local $SIG{ __WARN__ }; $global_log->( @_, longmess ); die @_, longmess }; ################################################################################ sub str_to_charnames { # todo: ord works on chars, not bytes # use charnames ":full"; # binmode STDOUT, ":utf8"; # say charnames::viacode( ord "\N{U+1F4A9}" )' # PILE OF POO =comment position: 2412 of 2431 (99%), column: 0 character: ಠ (displayed as ಠ) (codepoint 3232, #o6240, #xca0) preferred charset: unicode (Unicode (ISO10646)) code point in charset: 0x0CA0 script: kannada syntax: w which means: word category: .:Base, L:Left-to-right (strong) to input: type "C-x 8 RET HEX-CODEPOINT" or "C-x 8 RET NAME" buffer code: #xE0 #xB2 #xA0 file code: #xE0 #xB2 #xA0 (encoded by coding system utf-8-unix) display: terminal code #xE0 #xB2 #xA0 Character code properties: customize what to show name: KANNADA LETTER TTHA general-category: Lo (Letter, Other) decomposition: (3232) ('ಠ') [back] =cut # todo: mode to highlight the nonprintables or combining chars - separate out each char into a box # display; char, byte sequence, \N{NAME}, \x{hex}, U+NNNN, other records it was used in, ckp file offset, line no, col no, surrounding combining chars. alt-x describe-char, result after normalization } # todo: what is the 'null' encoding? filter it out? my %encs = map { $_ => 1 } Encode->encodings( ':all' ); sub fmt_encs { join "\n", sort { my $re = qr/^(cp|iso-\d+-)(\d+)/; my ( $apfx, $acp ) = $a =~ $re; my ( $bpfx, $bcp ) = $b =~ $re; $acp = 0 unless $apfx && $bpfx && $apfx eq $bpfx; ( $acp && $bcp ) ? $acp <=> $bcp : lc( $a ) cmp lc( $b ) } keys %encs } sub enc_list { split /\n/, fmt_encs } sub http_svr { my %cfg = @_; $cfg{ TRIES } = $cfg{ PORT } ? -1 : -80; --( $cfg{ PORT } //= 8000 ); $cfg{ HOST } //= 'localhost'; $cfg{ CB_RDY } //= sub {}; $cfg{ CB_LOG } //= sub { shift; say @_ }; LISTEN: my $s = IO::Socket::INET->new( Proto => 'tcp', Listen => SOMAXCONN, LocalAddr => $cfg{ HOST }, LocalPort => ++$cfg{ PORT }, Reuse => 1 ); ! $s ? ! ++$cfg{ TRIES } ? die "Error starting HTTP server on $cfg{HOST}:$cfg{PORT}" : goto LISTEN : 0; $cfg{ CB_RDY }->( "$cfg{HOST}:$cfg{PORT}" ); local $\ = $/ = Socket::CRLF; CONN: { my ( %req, %post ) = (); my ( $c, $paddr ) = $s->accept; dbg_log $cfg{ CB_LOG }, { peerAddress => [ Socket::getnameinfo $paddr, Socket::NI_NUMERICHOST ]->[ 1 ] }; dbg_log $cfg{ CB_LOG }, "HTTP_SVR bad connection; undef, $!" and goto CONN if ! $c; dbg_log( $cfg{ CB_LOG }, "HTTP_SVR bad connection; eof, $!" ), $c->shutdown( 2 ), $c->close, goto CONN if eof $c; @req{ qw / METHOD URL VERSION / } = <$c> =~ m,(\w+) ([^\s]+) HTTP/(.*)$/,; $req{ lc $1 } = $2 while <$c> =~ m,([^:]+): (.*)$/,; { use bytes; read $c, $req{ BODY }, $req{ 'content-length' } if $req{ METHOD } eq 'POST'; } die "Bad POST type: $req{ 'content-type' }" if $req{ METHOD } eq 'POST' and $req{ 'content-type' } !~ m,\Qapplication/x-www-form-urlencoded,; @req{ qw / URL BODY / } = $req{ URL } =~ m,([^?]+)\??(.*), if $req{ METHOD } eq 'GET'; # Default encoding is ISO-8859-1. uniconv_gui()'s HTML is UTF-8 so assume that. $req{ BODY } = decode 'UTF-8', $req{ BODY }; my @pvs = $req{ BODY } =~ /([^=]+)=([^&]+)&?/g; $post{ $pvs[ $_ ] } = uri_unescape $pvs[ $_ + 1 ] for grep { $_ % 2 == 0 } 0 .. @pvs - 1; # Poor man's List::Util::pairmap. dbg_log $cfg{ CB_LOG }, { 'request', \ %req, 'post', \ %post }; my %header = ( status => 'HTTP/1.0 200 OK', 'Cache-Control' => 'no-cache', 'Content-Type' => 'text/html; charset=UTF-8', Connection => 'close', ); my ( $cbo_cnt, $cbo_rsp ) = $cfg{ CB_RSP }->( \ %req, \ %post, \ %header ); die 'No http_svr callback response?' unless defined $cbo_rsp; my $gz; gzip( \ $cbo_rsp => \ $gz ), $header{ 'Content-Encoding' } = 'gzip' if ( $req{ 'accept-encoding' } // '' ) =~ /gzip/ && bytes_length $cbo_rsp > 2 ** 14; dbg_log $cfg{ CB_LOG }, { response_header => \ %header, data_size => bytes_length( $gz // $cbo_rsp ) }; print $c join "\n", delete $header{ status }, 'Content-Type: ' . delete $header{ 'Content-Type' }, map { "$_: $header{ $_ }" } keys %header; print $c "\n", ( $gz ? $gz : $cbo_rsp ); $c->shutdown( 2 ); $c->close; goto CONN if $cbo_cnt; return 0; } ERROR: 1 } ################################################################################ =comment Data format for records.db. { # Parsed record. 'rec' => { 'version' => '3', 'HAtime' => '1443849534', 'table' => 'db.have', 'op' => 'pv', 'HAtype' => '3', 'HArev' => '1', 'HAdfile' => '//latin1_spec/user/shiftjis.p4s', 'HAcfile' => '//ws4/latin1_spec/user/shiftjis.p4s' }, # byte offset range for full record 'range' => [ 51930, 52045 ], # if it's just a single charset, set it singular? todo: should be a nested list? 'charset' => [ 0, 100, cp949 ]; # byte offsets relative to each column. Cols ranges must be complete. 'charsets' => { all => 'cp949', HAcfile => [ [ 0, 100, 'UTF-8' ] ], HAdfile => [ [ 0, 200, 'shiftjis' ], [ 201, 300, 'eucjp' ] ] }, # Source of the record. Defaults to coming from a checkpoint, but some sources # like unloaded files can have records that duplicate records in the checkpoint, # so we use the source as an additional field in the key to prevent collisions. SRC => 'UNLOAD', }, =cut sub uni_db_fsk { return $_ if ref $_ ne 'HASH'; my $src = exists( $_->{ rec } ) && exists( $_->{ SRC } ) ? $_->{ SRC } : undef; my $rec = exists( $_->{ rec } ) ? $_->{ rec } : $_; $_ = join "\0", ( map { $_, $rec->{ $_ } } 'table', exists( $rec->{ version } ) # todo: check that all the necessary keys are supplied? can't do out of order keys. ? ( 'version', grep { exists $rec->{ $_ } } keyns $schema->{ $rec->{ table } }{ $rec->{ version } } ) : () ), ( $src ? "SRC\0$src" : () ) } sub uni_db_key { local $_ = $_[ 0 ]; uni_db_fsk } sub uni_db_sort {} # needed to binary search finding recs? sub init_uni_db { my ( $name, $dbh ) = @_; $dbh->filter_store_key( \ &uni_db_fsk ); # $dbh->filter_fetch_key( sub { ( 0 <= index "\0", $_ ) ? { split "\0", $_ } : $_ } ); # todo? local $Storable::drop_utf8 = 1; $dbh->filter_store_value( sub { die 'Stored empty hash! ' . longmess if ref( $_ ) eq 'HASH' && ! %{ $_ }; $_ = Storable::freeze( \ $_ ) } ); $dbh->filter_fetch_value( sub { $_ = ${ Storable::thaw( $_ ) } } ); } # xxx do not dereference the tied hash, it tries to store an empty hash! # $cs = $recs->{ { table => 'db.revsx', op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ 'db.revsx' }, REdfile => $rec->{ rec }->{ HAdfile } } }->{ charsets }->{ REdfile }; sub mk_uniconv_db { my $name = shift; my $dbh = tie my %db, 'DB_File', $name, O_RDWR|O_CREAT, 0666, $DB_BTREE or die "Cannot open file '$name': $!\n"; init_uni_db $name, $dbh; $dbh, \ %db; }; sub dbscan { my ( $dbh, $flt, $cb, $dir, $k, $v ) = @_; $dir //= R_NEXT; do { local $_ = $flt; $k = uni_db_fsk } if $flt; # todo: use $dbh->filter_store_key->()? my $r = $dbh->seq( $k, $v, $flt ? R_CURSOR : R_FIRST ) != 0; $r = $dbh->seq( $k, $v, $dir ) while $r == 0 && !$cb->( $v, $k ); } sub pager { my ( $dbh, $tab, $n, $l ) = @_; $l //= { table => $tab }; my ( $rs, $ret ) = ( [ {} ] ); my $cb = sub { $_[ 0 ]->{ rec }->{ table } ne $tab || @$rs == $n + 1 || ! push @$rs, $_[ 0 ] }; sub { $n = $_[ 0 ] if @_; dbscan $dbh, $l, $cb; splice @$rs, 0, 1; return undef if ! @$rs; $l = dclone $$rs[ -1 ]; ( $ret, $rs ) = ( $rs, [] ); $ret } } # todo: have one that just stores the raw k/v bytes. sub export_uniconv_db { my ( $dbf ) = @_; my ( $dbh, $db ) = mk_uniconv_db $dbf; my $ckp = "$dbf.ckp"; die "exported checkpoint already exists: $ckp" if -e $ckp; my $o = IO::File->new( $ckp, '>' ) or die "no '$ckp': $! $?"; dbscan $dbh, undef, sub { say $o uDumper [ reverse @_ ]; 0 } } ################################################################################ sub favicon_base64 { # convert H_Server_Hex(pv1).ico[5] image.png # perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)' < image.png << '!blob'; iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC AK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAkBQTFRF ////SEhIAAAABgYGwsLCGx4hAwMFQEA/AAECwcDASUlLv7+/R0ZGFRgbAQICAQIDQkhPrbzPqbjL ipWlZW14naq7oK/CfoqZOT5FanJ+orDDmqq/fYqbKzA2eHuAgY6drbzPi5uuT1llx8jIxM/ch5it 5OjsvMbS4eXpucPQ4OTotsDN3+Pns77L3uLnsLvI3eHmrbjG3ODlrbjH3eHmmaOvg5SpXG2A09bZ KS82fYyggJGmcIKXSVVjDhAVTFZic4SXb4GWV2V2Jiw1EhUYS1djcIGVa3uPYnKFJiw0prTGoK/B qbnNobDCn62/nKu9mqm7mqq9sr7OusXSqLXGmai7mae6laS3rLjGsbzInay+pLTIm6m91Nri//// xM3XkqK1laO2j56y6u3xo6+9h5eqmqm8l6e60NbfwMnUjp6xkaCyi5qu5entn6u7gZGlfY6ilqW4 k6O2ztXevMXRhperiJmtg5So5OfsnKi3fY2heYqekqG1j5+yzNPc8PL15Ojs5ejt+fr7/v7+maW1 eYmedYWai5uuytHalqKydYWZcYGWipqtyNDYxczVl6S0maa2laGx6Orukp+vcYKVbX6ShpWpg5On xs7XsrzHdIabeImdcoOX4OToj5ysbX2SaXqPgpGlf4+jyM/Xtb7JdYabeYmdc4SX5ujsjZuqaXmO ZXiNfo6is7zH4eTppK+8dISYcICVxszU3eHmgpChan2SdoebdISZdISXb4CTanqOa3yRd4idcICU bn6SbH2REFVzOAAAAEp0Uk5TAAAAAAAAAAAAAAAAAAAAABOm6k9Syu6PGzOz53MKFo71zksCpP4x uz+3Pbc9tz23Pbc9vj9e7LoWI5/93WIDRMXwgxQGZdr2oCfZ94muAAAAAWJLR0QAiAUdSAAAAAlw SFlzAAAASAAAAEgARslrPgAAAQtJREFUGNNjYGBkAgIBQSFhZhCDhQEkwCoi6uUtJi7BBhZg55CU 8vH18w8IlJaRZeJkYJCTVwgKDgkNC4+IjFJUUmZgUImOiY2LT0hMSo5LSVVVY2BQT0vPiMvMys7J jcvLL9BgYNAsLCqOKyktK6+Iq6yq1mJg0K6prYurb2isaGpuaW3TYWDQzWrviAOD5s6ubj0GBv2e 1N64vv4JEyfFTZ4y1YCBwXDa9BlxM2fNnjM3bt78BUYMDMYLFy2OW7J02fIVcStXrTZhYDA1W7N2 3foNXRs3bd6y1dyCgYHL0sp62/YdU3bu2m1ja8fNwMDDxGvvsGfvvv2OTs58UN8yubi6uXt48oO8 DwDwBlFbyxBIvAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNS0wOS0yOFQxNTozNDowMS0wNzowMLVM flQAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTUtMDktMjhUMTU6MzQ6MDEtMDc6MDDEEcboAAAAAElF TkSuQmCC !blob } sub helix_icon_base64 { # https://swarm.perforce.com/changes/1211328 # convert favicon.ico image.png # perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)' < image-2.png << '!blob'; iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC AK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAlhQTFRF AAAAEarIFFuhFFqhMmCMEarHEKrIEarHEarHEarIEarIEarIEanHFFuhFFqhFFuhFFuhEarHEarI FFqgE1qhFFuhEarIEarIEarIEKrHE1ugFFuhFFugFFuhEarIEKrIEanIEarHFFuhFFuhFFuhE1uh EaTFEarIEarHEanHEarIFFuhE22qEanHEafGEarIEanHEanIE2GkFFuhEarIEarIEarIEn6yE1uh E1uhEarIEarIEarIFFugFFuhFFuhFFuhEarIEarHEarIFFugFFuhFFuhEarIEarIFFuhFFuhFFqh EKrIEanIEarHW2dxXGdwXGdwXGdwGVudFFuhFFuhEarIEarIXGZwW2ZvXGdwXGdwWmZwFFuhFFug FFuhEarIEarIW2ZvXGdwXGdwFFqhFFuhEarIEarIXGdwXGdwXGdvFFuhFFuhEarIXGZvXGdvXGdw XGZwFFuhFFuhEarIEarHXGdwXGdwFFuhFFuhEarHEarIEanIXGdwXGdwXGdvXGdvFFuhE1uhEarI EarIWmhxXGdwXGdwXGdwJl6UFFuhFFuhEarIFFuhFFuhFFuhFFugEarIEarIE1qgFFuhEanIEarI FFugFFuhEarHEabGE2OlFFuhEarIEarIFFuhE2enFFuhE1yhEZG7FFugFFuhE2+qEanHEarIEarI FFuhFFqhEnmvEarIEarIFFuhFFuhFFuhFFuhEarIEarIFFuhE1qgFFugEarHEarIEarIFFuhFFuh FFuhEarIFFuhEZC7E1uhEZi/E2amE1yhFFqh////MCdOrAAAAL90Uk5TAAAAAAAAiuSaXvr8PgIO DghmmJroOgiOvApq9vZKJsi2DD7k+IZm8PyUBhpMpPJaQOD4dgJy+OCuEJDaOBCm0ByMsBJu1BiS BEzAAhLsmDhuanBg/G50zGRcVoIYjOAGvlACFA7iPN4WMmwIlmYYUlB+IIhqwloEGrRQetwOMHJy AjIW8qhQfHiSFtKiahC+7hycCiTOpMrqXoDuqPBM6iaa+oweGt5YquxOLqYc0nYw6PJg1qAW4jQW QhqkJDa1hu7XAAAAAWJLR0THjQVKWwAAAAlwSFlzAAAASAAAAEgARslrPgAAAddJREFUOMt9kvlf TGEUxt+ZqExqsjSiGqqLkCyNFKnQjprI2iYlZQkRCm32ytJmX5KlUMgSKZ0n29/l3Mkvd957PT+d z/0+7/ue55wrhJ48vby8xX80xUI+U90/+vpZrVZ/tTJNsxBNl075zwAwM0AI2ywfosDZ8r2+c4KA 4BD7XCKaF6r3sjksXJm/YCFRxKLFBs0tiVwaQRS1zGTU/fIVfP34ymgPfexYxd3HrP4BxMbp8jV8 fG38ugROk5gkc9N65hs2cppkBUhJlafH6dPSXWVGMJC5yY1v3kKUle3MUbV1Wy6w3c2wg2inbdfu Pary8guAwiKtYS9RsdhX4lKpsO4HyrQRDlBMecXBnAk5Dx0GjkiGSufRYxM6XnUCOKkNWU106vSZ fyqZVAPUans4S3TufF2964WGxiYFQRe0hvKLRJcuX7mq6lpzC9Bqdst5nQeZ5nCVN24Ct9qkVbSz o72Dq84uQMmQl2G7zY47dnPtXeb39NaZfp/X8eAhj/lRiMEP8/jJz1+87K5uAy6Knv4Gep49N+Iv Mv8AL3v7Xr2erMvfFPL1/QNviSzvHDr8/SDw4aMQ9k+c5rNN4kN8/stXtRr+xhqRDKNA+HdhrO5c KGN64C+VaLq3JA7XKwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNi0wMy0yNFQyMzowODowMS0wNzow MAlZZyEAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTYtMDMtMjRUMjM6MDg6MDEtMDc6MDB4BN+dAAAA AElFTkSuQmCC !blob } sub smudge_base64 { << '!blob'; iVBORw0KGgoAAAANSUhEUgAAADQAAAA5CAYAAAB55gg1AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABUtJREFUaN7tWtuO3EQQdVfbbY9n dnbCbCCQsFkJAQmEq5AQAvHCT/D/P7J0R6ejMzXV9ngu3g3alUr2eH2p01V16mJX9/f31f9JqidA T4CMh1aVRHEfPaAEAvLxAgKAJkobpc5gHjUgKOktJfG/EGUJYOk8eRQxVFA4u1ALhXfciUGQy7kp z7goIEPhrKiHJTwpXpGbVWNAtMwFyKnV9pCkeKcAjSpp/f8UUKcEuidX8irwnaXw0CKxu10UUGH1 hACx1RqIM9xUpsTMRVxuyHUM18u/awU+Wy+QRUVffy5iOIrVSpm/AHIR5VmUFWKsxbFibD0IIMMK boA4EoAbbFuAu2KXnQXQAG1LySoFVxVY59Moa+y/xLZIEnMB2kmUY6xE1yR5FeUzAFlD5BzsdjRt G1WATrSVkXh7lD63Ub7GvsNxb7Di/LUcHhwQE0LHahVTmc5T3PwQ5VvslxbkpGrcjJExclCxVKuY Cgh+ITCZ6X6PckcU7g0LZZo/PbEqRXXCFJVXhEqevG1UBZEUv4YkQvgjyrsoX0TZEssJuev7RTqL y9EK6wrAE5gV3MxTwAcA0tcGKN6CEF6DCL6M8gL3Wp6rMLUA7ZhbUbKQFdiSXAHUdF1NeSftb0AC OXaSxT6HK3JeOykfHcJkzgAkBf8XBahT1Xjer7AQKdl+oiqHWYpTnUy9ZjMFKjd8PfVJgVwzK9zB DW9wfr73+4W4CG2rOLKIwRVai47irQOYnuIv3/MV5IpicnEsMRxKzzp2/EBTJkQUmcI3VJjqJnAD ktjg2vWUDncM0BKrGkipHkrwxEaMGPMKSCaDFQigV3HkiQlTXfcc+7cMaGocaUCBwHSQZXYBowrQ FXcP1wnKzTaqoqgJUIU89RoE8RusdBCgPa8qtQYU2GsothNLhnW4SlhDuRYL8gLH2IU9WcIjX6Xz fo3yExZmspXGquoARVYqkV4RKdRqxT0B6rG/VSzmC21Ics2vovwV5Uc8Y1KbPgSGXWZFK7zAQxcG AfTUwH1wM9UiDPVO+V6JJH7BQkihZxKTZdWJtSKDHor3WL08TMzNGZPBgjL/h/tw9ldWEV1dU0zl vxu4YaPcOxTb98JIt1Hx0GF/S2PdTjGWhysueLVHRl96hqeriQbPvDZcdo9tS8UpV88BHeZLotdb KlJDYZyVr5fCBElojqfLotpgwcyeegDjBy2kqPuamCeV/d9H+TPK37jRkgpO3TK0Y8MPIpSW8pw3 WpCs/Ko0RhsD1CMOcs/f4vfbKP9G+Qc54w6gglIikIJjgISuF+W+QSlfFeaAMgaoQzAGemjOFc8x E3gHYHf0v1bFlShLOCOemCTYKi1cvTfizlndwFgealR81Ib5Uyx9o1pvXaf1OG+rAHqjam+M5rCj mDJLrz3LjyTWhii4NVqIXrlJpu9s3QT85yhvcK4z6rjGIAZn3NdbRfHB7YNiOm8UlplaOwLPlUWg geJ3qj7jOBFjllEbx6XEbJNHwcaNNVj+Haj93mK7VMp7zWSld0VTx1tHv44kt8zKd2ry01GVLgME IENBPnU8fMoL4tzr3GDbq7ahNt4leav0OahxuyAgR+71DDVeQ/WWV3M8632QaCo+5oX1OQHxxLTl wYYxTW3UsVr1Q/6AJk7mmG0LWSWo+sup5OmMt+X1gWDcXMN6Xu1QmAB5o1ypjdm2nOtzmXMAasya ardyF8NaYljxwQHtuVSB1sVo0/0lPmI69f2QqFrLlYK58FZDjOTtHgTQMS+oCi+2zmq1uT/688aA ZC/GHv0HgETtQX9edu5v556+OX0CNLP8B4mDMNB8OZC3AAAAAElFTkSuQmCC !blob } sub escapeHTML { # todo: U+200F RIGHT-TO-LEFT MARK -> &rlm my %ch_to_ent = ( '>' => '>', '<' => '<', '&' => '&', '"' => '"', "'" => ''', '\\' => '\' ); state $chars = join '|', map { "\\$_" } grep { $_ ne '&' } sort keys %ch_to_ent; $_[ 0 ] =~ s/\&/$ch_to_ent{'&'}/gr =~ s/($chars)/$ch_to_ent{$1}/gr; } sub highlight_record { my ( $rec, %ucs ) = escapeHTML to_jnl_fmt $_[ 0 ]->{ rec }; $ucs{ ord substr $rec, $-[ 0 ], 1 }++ while $rec =~ /[^\p{ASCII}]/g; my ( $sz, $nreps ) = length $rec; foreach my $ord ( sort { $a <=> $b } keys %ucs ) { my $char = sprintf '%c', $ord; my $rplchar = sprintf '<U+%04X>', $ord; my $n = 0; die "Uniconv rec did not have chars $n" unless ( $n ) = $rec =~ s/$char/\\$rplchar\<\/mark\>\<\/span\>/g; # todo: u3's nameval on ie11 has busted markup. not all replacements done so it eats it? $rec = '' . 'RECORD TOO BIG.. todo:...
' . substr $rec, 0, 7 + rindex $rec, '
', 10_000 and last if $nreps += $n > 500 || $sz > 2 ** 20 && 10_000 < index $rec, '<'; } # todo: have an option to force this and maybe set the return type to octets so it'll be downloaded. # could also have a jnltool dump range invocation printed $rec } sub nlines { my $n = () = $_[ 0 ] =~ /\R+/g } sub fetch_records { my ( $db_name, $req, $post, $hdr, @recs ) = @_; my ( $dbh, $db ) = mk_uniconv_db $db_name; my $flt = exists $schema->{ $post->{ key } } ? { table => $post->{ key } } : { split /\0/, decode_base64 $post->{ key } }; my $nl = 0; dbscan $dbh, $flt, sub { return 1 unless $_[ 0 ]->{ rec }->{ table } eq $flt->{ table }; my $jnl = to_jnl_fmt $_[ 0 ]->{ rec }; $nl += nlines $jnl; return 1 unless @recs < $post->{ n }; # return 1 unless @recs && ( $nl < $post->{ linesy } * .90 ); ! push @recs, $_[ 0 ] }, $post->{ direction } eq 'prev' ? R_PREV : R_NEXT; undef $dbh; untie %$db; # todo: remove this when paging is implemented. #[ @recs[ 0 .. ( @recs > 10 ? 10 : @recs - 1 ) ] ] # \ @recs; [ $post->{ direction } eq 'prev' ? reverse @recs : @recs ] } =comment # No round-trip mapping for most table-driven conversions # perl -E 'use Encode;binmode STDOUT, ":encoding(UTF-8)"; say decode "UTF-8", join "", map chr, 0xF0, 0x9F, 0x8D, 0xAD' > out # $characters = decode('UTF-8', $octets, Encode::FB_CROAK); # $octets = encode('UTF-8', $characters, Encode::FB_CROAK); # octs <- encode [ chars <- decode $rec ] # map { my $trans; my $rt = $rec eq encode $_, decode( $_, $rec, sub { $trans++ ), sub { $trans++ }; push @rt_ok, $_ if !$trans && $rt } enc_list; # x-charset octs -> utf8-chars -> x-charset octs push @rt_ok, $_ if $rec # bytes eq # octs decode 'UTF-8', # octs -> chars encode 'UTF-8', # chars -> octs decode $_, $rec # octs -> chars for enc_list; =cut sub roundtrip_enc { my ( $rec, @encs ) = @_; local $SIG{__WARN__} = sub {}; my %encs = map { @$_ } grep { $$_[ 1 ] } map { [ $_, eval { $rec eq encode $_, decode $_, $rec } ] } @encs; \ %encs } sub roundtrip_encs { roundtrip_encs $_[ 0 ], enc_list } # todo: keep most recent ten charsets selected # todo: keep track of strings converted and use that to set a default for new records sub render_records { my ( $req, $post, $hdr, $recs, $n, $ret ) = @_; my ( $ls, $ml ) = ( 0, $post->{ linesy } ); $n = 0; for( my $i = 0; $i < @$recs; $i++ ) { $_ = $recs->[ $i ]; my %css; my $rec = dclone $_; my $cs = $rec->{ charsets }; # todo: consolidate this with uniconv()'s copy. foreach my $col ( keys %$cs ) { my $cs = $rec->{ charsets }->{ $col }; use bytes; # todo: for substr... necessary? eval { $rec->{ rec }->{ $col } = join '', map { my $cc = $rec->{ rec }->{ $col }; # xxx todo: why does this need to be copied? my $str = substr $cc, $_->[ 0 ], $_->[ 1 ] - $_->[ 0 ]; encode 'UTF-8', decode( $_->[ 2 ], $str, Encode::FB_CROAK ), Encode::FB_CROAK } @$cs; }; local $Data::Dumper::Sortkeys = 1; # todo: return an error to the gui, don't die. $hdr->{ status } = 'HTTP/1.0 400 Bad Request', return '' if $@; $css{ $_->[ 2 ] } = 1 for @$cs;#@{ $cs->{ $col } }; } my $charset_str = 1 == keys %css ? [ keys %css ]->[ 0 ] : ''; # todo: 'mixed'? # todo: html-escape $hr. my $hr = keys %css ? to_jnl_fmt $rec->{ rec } : highlight_record $_; # todo: the check needs to be that there are unassigned columns? my $last = 0; # $last++ if ($ls += nlines $hr) > $ml / 4; # last if $last && $n > 0; # todo: need color for edited but not saved. my $bg = $_->{ charsets } ? 'darkgray' : 'lightgray'; my $key = encode_base64( uni_db_key $_->{ rec } ) =~ s/\n//gr; # todo: charset='mixed' support my $recno = $_->{ recno } // $n++; # todo: remove this since js does it? my $frec = $i == 0 ? 'data-jnl_firstrec' : ''; my $lrec = ( $i == @$recs - 1 ) ? 'data-jnl_lastrec' : ''; $ret .= "
$hr
\n"; last if $last; } $ret; } # todo: uniconv-big.t has db.change data with trailing invalid chars that trigger the # roundtrip conversion test here. todo: try Encode::Guess Encode::Detective Encode::Detect sub http_recode_rec { my ( $db_name, $req, $post, $hdr, $rec ) = @_; my ( $dbh, $db ) = mk_uniconv_db $db_name; my %s = split /\0/, decode_base64 $post->{ key }; my $k = \ %s; # todo: not found error # todo: operate on record as sent from browser if it has been edited in-place dbscan $dbh, $k, sub { $rec = $_[ 0 ] }; undef $dbh; untie %$db; # todo: decode db access? # todo: have the gui send a range to encode, not just assume the whole rec. # todo: do decode here and change header to validate encoding choice # todo: ebcdic cp1047 can't handle full-record decoding. which others can't? map { my $dat = $rec->{ rec }->{ $_ }; $rec->{ charsets }->{ $_ } = [], push @{ $rec->{ charsets }->{ $_ } }, [ 0, bytes_length $dat, $post->{ charset } ] if $dat =~ /[^\p{ASCII}]/g } names $schema->{ $rec->{ rec }->{ table } }{ $rec->{ rec }->{ version } }; render_records $req, $post, $hdr, [ $rec ] } # todo: need a more obvious way in the gui to know which rec is selected for saving # todo: need to have a check ensuring the entire record and its fields are fully # encoded (no partial ranges) sub http_save_rec { my ( $db_name, $freq_name, $tabs_name, $req, $post, $hdr ) = @_; my ( $dbh, $db ) = mk_uniconv_db $db_name; my %s = split /\0/, decode_base64 $post->{ key }; my $k = \ %s; my $len = bytes_length to_jnl_fmt $db->{ $k }->{ rec }; # todo: not found error # todo: push to the list to add more ranges. disallow if already full. sort list. might be out of order. my $rec = $db->{ $k }; my $valid_encs = roundtrip_enc to_jnl_fmt( $rec->{ rec } ), $post->{ charset }; $hdr->{ status } = 'HTTP/1.0 400 Bad Request', return '' unless $valid_encs->{ $post->{ charset } }; map { my $dat = $rec->{ rec }->{ $_ }; $rec->{ charsets }->{ $_ } = [], push @{ $rec->{ charsets }->{ $_ } }, [ 0, bytes_length $dat, $post->{ charset } ] if $dat =~ /[^\p{ASCII}]/g } names $schema->{ $rec->{ rec }->{ table } }{ $rec->{ rec }->{ version } }; $db->{ $k } = $rec; undef $dbh; untie %$db; my $tabs = retrieve $tabs_name; my $tab = $rec->{ rec }->{ table }; $tabs->{ $tab }->{ remaining }--; store $tabs, $tabs_name; my $freqs = retrieve $freq_name; map { map { $freqs->{ $_->[ 2 ] }++ } @{ $rec->{ charsets }->{ $_ } } } keys %{ $rec->{ charsets } }; store $freqs, $freq_name; render_records $req, $post, $hdr, [ $rec ] } sub http_revert_rec { my ( $db_name, $tabs_name, $req, $post, $hdr ) = @_; my ( $dbh, $db ) = mk_uniconv_db $db_name; my %s = split /\0/, decode_base64 $post->{ key }; my $k = \ %s; my $rec = $db->{ $k }; delete $rec->{ charset }; $db->{ $k } = $rec; undef $dbh; untie %$db; my $tabs = retrieve $tabs_name; my $tab = $rec->{ rec }->{ table }; $tabs->{ $tab }->{ remaining }++; store $tabs, $tabs_name; render_records $req, $post, $hdr, [ $rec ] } sub http_conv_summary_completed { } sub http_conv_summary { # remaining my $tabs_name = $_[ 0 ]; my $summary; my $tabs = retrieve $tabs_name; my ( $remaining, $total ) = ( 0, 0 ); map { $total += $tabs->{ $_ }->{ total }; $remaining += $tabs->{ $_ }->{ remaining } } keys %$tabs; my @stabs = sort { $tabs->{ $b }->{ remaining } <=> $tabs->{ $a }->{ remaining } || $a cmp $b } keys %$tabs; $summary .= "\n" . "\n"; my $ckp_ver = 'r'. rel_to_ver_short check_ckp_ver $jnl, $min_rel, $min_ver, $max_rel, $max_ver; # todo: fix the schema link from jumping around # map { $summary .= "\n" } # todo: sort by table name to keep tables from floating around as they are fixed. # todo: move onclick to main document click handler. # todo: re-sort on js update map { $summary .= "\n" } @stabs; $summary .= "
summary
tablerow count
$_
?
$tabs->{ $_ }
$_
$tabs->{ $_ }->{ remaining }
\n"; } sub http_conv_cs_freqs { my $freqs = retrieve $_[ 0 ]; my @sorted = sort { $freqs->{ $b } <=> $freqs->{ $a } } keys %$freqs; my $html = ''; $html .= "
[$_] - $sorted[ $_ ]
" for 0 .. @sorted - 1; $html } sub encs_dropdown { join "\n", map { "" } enc_list } # todo: http://xhr.spec.whatwg.org/ make xmlhttprequests not synchronous. they're deprecated. sub uniconv_htmlguistr { << 'EOHTMLGUIW' << "EOHTMLGUI" ${\ escapeHTML $jnl_name } - Perforce database Unicode conversion
Application offline

Click to refresh
Use \N{UPWARDS ARROW} and \N{DOWNWARDS ARROW} to navigate.


${\ http_conv_summary $tabs_name }


\N{BLACK UP-POINTING TRIANGLE}

\N{BLACK DOWN-POINTING TRIANGLE}

EOHTMLGUI EOHTMLGUIW } sub uniconv_htmlgui404str { << 'EO404'; Perforce database Unicode conversion
404
EO404 } sub uniconv { # For ?{} in http://search.cpan.org/~rjbs/perl-5.18.0/pod/perldelta.pod#Selected_Bug_Fixes # The db.view/db.have matcher relies on this. ERR "Need at least Perl version v5.18.0. Found $^V.\n" if ! $^V or $^V lt v5.18; my %args = @_; my $ckp_name = delete $args{ CKP_NAME }; my $check = delete $args{ CHECK }; my $convert = delete $args{ CONVERT }; my $gui = delete $args{ GUI }; my $gui_lchk = delete $args{ GUI_LOCK_CHECK }; my $out_type = delete $args{ OUTPUT_TYPE } // 'jnl'; my $ckp_case = sniff_ckp_case; my $needs_case_copy = $ckp_case != 0; $args{ PAR_HELPER } //= ''; ( $args{ PAR_HELPER }, $args{ PAR_HELPER_ARGS } ) = $args{ PAR_HELPER } =~ /([^:]+):(.*)/ if $args{ PAR_HELPER }; ERR 'Output type must be either ckp|jnl.' unless $out_type =~ /^(?:jnl|ckp)$/; ERR "Hybrid-case (-C2) checkpoints are unsupported." if $ckp_case == 2; my $data_dir = File::Spec->rel2abs( File::Spec->catdir( $output_dir // dirname( $ckp_name ), basename( $ckp_name ) . '-jnltool-uniconv' ) ); my $fname = sub { File::Spec->catfile( $data_dir, @_ ) }; my $fname_int = sub { $fname->( '.internal', $_[ 0 ] ) }; my $mk_log_fh = sub { IO::File->new( $fname->( $_[ 0 ] ), '>>:encoding(UTF-8)' ) }; my $version_file = $fname_int->( 'version' ); # todo: must have a charset assigned for this. # todo: doc that droot must be in /this/that slash format without a drive? # todo: need a normalization pass on the file names for slashes? my $droot_name = $fname_int->( 'depot_root' ); my $droot = $check ? ( delete( $args{ DEPOT_ROOT } ) or dirname( File::Spec->rel2abs( $jnl_name ) ) ) : undef; ERR '--depot-root must be an absolute path.' if $check && ! File::Spec->file_name_is_absolute( $droot ); my $droot_encf = $fname_int->( 'depot_root_enc' ); my $droot_uni = ( $droot // '' ) =~ /[^\p{ASCII}]/; ERR '--depot-root is only necessary for --uniconv-check.' if ! $check && $args{ DEPOT_ROOT }; ERR '--depot-root-cs is only necessary for --uniconv-check.' if ! $check && $args{ DEPOT_ROOT_ENC }; ERR '--depot-root is non-ASCII and --depot-root-cs was not supplied.' if $check && $droot_uni && ! $args{ DEPOT_ROOT_ENC }; my $droot_enc = $args{ DEPOT_ROOT_ENC }; if( $check && $droot_enc ) { ERR "'$droot_enc' is not a valid encoding." unless exists $encs{ $droot_enc }; my $drenc0 = eval { decode $droot_enc, $droot }; ERR "--depot-root-cs=$droot_enc does not encode --depot-root=$droot" if $@; my $drenc1 = eval { encode $droot_enc, $drenc0, Encode::FB_CROAK }; ERR "--depot-root-cs=$droot_enc can not be used for --depot-root=$droot" if $@ || $droot ne $drenc1; } # todo: store nx ckp info to make sure ckp hasn't been swapped out. my $validate_data_dir = sub { ERR "Data directory '$data_dir' is missing or not a directory." unless -d $data_dir; ERR 'Config version file missing.' unless -e $version_file; my ( $v_cfg, $v_our ) = ( IO::File->new( $version_file, '<' )->getline, version_num ); ERR 'Malformed config version' if $v_cfg !~ /^\d+$/; ERR "Config version too new: $v_cfg vs $v_our" if $v_our < $v_cfg; ERR "depot_root path '$droot' isn't a directory." if $droot && ! -d $droot; }; my $recs_name = $fname_int->( 'records.db' ); my $tabs_name = $fname_int->( 'tabs.dat' ); my $freq_name = $fname_int->( 'cs_freqs.dat' ); my $conv_out = $fname->( 'out.jnl' ); my $log_name = $fname->( 'log.txt' ); # todo: multiprocess safe? my $db_lck = "$recs_name.lock"; my ( $log, $log_pl ) = map { $mk_log_fh->( $_ ) } 'log.txt', 'log.pl'; my $lock_db = sub { my %args = @_; my $lfh = IO::File->new( $db_lck, '>>' ) or die "no lockfile: $!"; flock( $lfh, $args{ NB } ? LOCK_EX | LOCK_NB : LOCK_EX ) ? $lfh : undef }; my $mk_log_fn = sub { my $log_std = $_[ 0 ]; sub { my ( $raw, $fmt ) = @_; use bytes; say $output_fh $fmt if $log_std; return unless $log && $log_pl; say $log $fmt; local $Data::Dumper::Terse = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Indent = 0; say $log_pl Dumper [ gettimeofday, @$raw ]; } }; my $db_has_recs = sub { my ( $dbf, $n ) = @_; return 0 unless -f $dbf; my $dbh = tie my %recs, 'DB_File', $dbf, O_RDONLY, 0666, $DB_BTREE or die "Cannot open file '$dbf': $!\n"; dbscan $dbh, undef, sub { $n++; 1 }; undef $dbh; untie %recs; return $n }; my $tabs_empty = sub { -f( $tabs_name ) ? ! keys %{ retrieve $tabs_name } : 1 }; goto $args{ PAR_HELPER } if $args{ PAR_HELPER }; return $mk_log_fn->( 1 ) if $args{ LOG_FN }; my $log_fn = $mk_log_fn->(); my $oglog = $global_log; $global_log = sub { $oglog->( @_ ); dbg_log $log_fn, @_; 1 }; dbg_log $log_fn, "uniconv() starting: ( ${\ version } ): @_\n" . "environment:\n${\ env_report }\n" . # todo: why isn't this in the log?! "checkpoint case: $ckp_case\n" . "PID: $$" if -d $data_dir; state $gui_lock; if( $gui_lchk ) { $gui_lock //= $lock_db->( NB => 1 ); ERR "Database already locked on '$db_lck'" if ! $gui_lock; } # todo: have a state config var with open handles to all the data so all fns # don't need to go opening and closing the db themselves. would only work # in a single-threaded http_svr. my $uniconv_gui = sub { my ( $pct, $dbg_border ) = ( 10, 0 ); $validate_data_dir->(); my $gstr = encode 'UTF-8', eval( uniconv_htmlguistr ), sub { die 'Encode error on: uniconv_htmlguistr' }; die "bad eval on uniconv_htmlguistr: $@" if $@; sub { my ( $req, $post, $hdr ) = @_; $validate_data_dir->(); $hdr->{ 'Content-Type' } = 'text/plain' , return 0, 'bye' if $req->{ URL } eq '/shutdown'; # $hdr->{ 'Content-Type' } = 'image/x-icon', return 1, favicon $req if $req->{ URL } eq '/p4.ico'; return 1, mk_man 'html' if $req->{ URL } eq '/manpage'; return 1, http_recode_rec $recs_name, $req, $post, $hdr if $req->{ URL } eq '/recode_rec'; return 1, http_save_rec $recs_name, $freq_name, $tabs_name, $req, $post, $hdr if $req->{ URL } eq '/save_rec'; return 1, http_revert_rec $recs_name, $tabs_name, $req, $post, $hdr if $req->{ URL } eq '/revert_rec'; return 1, render_records $req, $post, $hdr, fetch_records $recs_name, $req, $post, $hdr if $req->{ URL } eq '/render_records'; # todo: add a link to Support here. $hdr->{ status } = 'HTTP/1.0 400 Bad Request', return 1, uniconv_htmlgui404str if $req->{ URL } ne '/'; 1, $gstr # main interface } }; if( $gui ) { ERR 'GUI may not be run once --uniconv has completed.' if -e $fname_int->( 'uniconv-complete' ); ERR 'GUI requires results from --uniconv-check.' unless $db_has_recs->( $recs_name ) && !$tabs_empty->() && -e $fname_int->( 'check-complete' ); dbg_log $log_fn, "--uniconv-gui starting ( ${\ version } ): @_"; return $uniconv_gui->() } # todo: can't skip have because of 'obliterate -H'? # todo: revsh? ixdate integtx # todo: archmap is never cleaned up so needs to be checked? but -xi doesn't look there? # todo 1047418 has user.rp journaled now? # todo: db.ixtext could have spurious entries due to 'p4 index' manual runs. # todo: missing view lines due to ckp inconsistency my %dup_tabs = map { ( "db.$_" => $tab_locs{ "db.$_" } ) } grep { defined $tab_locs{ "db.$_" } } # todo: db.job (what about jobspec?)? remove revsx? add db.template? qw / archmap excl fix fixrev have have.rp integed integtx ixtext jnlack label locks resolve resolvex revbx revcx revdx revhx revpx revsh revsx revux sendq user.rp uxtext view.rp /; my %check_tabs = %{ dclone( $check ? \ %tab_locs : \ %dup_tabs ) }; # my %src_tabs = map { $_ => $tab_locs{ $_ } } # grep { ! exists $dup_tabs{ $_ } } keys %tab_locs; delete @check_tabs{ keys %dup_tabs } if $check; my $tabs_sz = range_size values %check_tabs; my @cts = sort keys %check_tabs; my $tlen = 0; map { my $l = length; $tlen = $l if $l > $tlen } @cts; $tlen += 5; if( $check ) { my $no_data = ! -d $data_dir; make_path "$data_dir/.internal" if $no_data; ( $log, $log_pl ) = map { $mk_log_fh->( $_ ) } 'log.txt', 'log.pl' unless $log; dbg_log $log_fn, "uniconv() starting ( ${\ version } ): @_" if $no_data; ERR '--uniconv-check already completed. Run --uniconv-gui to continue.' if -e $fname_int->( 'check-complete' ); ERR 'Incomplete --uniconv-check run? Intermediate files ' . 'records.db/tabs.dat/records.jnl already exist in ' . "'$data_dir'." if $db_has_recs->( $recs_name ) or -e $conv_out or ! $tabs_empty->(); IO::File->new( $db_lck, '>' ); my ( $dbh, $recs ) = mk_uniconv_db $recs_name; undef $dbh; untie %$recs; store {}, $tabs_name; store {}, $freq_name; # todo: is this broken by patch releases? print { IO::File->new( $version_file, '>' ) } version_num; print { IO::File->new( $droot_name , '>' ) } $droot; print { IO::File->new( $droot_encf , '>' ) } $droot_enc; } UNICONV_CHECK: $droot = slurp $droot_name if ! $check && -e $droot_name; $droot_enc = slurp $droot_encf if ! $check && -e $droot_encf; my $droot_utf8 = $droot_enc ? encode 'UTF-8', decode $droot_enc, $droot : $droot; $validate_data_dir->(); ERR '--uniconv may not be called until --uniconv-check has completed.' if $convert && ! -e $fname_int->( 'check-complete' ); ERR '--uniconv not necessary as there was no non-ASCII data found.' if $convert && ! -e $fname_int->( 'conversion-required' ); ERR '--uniconv may not be called until all records have been processed.' if $convert && -e( $tabs_name ) && reduce { $a + $b } map { $_->{ remaining } } values %{ retrieve $tabs_name }; my %cfgs; parse $jnl, sub { $cfgs{ "$_[ 0 ]->{ CFsname }\0$_[ 0 ]->{ CFname }" } = $_[ 0 ]->{ CFvalue }; 0 }, $tab_locs{ 'db.config' }; my $sd_root = $cfgs{ "any\0server.depot.root" }; die "server.depot.root is not an absolute path! '$sd_root'" if $sd_root && ! File::Spec->file_name_is_absolute( $sd_root ); my $sd_sep = ( $sd_root && ( $sd_root =~ /\\/ ) ) ? '\\' : '/'; $sd_root =~ s/\Q$sd_sep\E$// if $sd_root; my $sd_root_o = $sd_root; # Note that the depotMaps are populate with both the pre and post conversion # values and that the post conversion values are stored during the transcoding # loop. Any consumers must be after db.depot in locking order (since that's # the order the loop is processed in.) my ( %depotTypes, %depotMaps, %depotMaps_o ); parse $jnl, sub { $depotTypes{ $_[ 0 ]->{ DPname } } = ${ { 0 => 'local', 1 => 'remote', 2 => 'spec', 3 => 'stream', 4 => 'archive', 5 => 'unload', 6 => 'tangent', 7 => 'graph' , 8 => 'extension' } }{ $_[ 0 ]->{ DPtype } }; $depotMaps_o{ $_[ 0 ]->{ DPname } } = $depotMaps{ $_[ 0 ]->{ DPname } } = $_[ 0 ]->{ DPmap } =~ s,\Q/...,,gr; $depotMaps_o{ bytes_lc $_[ 0 ]->{ DPname } } = $depotMaps_o{ $_[ 0 ]->{ DPname } } if $needs_case_copy; 0 }, $tab_locs{ 'db.depot' }; my $unload_dp_name = sub { my $name; map { $name = $_ if $depotTypes{ $_ } eq 'unload' } keys %depotTypes; $name }; my $dr_abspath = sub { my $dmap = $depotMaps{ $_[ 0 ] }; File::Spec->file_name_is_absolute( $dmap ) ? $dmap : File::Spec->catdir( $sd_root // $droot, $dmap ); }; my $arch_file = sub { my ( $dfile, $type ) = @_; my ( $rcs, $bin, $tiny, $comp, $uncomp, $script ) = map { ( $type & 0x0000F ) == $_ } 0x00000, 0x00001, 0x00002, 0x00003, 0x00007, 0x00008; die "bad arch_file usage $dfile, $type" if $tiny || $script; my ( $depot ) = $dfile =~ m,^//([^/]+),,; $dfile =~ s,^//[^/]+/,,; my $ext = $rcs ? ',v' : ( $comp || $uncomp || $bin ) ? ',d' : ''; File::Spec->catfile( $dr_abspath->( $depot ), "$dfile$ext" ) =~ s,\\,/,gr; }; # todo: gets written during -check? # todo: needs to be part of -check? my ( $uldir, $ulfn, $ulfh ) = ( $fname_int->( 'unloaded' ), '' ); mkdir $uldir if $check && ! -d $uldir; # todo: need to collect child errors after processing. my $par_fn = sub { my ( $type, $tab, $r, $rec_src, $rec_src_file ) = @_; $jnl = open_ckp $ckp_name; my $save_work = sub { my ( $work, %tabs ) = $_[ 0 ]; for( my $i = 0; $i < @$work; $i++ ) { my $prec = $$work[ $i ][ 0 ]; $tabs{ $prec->{ table } }->{ total }++; $tabs{ $prec->{ table } }->{ remaining }++; } my $lfh = $lock_db->(); # todo: could have a separate db per process to avoid locking overhead. my ( $dbh, $recs ) = mk_uniconv_db $recs_name; my $otabs = retrieve $tabs_name; $tabs{ $_ }->{ total } += $otabs->{ $_ }->{ total } for keys %$otabs; $tabs{ $_ }->{ remaining } += $otabs->{ $_ }->{ total } for keys %$otabs; store \ %tabs, $tabs_name; # todo: max key length? for( 0 .. @$work - 1 ) { my $r = { rec => $$work[ $_ ][ 0 ], range => $$work[ $_ ][ 1 ] }; $r->{ SRC } = $rec_src, $r->{ SRC_FILE } = $rec_src_file if $rec_src; if( $needs_case_copy ) { $r->{ case_rec } = dclone $r->{ rec }; use bytes; # So lc() just does ASCII. $r->{ rec }->{ $_ } = lc $r->{ rec }->{ $_ } for names $schema->{ $r->{ rec }->{ table } }{ $r->{ rec }->{ version } }; } $recs->{ $r } = $r; } undef $dbh; untie %$recs; }; my $last_range = [ 0, 0 ]; my $base_offset = $tab_locs{ $tab }->[ 0 ]; my $check_cb = sub { my ( $ofh, $bufref, $loc, $bufsize, $ifh, @work ) = @_; return if ( $loc + $bufsize ) <= $$last_range[ 1 ]; # skip repeating a big record. use bytes; pos( ${ $_[ 1 ] } ) = ( $$last_range[ 1 ] > $loc ) ? $$last_range[ 1 ] - $loc : 0; my $op = pos( ${ $_[ 1 ] } ); my $np = $op; while( ${ $_[ 1 ] } =~ /[^\p{ASCII}]/g ) { next if $-[ 0 ] < $np; my $rec = { tab => $tab, l => $loc + $np, pos => $loc + $-[ 0 ], b => $base_offset }; my ( $prec, @range ) = find_rec_from_pos $ifh, $rec; die 'find_rec_from_pos error, could not find record in buffer: ' . Dumper $rec if ! defined $range[ 0 ]; $np = pos( ${ $_[ 1 ] } ) = $range[ 1 ] - $loc; push @work, [ $prec, \ @range ]; } return unless @work; $save_work->( \ @work ); $last_range = $work[ -1 ][ 1 ]; }; dump_range_fn $jnl, $r, undef, $check_cb, 1 if $type eq 'CKP'; $rec_src = 'UNLOAD' if $type eq 'UNLOAD'; parse $jnl, sub { my $cmp = ( $_[ 0 ]->{ REatype } & 0x0000F ) == 0x00003 ? '.gz' : ''; my $afile = $arch_file->( $_[ 0 ]->{ REafile }, $_[ 0 ]->{ REatype } ) . "/1.0$cmp"; $rec_src_file = $_[ 0 ]->{ REdfile }; my $ckp = $cmp ? File::Temp::tempfile : IO::File->new( $afile, '<' ); gunzip( $afile, $ckp ), $ckp->flush if $cmp; $ckp->seek( 0, SEEK_SET ); readline $ckp; # Get past the nx record for find_rec_from_pos(). $base_offset = 0; $last_range = [ 0, 0 ]; $tab = $afile; # todo: put in a hierarchy to avoid too many files in a dir? my $hash = "$uldir/" . md5_hex $_[ 0 ]->{ REdfile }; print { IO::File->new( "$hash.dfile" , '>' ) } $_[ 0 ]->{ REdfile }; print { IO::File->new( "$hash.afile" , '>' ) } $afile; print { IO::File->new( "$hash.dfile.UTF-8", '>' ) } $_[ 0 ]->{ REdfile }; print { IO::File->new( "$hash.afile.UTF-8", '>' ) } $_[ 0 ]->{ REafile }; store_fd $_[ 0 ], IO::File->new( "$hash.orec" , '>:raw' ); dump_range_fn $ckp, [ tell( $ckp ), -s $ckp ], undef, $check_cb, 1; 0 }, $r if $type eq 'UNLOAD'; 0 }; if( $args{ PAR_HELPER } eq 'UNICONV_CHECK' ) { my @as = split ',', $args{ PAR_HELPER_ARGS }; return $par_fn->( shift @as, shift @as, [ @as ] ) ? 1 : 0; } status_heading $check ? "searching for non-ASCII data:\n" : "transcoding records to UTF-8:\n"; # todo: rename my $par_n = $parallel; my @tabs = map { [ $_, split_table $jnl, $tab_locs{ $_ }, $par_n ] } @cts; my $cur_sz = 0; my $start_time = localtime; my $ul_name = $unload_dp_name->(); my $ul_dir = $ul_name ? $dr_abspath->( $ul_name ) : undef; my $has_ul_depot_dir = $ul_dir ? -d $ul_dir : undef; status_run 'Processing unloaded data', sub { my @procs = status_agg map { my $par_arg = $^O eq 'MSWin32' ? "UNICONV_CHECK:UNLOAD,db.revux,$$_[0],$$_[1]" : $par_fn; par $par_arg, 'UNLOAD', 'unloaded_files', $_ } split_table $jnl, $tab_locs{ 'db.revux' }, $par_n; usleep $status_child_wait while @procs = status_agg @procs } if $check && $has_ul_depot_dir; WARN "Unloaded depot '$ul_name' directory '$ul_dir' missing, not processing unloaded files." if $ul_name && ! $has_ul_depot_dir; status_heading 'Processing checkpoint'; for( my $i = 0; $i < @tabs; $i++ ) { my $tab = shift @{ $tabs[ $i ] }; my @work = batch_ranges @{ $tabs[ $i ] }; my $tabfmt = $tab . ' ' x ( $tlen - length $tab ); my $rs = range_size @{ $tabs[ $i ] }; my $st = sprintf '% 2u/%u % 2u%% - % 3u%% %s', $i, @tabs - 1, ( $cur_sz / $tabs_sz ) * 100, ( ( $cur_sz + $rs ) / $tabs_sz ) * 100, $tabfmt; $cur_sz += $rs; status_run $st, sub { my @procs = status_agg map { my $par_arg = $^O eq 'MSWin32' ? "UNICONV_CHECK:CKP,$tab,$$_[0],$$_[1]" : $par_fn; par $par_arg, 'CKP', $tab, $_ } @{ $work[ 0 ] }; usleep $status_child_wait while @procs = status_agg @procs }; } die "why do pids haunt me? @pids" if @pids; status_heading "\nProcessing took " . ( scalar (localtime) - $start_time )->pretty; if( $check ) { IO::File->new( $fname_int->( 'check-complete' ), '>' ); my $ascii = "\nCheckpoint is just ASCII. Run 'p4d -xi' on " . "the database to go to Unicode mode."; status_heading( $ascii ), return 0 if ! $db_has_recs->( $recs_name ); # todo: git notes also? WARN 'Non-ASCII graph depot data (git refs) detected. Unless the charset' . ' is found to be all UTF-8, conversion will not be possible.' if grep defined, @{ retrieve $tabs_name }{ qw/ db.ref db.refhist / }; IO::File->new( $fname_int->( 'conversion-required' ), '>' ); status_heading "\nCheckpoint has non-ASCII characters needing " . "charset assignment.\nRun '\"$orig_progname\" --uniconv-gui" . " \"$ckp_name\"' to continue."; return 0 } $start_time = localtime; ################################################################################ # scan %dups_tab in ckp, update records-dups.db, partition records.db, parallel transcode. # to avoid going over dups_tab twice, map/probe it when there's not many records? # two modes, sparse journal output just emitting records needing changing, and # full ckp transcode (best when there's too much data or data you don't want # to change piecemeal.) # todo: some of these (like archmap) should get processed via by-path charset defaults. # e.g. all matching AMafile's at once. # todo: by-path could be partially constructed automatically. maybe by-col? # use cluster.t's commands # todo: add workingx to dups? # @pv@ 10 @db.workingx@ @//7/depot/file9@ @//depot/file9@ @7@ @user@ 1 1 0 65538 1 7 TENDIGITMASK 0 D41D8CD98F00B204E9800998ECF8427E 0 0 0 65538 @@ 0 # todo: for jnlout, the default replay invocation needs to include -s so replicas get it. # secondary table list, with their keys needing charset conversion and their source table keys my %trans = ( # db.rev REdfile, RErev # db.domain DOname # db.job JOjob # db.user USuser # todo: when looking up a rev record, need to make sure to get one that's either a # whole-record charset, or one with a matching column. 'db.archmap' => { AMafile => [ qw / db.rev REafile / ], AMdfile => [ qw / db.rev REdfile / ] }, 'db.excl' => { EXdfile => [ qw / db.rev REdfile / ], EXclient => [ qw / db.domain DOname / ], EXuser => [ qw / db.user USuser / ] }, 'db.fix' => { FXjob => [ qw / db.job JOjob / ], FXclient => [ qw / db.domain DOname / ], FXuser => [ qw / db.user USuser / ] }, # 'db.fix' => { FXjob => [ qw / db.bodtext BTkey / ], FXclient => [ qw / db.domain DOname / ], FXuser => [ qw / db.user USuser / ] }, 'db.fixrev' => { FXjob => [ qw / db.job JOjob / ], FXclient => [ qw / db.domain DOname / ], FXuser => [ qw / db.user USuser / ] }, # db.view vfile? what if view changes? assume unique dir conversions in db.rev? # $map? changemap, havemap # 'db.have' => { HAdfile => [ qw / db.rev REdfile / ], HAcfile => { fn => sub {} } }, 'db.have' => { HAdfile => [ qw / db.rev REdfile / ], HAcfile => [ qw / db.rev REdfile / ] }, #db.have.rp disallow 'db.integed' => { INtfile => [ qw / db.rev REdfile / ], INffile => [ qw / db.rev REdfile / ] }, 'db.integtx' => { INtfile => [ qw / db.rev REdfile / ], INffile => [ qw / db.rev REdfile / ] }, # todo: 'p4 index' additions ITword 'db.ixtext' => { ITword => [ qw / db.bodtext BTtext / ], ITvalue => [ qw / db.job JOjob / ] }, # non-precious 'db.jnlack' 'db.label' => { LAname => [ qw / db.domain DOname / ], LAfile => [ qw / db.rev REdfile / ] }, 'db.locks' => { LOdfile => [ qw / db.rev REdfile / ], LOclient => [ qw / db.domain DOname / ], LOuser => [ qw / db.user USuser / ] }, # client file, depot file 'db.resolve' => { RStfile => [ qw / db.rev REdfile / ], RSffile => [ qw / db.rev REdfile / ], RSbfile => [ qw / db.rev REdfile / ] }, 'db.resolvex' => { RStfile => [ qw / db.rev REdfile / ], RSffile => [ qw / db.rev REdfile / ], RSbfile => [ qw / db.rev REdfile / ] }, 'db.revbx' => { REdfile => [ qw / db.rev REdfile / ], REafile => [ qw / db.rev REafile / ] }, 'db.revcx' => { RXdfile => [ qw / db.rev REdfile / ] }, 'db.revdx' => { REdfile => [ qw / db.rev REdfile / ], REafile => [ qw / db.rev REafile / ] }, 'db.revhx' => { REdfile => [ qw / db.rev REdfile / ], REafile => [ qw / db.rev REafile / ] }, # todo: non-precious? 'db.revpx' => { REdfile => [ qw / db.rev REdfile / ], REafile => [ qw / db.rev REafile / ] }, 'db.revsh' => { REdfile => [ qw / db.rev REdfile / ], REafile => [ qw / db.rev REafile / ] }, 'db.revsx' => { REdfile => [ qw / db.domain DOname / ], REafile => [ qw / db.domain DOname / ] }, 'db.revux' => { REdfile => [ qw / db.domain DOname / ], REafile => [ qw / db.domain DOname / ] }, # non-precious 'db.sendq' # db.user.rp disallow # 'db.uxtext' => { ITword => [ qw // ], ITvalue => [ qw // ] }, # 'db.uxtext' => { ITword => [ qw / db.bodtext BTtext / ], ITvalue => [ qw / db.job JOjob / ] }, # db.view.rp disallow ); # todo: up the read cache here? # https://docs.oracle.com/cd/E17276_01/html/programmer_reference/general_am_conf.html#am_conf_cachesize # $b->{'cachesize'} ; my ( $dbh, $recs ) = mk_uniconv_db $recs_name; # Assume that there will never be two REdfile/REafile's that are byte-identical but # intended to be in different charsets. I.e. those columns are globally unique. my ( $df2c_db, $af2c_db ) = ( $fname_int->( 'df2c.db' ), $fname_int->( 'af2c.db' ) ); my $df2c_dbh = tie my %df2c, 'DB_File', $df2c_db, O_RDWR|O_CREAT, 0666, $DB_BTREE or die "Cannot open file '$df2c_db': $!\n"; my $af2c_dbh = tie my %af2c, 'DB_File', $af2c_db, O_RDWR|O_CREAT, 0666, $DB_BTREE or die "Cannot open file '$af2c_db': $!\n"; $df2c_dbh->filter_store_value( sub { $_ = Storable::freeze( \ $_ ) } ); $af2c_dbh->filter_store_value( sub { $_ = Storable::freeze( \ $_ ) } ); $df2c_dbh->filter_fetch_value( sub { $_ = ${ Storable::thaw( $_ ) } } ); $af2c_dbh->filter_fetch_value( sub { $_ = ${ Storable::thaw( $_ ) } } ); my $integ_check = IO::File->new( $fname->( 'integ_check.pl' ), '>:raw' ); my $skiprec_name = $fname->( 'skipped_records.pl' ); my $skipped_records = IO::File->new( $skiprec_name, '>:raw' ); my $skipfiles_name = $fname->( 'skipped_files.pl' ); my $skipped_files = IO::File->new( $skipfiles_name, '>:raw' ); my $rev_prog_max = reduce { my ( $x, $y ) = ( $a // 0, $b // 0 ); $x = ref( $x ) ? $x->{ total } : $x; $y = ref( $y ) ? $y->{ total } : $y; $x + $y } @{ retrieve $tabs_name }{ qw / db.rev db.revtx / }; my $rev_prog_cnt = 0; status_heading 'creating rev index'; status_run 'creating rev index', sub { foreach my $tab ( 'db.rev', 'db.revtx' ) { # todo revsh etc? dbscan $dbh, { table => $tab }, sub { my $r = $_[ 0 ]; return 1 if $r->{ rec }->{ table } ne $tab; status $rev_prog_cnt, $rev_prog_max if $rev_prog_cnt++ % 10_000 == 0; # todo: multiple charset in a column will never happen to these? $df2c{ $r->{ rec }->{ REdfile } } = $r->{ charsets }->{ REdfile } // [ [ 0, bytes_length $r->{ rec }->{ REdfile }, 'latin1' ] ]; $af2c{ $r->{ rec }->{ REafile } } = $r->{ charsets }->{ REafile } // [ [ 0, bytes_length $r->{ rec }->{ REafile }, 'latin1' ] ]; 0 } } } if $rev_prog_max; my %g2c; dbscan $dbh, { table => 'db.group' }, sub { my $r = $_[ 0 ]; return 1 if $r->{ rec }->{ table } ne 'db.group'; # todo: won't be a charset here if it's just a user in the group? return 0 unless $r->{ charsets }->{ GRgroup }; $g2c{ $r->{ rec }->{ GRgroup } } = $r->{ charsets }->{ GRgroup }; 0 }; my $c2v_db = $fname_int->( 'c2v.db' ); my $c2v_dbh = tie my %c2vh, 'DB_File', $c2v_db; my $c2v = \ %c2vh; $c2v_dbh->filter_store_value( sub { $_ = Storable::freeze( \ $_ ) } ); $c2v_dbh->filter_fetch_value( sub { $_ = ${ Storable::thaw( $_ ) } } ); # todo: fold these funcs into mk_uniconv_db my $armv_fsk = sub { use bytes; return unless $_; # todo: where is this coming from? $_ = $needs_case_copy ? lc $_ : $_ }; my $armv_fsv = sub { use bytes; if( $needs_case_copy ) { my $h = $_; if( ref $h eq 'HASH' ) { map { $h->{ $_ } = lc $h->{ $_ } } keys %$_; } else { $h = lc $h } $_ = $h } $_ = Storable::freeze( \ $_ ) }; # $c2v_dbh->filter_store_key( $armv_fsk ); # $c2v_dbh->filter_store_value( $armv_fsv ); my $p4wild_re = qr /\*|\.\.\.|\%\%[1-9]{1}/; my $splitp4wild = sub { grep length, split /($p4wild_re)/, $_[ 0 ] }; my $p4wild2re = sub { my ( $path, $cap ) = @_; use bytes; # for lc $path = lc $path if $needs_case_copy; join '', map { $cap ? "($_)" : $_ } # %%N -> *, * -> [^/]*, ... -> .*, //depot/ -> \/\/depot\/ map { ( s/\%\%[1-9]{1}/*/ + s,\*,[^/]*, + s/\Q...\E/.*/ ) ? $_ : quotemeta } $splitp4wild->( $path ) }; status_start 'creating view index'; my $view_tell_check = 0; my $mk_c2v = sub { my $r = $_[ 0 ]; my $n = $r->{ VIname }; use bytes; # for lc $n = lc $n if $needs_case_copy; my $rec = $c2v->{ $n }; if( $needs_case_copy ) { use bytes; # for lc $r->{ $_ } = lc $r->{ $_ } for keys %$r; } push @{ $rec->{ SEQUENCES } }, $r; goto DONE if $_[ 0 ]->{ VImapflag } == 1; # Don't match exclusionary maps. # todo: need to anchor at ^? my $compile = sub { my ( $path, $key ) = @_; # \z ensures whole paths are matched. (*F) forces all patterns to be considered. # (?{}) records which db.view sequence numbers matched. ( $rec->{ $key } ? '|' : '' ) . $p4wild2re->( $path ) . "\\z(?{push \@ms_$key, $r->{ VIseq }})(*F)"; }; $rec->{ re_dfile } .= $compile->( $r->{ VIdfile }, 're_dfile' ); $rec->{ re_vfile } .= $compile->( $r->{ VIvfile }, 're_vfile' ); DONE: $c2v->{ $n } = $rec; }; parse $jnl, sub { $mk_c2v->( @_ ); status tell( $jnl ) - $tab_locs{ 'db.view' }->[ 0 ], $tab_locs{ 'db.view' }->[ 1 ] - $tab_locs{ 'db.view' }->[ 0 ] if $view_tell_check++ % 10_000 == 0; 0 }, $tab_locs{ 'db.view' } if $tab_locs{ 'db.view' }; # todo: move this into the initial db.revux parse in --uniconv-check so it # happens in parallel and we only have to parse the table once. parse $jnl, sub { my ( $type ) = $_[ 0 ]->{ REdfile } =~ m,^//[^/]+/([^/]+)/,; return 0 if $type ne 'client'; my $cmp = ( $_[ 0 ]->{ REatype } & 0x0000F ) == 0x00003 ? '.gz' : ''; my $afile = $arch_file->( $_[ 0 ]->{ REafile }, $_[ 0 ]->{ REatype } ) . "/1.0$cmp"; my ( $ckp, $ckp_name ) = $cmp ? File::Temp::tempfile : ( IO::File->new( $afile, '<:raw' ), $afile ); gunzip( $afile, $ckp ), $ckp->flush if $cmp; $ckp->seek( 0, SEEK_SET ); # todo: $ckp is autoremoved? parse $ckp, sub { $mk_c2v->( @_ ) if $_[ 0 ]->{ table } eq 'db.view'; 0 }, [ 0, -s $ckp_name ]; $ckp->close, unlink $ckp_name if $cmp; # todo: enable progress # status tell( $jnl ) - $tab_locs{ 'db.view' }->[ 0 ], # $tab_locs{ 'db.view' }->[ 1 ] - $tab_locs{ 'db.view' }->[ 0 ] # if $view_tell_check++ % 10_000 == 0; 0 }, $tab_locs{ 'db.revux' } if $tab_locs{ 'db.revux' }; status_end; parse $jnl, sub { my $name = $_[ 0 ]->{ DPname }; $depotTypes{ $name } = ${ { 0 => 'local', 1 => 'remote', 2 => 'spec', 3 => 'stream', 4 => 'archive', 5 => 'unload', 6 => 'tangent', 7 => 'graph' , 8 => 'extension' } }{ $_[ 0 ]->{ DPtype } }; $depotTypes{ bytes_lc $name } = $depotTypes{ $name } if $needs_case_copy; $depotMaps{ $name } = $_[ 0 ]->{ DPmap } =~ s,\Q/...,,gr; $depotMaps{ bytes_lc $name } = $depotMaps{ $name } if $needs_case_copy; 0 }, $tab_locs{ 'db.depot' }; # todo: check for rev (and others!) records that are duplicated/collide when encoding is applied. local $Data::Dumper::Sortkeys = 1; status_start 'processing secondary tables'; my $record_count = reduce { my $x = ref( $a ) ? $a->{ total } : $a; $x + $b->{ total } } values %{ retrieve $tabs_name }; # todo: shelved +k files (task too? unloaded possible?), auth trigger list # todo: need a way to automatically do the depot half of views while letting the user select the rhs # db.have/db.label has to come after db.revsx. todo: revtx? revux? my %dup_process = %dup_tabs; delete @dup_process{ qw / db.have db.label / }; my $recs_processed = 0; # todo: assume that save_recs assigned latin1 to any ASCII fields already? foreach my $tab ( sort( keys %dup_process ), 'db.have', 'db.label' ) { my $pg = pager $dbh, $tab, 10_000; while( my $rs = $pg->() ) { foreach my $rec ( @$rs ) { status $recs_processed, $record_count if $recs_processed++ % POSIX::ceil( $record_count / 10_000 ) == 0; die 'u wot, m8?! ' . Dumper $rec if exists $rec->{ charsets }; my %css; my $needs_integ_check; my @ks = keys %{ $trans{ $tab } }; @ks = sort @ks if $sort_dumps; foreach my $rec_col ( @ks ) { # todo: just check $rec->{ charsets } for this? my $col_has_cs = $rec->{ rec }->{ $rec_col } =~ /[^\p{ASCII}]/; next if ! $col_has_cs && $tab ne 'db.have'; # say STDERR "$tab $rec_col '$rec->{ rec }->{ $rec_col }', " . $rec->{ rec }->{ $rec_col } =~ /[^\p{ASCII}]/; my ( $src_tab, $src_col ) = @{ $trans{ $tab }->{ $rec_col } }; my $rev = $src_tab eq 'db.rev'; my $src_rec; # todo: don't do a lookup for archmap/ixtext/etc $src_rec = $recs->{ { table => $src_tab, op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ $src_tab }, $src_col => $rec->{ rec }->{ $rec_col } } } unless $rev; my $col = $rec->{ rec }->{ $rec_col }; my $revdomain; my $revsxtype; # todo: move this ahead of the first src_rec lookup? if( $tab eq 'db.revbx' || ( $tab eq 'db.resolve' || $tab eq 'db.resolvex' ) && $rec_col eq 'RStfile' || $tab eq 'db.revux' || $tab eq 'db.revsx' ) { ( $revdomain ) = $col =~ m,^//([^/]+),,; # //domain/depot/file -> domain $col =~ s,^//[^/]+,/,; # //domain/depot/file -> //depot/file # spec: branch, client, depot, group, job, label, ldap, remote, server, spec, stream, user # in domain: branches,, clients, depots, labels, , streams, and typemap # not in dm: group, job, ldap, remote, server, spec, user # todo: spec forms? my %spec_map = ( 'db.job', 'JOjob', 'db.ldap', 'LDname', 'db.remote', 'RMid', 'db.server', 'SVid', 'db.user', 'USuser' ); # //spec/server/master.p4s # //spec/group/group.p4s # //spec/protect.p4s license, protect, triggers, typemap # //spec/stream/stream/main.p4s ( $revsxtype ) = $rec->{ rec }->{ $rec_col } =~ m,^//[^/]+/(\w+),; # 'group', 'job', etc # todo: do both revux cols at once to avoid a second lookup # //unload/client/1,d/domain.ckp -> domain # //spec/job/name.p4s, //spec/client/domain.p4s -> domain. //spec/protect.p4s (no domain, handled later). # todo: custom spec extention ( $col ) = $col =~ m,/([^/]+)\.(?:ckp|p4s)$,g if $tab eq 'db.revux' || ( $tab eq 'db.revsx' && ( $revsxtype eq 'stream' && $rec_col eq 'REafile' || $revsxtype ne 'stream' ) ); # todo: do this after the column lookup too? $rec->{ SKIP } = 'spec depot, unsupported suffix', say( $skipped_records uDumper $rec ), last if ! $col; # col //stream/38,d/stream-áíáãêáóôéêÜ%2Fmain-áíáãêáóôéêÜ.p4s ( $col ) = $col =~ m,/([^/]+/[^/]+)\.(?:ckp|p4s)$,g if $tab eq 'db.revsx' && $revsxtype eq 'stream' && $rec_col eq 'REdfile'; $col = "//$col" if $tab eq 'db.revsx' && $revsxtype eq 'stream'; # todo: only if stream? todo: only some chars? todo: revsx only REafile? $col = uri_unescape $col if ( $tab eq 'db.revux' || $tab eq 'db.revsx' ) && $rec_col eq 'REafile' || $tab eq 'db.revux' && $rec_col eq 'REdfile'; ( $src_tab, $src_col ) = ( "db.$revsxtype", $spec_map{ "db.$revsxtype" } ) if $tab eq 'db.revsx' && exists $spec_map{ "db.$revsxtype" }; $src_rec = $recs->{ { table => $src_tab, op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ $src_tab }, $src_col => $col } } if $tab eq 'db.revux' || $tab eq 'db.revsx'; } # todo: don't do a lookup for archmap/ixtext/etc my $cs = $rev ? $src_col eq 'REdfile' ? $df2c{ $col } : $af2c{ $col } : $src_rec->{ charsets }->{ $src_col }; # Remote depot data, deleted depots, etc. if( $tab eq 'db.integed' && ! $cs ) { $cs = $df2c{ $rec->{ rec }->{ INffile } } if $rec_col eq 'INtfile'; $cs = $df2c{ $rec->{ rec }->{ INtfile } } if $rec_col eq 'INffile'; die "no integed: " . uDumper $rec unless $cs; $needs_integ_check = 1; # todo: wrong for ranges. write these to a separate file for manual inspection # until a second --uniconv-gui pass can be created. $cs->[ -1 ]->[ 1 ] = bytes_length $col; } =comment Find the view line that created HAcfile and get its charsets from VIvfile and HAdfile using the mapping. 0. Make a list of non-exclusionary LHS VIdfile view mappings that match HAdfile 1. Take the last VIvfile and get its charset data a. If VIdfile is a ditto-map, find the last VIvfile in [0]'s list that matches HAcfile 2. Split HAdfile/HAcfile into lists by the wildcards in VIdfile/VIvfile 3. Make a list of charset ranges matching [2]'s pieces 4. Match the HAcfile list in [2] with [3]'s charset ranges, VIvfile's parts for path literals and HAdfile's for the wildcards. =cut next if $tab eq 'db.have' && $rec_col eq 'HAcfile'; if( $tab eq 'db.have' && $rec_col eq 'HAdfile' ) { my ( $depot ) = $rec->{ rec }->{ HAdfile } =~ m,^//([^/]+)/,; dbscan( $dbh, { table => 'db.revsx', op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ 'db.revsx' }, REdfile => $col }, sub { $src_rec = $_[ 0 ]; 1 } ), $cs = $src_rec->{ charsets }->{ REdfile } if $depotTypes{ $depot } eq 'spec'; my ( $client ) = $rec->{ rec }->{ HAcfile } =~ m,^//([^/]+)/,; my ( $vs, @ms_re_dfile, @ms_re_vfile ) = $c2v->{ $client }; use re 'eval'; # Allow code execution in the regex via(?{}) no utf8; # xxx todo: why does the non-ASCII data in the regex cause invalid UTF-8 errors? die "no \$vs!: client: '$client'" if ! $vs; # Step 0. $col =~ /$vs->{ re_dfile }/; # todo: /o? http://perldoc.perl.org/perlop.html#Regexp-Quote-Like-Operators die "no HAdfile match!\nrec: " . uDumper( $rec ) . "\nre_dfile: " . uDumper( $vs->{ re_dfile } ). "\n" . $vs->{ re_dfile } unless @ms_re_dfile; # Step 1. my $seq = $ms_re_dfile[ -1 ]; my $vrec = $recs->{ $vs->{ SEQUENCES }->[ $seq ] }; my $vr = $vs->{ SEQUENCES }->[ $seq ]; if( $vr->{ VImapflag } == 5 ) { $rec->{ rec }->{ HAcfile } =~ /$vs->{ re_vfile }/; die "no HAvfile match! seq $seq: " . uDumper( $rec ) . uDumper( $vs->{ re_vfile } ) unless @ms_re_vfile; my %re_dfiles = map { $_, 1 } @ms_re_dfile; # Step 1a. $re_dfiles{ $_ } ? ( $seq = $_, last ) : 0 for reverse @ms_re_vfile; $vrec = $recs->{ $vs->{ SEQUENCES }->[ $seq ]->{ rec } }; } $rec->{ SKIP } = 'db.have no dfile', say( $skipped_records uDumper $rec ), last if ! $cs && $col =~ /[^\p{ASCII}]/; $cs //= [ [ 0, bytes_length $col, 'latin1' ] ]; $vrec //= { rec => $vr }; $vrec->{ charsets }->{ VIvfile } //= [ [ 0, bytes_length $vr->{ VIvfile }, 'latin1' ] ]; $vrec->{ charsets }->{ VIdfile } //= [ [ 0, bytes_length $vr->{ VIdfile }, 'latin1' ] ]; my ( $dfre, $vfre ) = map { $p4wild2re->( $vrec->{ rec }->{ $_ }, 1 ) } 'VIdfile', 'VIvfile'; # VIdfile //depot/*/file VIvfile //ws/*/file # @LHS HAdfile [//depot/][dir][/file] @RHS HAcfile [//ws/][dir][/file] ('*' doesn't include '/' ) # @LHS_css HAdfile [[0,6,'latin1'],[6,9,'latin1'],[9,14,'latin1']] # @RHS_wild VIvfile [//ws/][*][/file] # @RHS_css VIvfile [[0,4,'latin1'],[4,5,'latin1'],[5,10,'latin1']] # Step 2. my @LHS = grep length, $col =~ /$dfre/; # (file)(...)\z the ... is empty, but captured. my @RHS = grep length, $rec->{ rec }->{ HAcfile } =~ /$vfre/; # die "HAcfile wild mismatch: " . uDumper( $rec ) . "\nvrec:".uDumper( $vrec )."\nlhs '$col', '$vrec->{ rec }->{ VIdfile }':" #.Dumper(\@LHS)."\nrhs '$rec->{ rec }->{ HAcfile }', '$vrec->{ rec }->{ VIvfile }':".Dumper(\@RHS) # unless @LHS && @RHS && @LHS == @RHS; my @RHS_wild = $splitp4wild->( $vrec->{ rec }->{ VIvfile } ); my @LHS_wild = $splitp4wild->( $vrec->{ rec }->{ VIdfile } ); # Step 3. my @LHS_css; my @LHS_css_pct; push @LHS_css_pct, undef; # %%n indexing starts at 1 $LHS_css[ 0 ] = [ [ 0, 0, undef ] ]; for( my $i = 0; $i < @LHS; $i++ ) { # todo? no autovivification 'strict'; my ( $vra, $vrb ) = $LHS_css[ $i - 1 ]->[ -1 ]->[ 1 ]; $vrb = $vra + bytes_length $LHS[ $i ]; #say STDERR "LHS_css -1 (".($i-1).") a (vra $vra, vrb $vrb) lhs[$i] '$LHS[$i]' " . uDumper( $LHS_css[ $i - 1] ).', cs '.uDumper($cs)."\n"; my @lcs = map { dclone $_ } grep { $vra < $_->[ 1 ] && $_->[ 0 ] < $vrb } @$cs; #say STDERR "lcs $i '$LHS[$i]' " . uDumper( \@lcs)."\n"; # todo: need to correct the rest of the list? $lcs[ 0 ]->[ 0 ] = $vra; $lcs[ -1 ]->[ 1 ] = $vrb; #say STDERR "lcs $i done '$LHS[$i]' " . uDumper( \@lcs)."\n"; die "LHS_css no cs " . uDumper( $rec ) . uDumper( $vrec ) . uDumper \ @lcs unless $lcs[ -1 ]->[ 2 ]; push @LHS_css_pct, dclone [ @lcs ] if $LHS_wild[ $i ] =~ /\%\%[1-9]{1}/; $LHS_css[ $i ] = dclone [ @lcs ]; } # Step 3. my @RHS_css; $RHS_css[ 0 ] = [ [ 0, 0, undef ] ]; for( my $i = 0; $i < @RHS_wild; $i++ ) { my ( $vra, $vrb ) = $RHS_css[ $i - 1 ]->[ -1 ]->[ 1 ]; $vrb = $vra + bytes_length $RHS_wild[ $i ]; #say STDERR "RHS_css -1 (".($i-1).") a (vra $vra, vrb $vrb) '$vrec->{rec}->{VIvfile}' " . uDumper( $RHS_css[ $i - 1] ).', cs '.uDumper($vrec->{ charsets }->{ VIvfile })."\n"; my @rcs = map { dclone $_ } grep { $vra < $_->[ 1 ] && $_->[ 0 ] < $vrb } @{ $vrec->{ charsets }->{ VIvfile } }; #say STDERR "rcs $i '$RHS_wild[$i]' " . uDumper( \@rcs)."\n"; $rcs[ 0 ]->[ 0 ] = $vra; $rcs[ -1 ]->[ 1 ] = $vrb; #say STDERR "rcs b " . uDumper \@rcs; #say STDERR "rcs c '$RHS_wild[$i]', '$rec->{rec}->{HAcfile}'"; die "RHS_css no cs " . uDumper( $rec ) . uDumper( $vrec ) . uDumper \ @rcs unless $rcs[ -1 ]->[ 2 ]; $RHS_css[ $i ] = dclone [ @rcs ]; } =comment say STDERR 'LHS ' . uDumper @LHS; say STDERR 'LHS_css ' . uDumper @LHS_css; say STDERR 'LHS_css_pct ' . uDumper @LHS_css_pct; say STDERR 'RHS ' . uDumper @RHS; say STDERR 'RHS_wild ' . uDumper @RHS_wild; say STDERR 'RHS_css ' . uDumper @RHS_css; =cut # Step 4. my @rhs_cs; $rhs_cs[ 0 ] = [ [ 0, 0, undef ] ]; for( my $i = 0; $i < @RHS; $i++ ) { # not @RHS_wild since it might have a trailing wildcard that didn't match, making @RHS have fewer elements #say STDERR "lhs '$LHS[$i]', rhs '$RHS[$i]', '$col', cs " . uDumper $cs; my $j = $RHS_wild[ $i ] =~ $p4wild_re ? $RHS_wild[ $i ] =~ /\%\%(\d+)/ ? $LHS[$1] : $LHS[$i] : $RHS[$i]; my $part = $RHS_wild[ $i ] =~ $p4wild_re ? $RHS_wild[ $i ] =~ /\%\%(\d+)/ ? $LHS_css_pct[ $1 ] : $LHS_css[ $i ] : $RHS_css[ $i ]; #say STDERR "$i part a " . uDumper( $part)." '$j'"; my $off = $part->[ 0 ]->[ 0 ]; $part = [ map { [ $_->[ 0 ] - $off, $_->[ 1 ] - $off, $_->[ 2 ] ] } @$part ] if $off; $off = $rhs_cs[ $i - 1 ]->[ -1 ]->[ 1 ]; #say STDERR "part b " . uDumper $part; #say STDERR "off $off, " . uDumper @rhs_cs; $part = [ map { [ $_->[ 0 ] + $off, $_->[ 1 ] + $off, $_->[ 2 ] ] } @$part ]; #say STDERR "part c " . uDumper( $part).", '$j'"; # part c [[33,55,\'latin1\']], \'user/u-áíáãêáóôéêÜ.p4s\' $rhs_cs[ $i ] = $part; } #say STDERR "rhs_cs '$rec->{rec}->{HAcfile}'" . uDumper( @rhs_cs).uDumper($vrec->{rec}->{VIvfile}); $rec->{ charsets }->{ HAcfile } = [ map { @{ $_ } } @rhs_cs ]; #say STDERR 'rhs_cs1 ' . uDumper $rec->{ charsets }->{ HAcfile }; $rec->{ charsets }->{ HAdfile } = $cs; map { $css{ $_->[ 2 ] } = 1 } map { @{ $rec->{ charsets }->{ $_ } } } keys %{ $rec->{ charsets } }; $rec->{ charset } = [ keys %css ]->[ 0 ] if 1 == keys %css; $recs->{ $rec } = $rec; last; } if( $tab eq 'db.archmap' ) { # strip '*' and '...', leaving a partial key. my $dcol = $rec->{ rec }->{ $rec_col } =~ s/(?:\*|\Q...\E)$//gr; my $blen = bytes_length $dcol; dbscan $df2c_dbh, $dcol, sub { # todo: why isn't the key always the first or second match? return 0 unless 0 == index $_[ 1 ], $dcol; # say STDERR "df2c archmap sub match $rec_col '$dcol': k/v: " . uDumper($_[1]).uDumper($_[0]); $cs = [ grep { $$_[ 1 ] <= $blen } @{ $_[ 0 ] } ]; $cs = [ $_[ 0 ]->[ 0 ] ] unless @$cs; 1 }; $rec->{ SKIP } = 'db.archmap no dfile', say( $skipped_records uDumper $rec ), last if ! $cs; $cs->[ -1 ]->[ 1 ] = bytes_length $rec->{ rec }->{ $rec_col }; } if( $tab eq 'db.ixtext' ) { # next unless $rec->{ charsets }->{ $src_col }; # todo: should put jobs first since most will be jobs not domains? my $s = $recs->{ { table => 'db.domain', op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ 'db.domain' }, DOname => $rec->{ rec }->{ ITvalue } } }; goto JOB unless $s; $rec->{ charsets }->{ ITvalue } = $s->{ charsets }->{ DOname } if $s->{ charsets }->{ DOname }; $rec->{ charsets }->{ ITword } = $s->{ charsets }->{ DOowner } if $s->{ charsets }->{ DOowner }; goto DONE; JOB: $s = $recs->{ { table => 'db.bodtext', op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ 'db.bodtext' }, BTkey => $rec->{ rec }->{ ITvalue }, BTattr => $rec->{ rec }->{ ITattr } } }; # todo: need to tokenize, or is this enough to get the right match? { # Keywords are lower-cased. use bytes; $s->{ rec }->{ BTtext } =~ /\Q$rec->{ rec }->{ ITword }\E/i; } my $off = $-[ 0 ]; my $blen = bytes_length $rec->{ rec }->{ ITword }; $rec->{ charsets }->{ ITvalue } = $s->{ charsets }->{ BTkey } if $s->{ charsets }->{ BTkey }; my ( $vra, $vrb ) = ( $off, $off + $blen ); $rec->{ charsets }->{ ITword } = [ grep { $vra < $_->[ 1 ] && $_->[ 0 ] < $vrb } @{ $s->{ charsets }->{ BTtext } } ] if $s->{ charsets }->{ BTtext }; $rec->{ charsets }->{ ITword }->[ 0 ]->[ 0 ] = 0 if $s->{ charsets }->{ BTtext }; $rec->{ charsets }->{ ITword }->[ -1 ]->[ 1 ] = $blen if $s->{ charsets }->{ BTtext }; DONE: map { $css{ $_->[ 2 ] } = 1 } map { @{ $rec->{ charsets }->{ $_ } } } keys %{ $rec->{ charsets } }; $rec->{ charset } = [ keys %css ]->[ 0 ] if 1 == keys %css; $recs->{ $rec } = $rec; last; } # xxx todo: this is wrong for ranges and client mapping # //domain/file -> domain/file since the slashes are accounted for below. $cs = [ [ 0, -2 + bytes_length $col, $df2c{ $rec->{ rec }->{ RSffile } }->[ 0 ]->[ 2 ] ] ] if ( $tab eq 'db.resolve' || $tab eq 'db.resolvex' ) && $rec_col eq 'RStfile'; $cs = [ map { [ $_->[ 0 ], -2 + $_->[ 1 ], $_->[ 2 ] ] } @$cs ] if $tab eq 'db.revsx' && $revsxtype eq 'stream' || $tab eq 'db.revbx'; # todo: should only apply the grep to the first element. $cs = [ grep { $_->[ 1 ] - $_->[ 0 ] } @$cs ] if $tab eq 'db.revbx'; $cs = [ map { [ -2 + $_->[ 0 ], $_->[ 1 ], $_->[ 2 ] ] } @$cs ] if $tab eq 'db.revbx' && $cs->[ 0 ]->[ 1 ] != 0; if( $tab eq 'db.label' && $rec_col eq 'LAfile' && $rec->{ rec }->{ LAfile } =~ m,^//([^/]+), && $depotTypes{ $1 } eq 'spec' ) { # todo: wrong for ranges. my $r; dbscan( $dbh, { table => 'db.revsx', op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ 'db.revsx' }, REdfile => $rec->{ rec }->{ LAfile } }, sub { $r = $_[ 0 ]; 1 } ), $cs = $r->{ charsets }->{ REdfile }; # todo: wrong for ranges. $cs->[ -1 ]->[ 1 ] = bytes_length $rec->{ rec }->{ LAfile }; } $cs = dclone $cs if ref $cs; # xxx todo RSffile was being linked to another of its columns if( $tab eq 'db.revux' || $tab eq 'db.revsx' ) { # //domain0/[client/1.1/]domain1.ckp -> domain1/1.1/ my ( $extra ) = $rec->{ rec }->{ $rec_col } =~ m,^//[^/]+/(.*/),; ( $extra ) = $rec->{ rec }->{ $rec_col } =~ m,^//[^/]+/([^/]+/), if $tab eq 'db.revsx' && $rec_col eq 'REdfile'; $extra //= ''; my $blen = bytes_length $extra; # //unload/client/culz.ckp col culz # //unload/client/1,d/culz.ckp col cul # //unload/stream/13,d/%2F%2Fstream%2Fstream-ul.ckp col //stream/stream-ul # //unload/stream/%2F%2Fstream-%2Fstream-ul.ckp col //stream/stream-ul # todo: need to separately handle (charset) the percent-encoded bits of //stream? # todo: this isn't right for charsets without an ASCII subset (cp1047 etc) # todo: this isn't right for multiple ranges? # //stream-áíáãêáóôéêÜ/main-áíáãêáóôéêÜ # //spec-áíáãêáóôéêÜ/stream/38,d/stream-áíáãêáóôéêÜ%2Fmain-áíáãêáóôéêÜ.p4s\ # account for the extra bytes in a percent-encoded stream. $cs = [ map { [ $_->[ 0 ], $_->[ 1 ] + 4, $_->[ 2 ] ] } @$cs ] if $extra =~ /stream/ && $rec_col eq 'REafile'; $cs = [ map { [ $_->[ 0 ], $_->[ 1 ] + 6, $_->[ 2 ] ] } @$cs ] if $extra =~ /stream/ && $rec_col eq 'REdfile' && $tab eq 'db.revux'; $cs = [ map { [ $_->[ 0 ], $_->[ 1 ] + 4, $_->[ 2 ] ] } @$cs ] if $extra =~ /stream/ && $rec_col eq 'REafile' && $tab eq 'db.revux'; $cs = $g2c{ $col } if $tab eq 'db.revsx' && $revsxtype eq 'group'; # Add in the extra. Shifts right. $cs = [ [ 0, $blen, 'latin1' ], map { [ $blen + $_->[ 0 ], $blen + $_->[ 1 ], $_->[ 2 ] ] } @$cs ] if $blen && $cs && $tab eq 'db.revsx' || $tab eq 'db.revux'; # todo: need to look up the stream depot as another domain? No, already assigned in db.domain. # //spec/protect.p4s, no $extra. # todo: encoding for the extension? my $ext = '.ckp'; $cs = [ [ 0, bytes_length( $rec->{ rec }->{ $rec_col } ) - ( 2 + bytes_length( $revdomain ) + 1 ) - bytes_length $ext, 'latin1' ] ] if $tab eq 'db.revsx' && ! $cs; # todo: use custom extension for spec files push @$cs, [ $cs->[ -1 ]->[ 1 ], $cs->[ -1 ]->[ 1 ] + bytes_length $ext, 'latin1' ]; # if grep { $cs->[ $_ - 1 ]->[ 1 ] != $cs->[ $_ ]->[ 0 ] } 1 .. @$cs - 1; } if( $revdomain ) { my $rdcs; my $blen = bytes_length $revdomain; if( $tab ne 'db.resolvex' ) { my $src_domain_rec = $recs->{ { table => 'db.domain', op => 'pv', version => $schema->{ SVR_RELTAB_MAP }->{ $ckp_ver }->{ 'db.domain' }, 'DOname' => $revdomain } }; $rdcs = $src_domain_rec->{ charsets }->{ DOname } if $src_domain_rec; } $rdcs = [ [ 0, $blen, 'latin1' ] ] unless $rdcs; # db.resolvex //123/ or ASCII domain. # Correct the offsets: # //$revdomain = 2 + $blen my $docs = [ [ 0, 2, 'latin1' ], map { [ 2 + $_->[ 0 ], 2 + $_->[ 1 ], $_->[ 2 ] ] } @$rdcs ]; # '/' push @$docs, [ 2 + $blen, 2 + $blen + 1, 'latin1' ]; # todo: 0 starts ahead of the leading //. ok for all charsets? # //$revdomain/ -> $blen + 3 # Account for the // tacked onto paths in the first revdomain block. my $sl = ( $tab eq 'db.revsx' && $rec_col eq 'REafile' && $revsxtype eq 'stream' || $tab eq 'db.revux' && $rec_col eq 'REafile' && $revsxtype eq 'stream' ) ? 2 : 0; #xxx id the stream type better my @revcs = map { [ 2 - $sl + $blen + 1 + $_->[ 0 ], 2 - $sl + $blen + 1 + $_->[ 1 ], $_->[ 2 ] ] } @$cs; $revcs[ 0 ]->[ 0 ] = $docs->[ -1 ]->[ 1 ]; $cs = [ @$docs, @revcs ]; # map { my $c = $rec->{ rec }->{ $rec_col };say STDERR "$_->[ 0 ], $_->[ 1 ]: " . substr $c, $_->[ 0 ], $_->[ 1 ] } @$cs if $tab eq 'db.revsx'; } $rec->{ SKIP } = 'Unable to locate charset for secondary table ' . "$rec->{ rec }->{ table }, rec_col '$rec_col' ('$rec->{rec}->{$rec_col}') -> src_col '$src_col', col '$col', revdom '" . ( $revdomain // 'undef' ) . "', cs " . uDumper( $cs ) . ', rec ' . uDumper( $rec ), say( $skipped_records uDumper $rec ), last if ! $cs || $cs && grep { ! $_->[ 2 ] } @$cs; $rec->{ charsets }->{ $rec_col } = $cs; $css{ $_->[ 2 ] } = 1 for @$cs; } { # todo: make this into a function to dedupe from the transcoding loop's copy. my $cs = $rec->{ charsets }; foreach my $col ( sort keys %$cs ) { die "Charset range not contiguous or increasing for column '$col' " . "'$rec->{ rec }->{ $col }' " . uDumper $rec if grep { $cs->{ $col }->[ $_ - 1 ]->[ 1 ] != $cs->{ $col }->[ $_ ]->[ 0 ] || $cs->{ $col }->[ $_ ]->[ 0 ] == $cs->{ $col }->[ $_ ]->[ 1 ] } 1 .. @{ $cs->{ $col } } - 1; my $a = bytes_length $rec->{ rec }->{ $col }; my $b = sum map { $_->[ 1 ] - $_->[ 0 ] } @{ $cs->{ $col } }; die "Charset range mismatch for column '$col' '$rec->{ rec }->{ $col }', " . "blen $a, ranges $b! " . uDumper $rec if $a != $b; } } # todo: collapse iso-8859-1, latin1, utf8. # {REafile => [[0,2,\'iso-8859-1\'],[2,21,\'iso-8859-1\'],[21,244,\'utf-8\']],REdfile => [[0,2,\'iso-8859-1\'],[2,21,\'iso-8859-1\'],[21,244,\'utf-8\']]} $rec->{ charset } = [ keys %css ]->[ 0 ] if 1 == keys %css; say $integ_check uDumper $rec if $needs_integ_check; # todo: coalese charsets before storage. $recs->{ $rec } = $rec; } } } status_end; $skipped_records->close; # todo: make sure this catches the integ skip and the rest. -s( $skiprec_name ) ? WARN "Records were skipped. Inspect '$skiprec_name'." : unlink $skiprec_name; my $crod_dir; my $crod_dir_o = $cfgs{ "any\0client.readonly.dir" }; $crod_dir_o = File::Spec->catdir( $sd_root // $droot, $crod_dir_o ) if $crod_dir_o && ! File::Spec->file_name_is_absolute( $crod_dir_o ); my $crod_uni; $crod_uni = $crod_dir_o =~ /[^\p{ASCII}]/ if $crod_dir_o; $crod_dir = $crod_dir_o unless $crod_uni; my $part_file = sub { use bytes; use integer; # For modulo op. # todo: account for 1469862? $crod_dir . '/server.dbs/client/' . ( ( reduce { $a + $b } map { unpack 'c' } $_[ 0 ] =~ /(.)/g ) % 999 ) . "/db.$_[0]"; }; # todo: have an env_report for this. # todo: record each script and its result in the main log. my $scr_preamble = scr_preamble_str; # Even if this file gets huge, it should be ok since Perl stops # reading the file once it hits the DATA section. # todo: handle MISSING archive files # todo: add progress to this # todo: tiny.db. also, make sure if verifies clean and doesn't have a different server.depot.root set # todo: rewrite +k checksums # todo: add to a log file # todo: +T and spec depot files, unload, archive # todo: write a post-rename verify script (add flag to ignore lazy copies?) # todo: include identifying info in a comment in case the file is copied/renamed. # todo: make rename scripts for client-side files? # 'p4 verify' on windows creates lbr dirs, and trying to mv/rename when that happens causes an error, # so we have to do file by file if dst exists. # # todo: track if lbr is a file or dir and bail if dst file exists? # todo: this only works for partitioned clients because the destination # won't already exist and there's only one (db.have) file in the directory. # todo: have a 'nothing to do' message. # todo: have a --print-files arg to pipe the output to something else # todo: capture windows-specific error too on mv failure? # todo: is make_path's error being thrown out? my $arch_mv_scr = $scr_preamble . << ' EOF'; use File::Basename; use File::Copy qw / copy move mv cp /; use File::Path 'make_path'; exit say 'Already completed, may not run again.' if -e $sf; while( ! eof DATA && eval '$_ = '. ) { my ( $src, $dst ) = @$_; my $skip_msg = "($rec_no) Source '$src' does not exist, skipping."; say $skip_msg and say $lfh $skip_msg and next unless -e $src; my $dst_path = dirname $dst; make_path $dst_path, { error => \ my $err } unless -d $dst_path; mv $src, $dst or die "($rec_no) Couldn't rename src '$src' to dst '$dst':" . " \$! '$!', \$^E '$^E'"; } continue { $rec_no++ } say $lfh "\nCompleted in ${\ ( scalar( localtime ) - $start )->pretty }"; IO::File->new( $sf, '>' ); 0 __DATA__ EOF ; $arch_mv_scr =~ s/^ //gm; # todo: log the temp names. my $unld_mv_scr = $scr_preamble . << ' EOF'; use File::Basename; use File::Copy qw / cp mv /; use File::Temp; exit say 'Already completed, may not run again.' if -e $sf; while( ! eof DATA && eval '$_ = '. ) { my ( $src, $afile ) = @$_; $src = "$RealBin/.internal/unloaded/$src"; die "($rec_no) Source '$src' does not exist! (afile '$afile')" unless -e $src; # Swap the files, keeping the original until the new one is safely in place. my $afile_out = File::Temp::tempnam dirname( $afile ), 'jnltool-tmp-orig-archive-XXXXX'; mv $afile, $afile_out or die "Couldn't rename '$afile' to '$afile_out': $!"; cp $src, $afile or die "Couldn't copy src '$src' to dst '$afile': \$! '$!', \$^E '$^E'"; unlink $afile_out; } continue { $rec_no++ } say $lfh "\nCompleted in ${\ ( scalar( localtime ) - $start )->pretty }"; IO::File->new( $sf, '>' ); 0 __DATA__ EOF ; $unld_mv_scr =~ s/^ //gm; # todo: add to a log file and error check # todo: uDumper these so they can be transferred as ASCII? # todo: use p4 -x - verify? # todo: have to verify all +k files? use a separate script for the full list? # todo: test the stderr regex # Doesn't check if it's been run or not before since there's no harm. my $arch_kv_scr = $scr_preamble . << ' EOF'; use IPC::Cmd qw / can_run run /; use Data::Dumper; die "Can't find 'p4' program.\n" unless can_run 'p4'; $ENV{ P4CHARSET } = `p4 -ztag info 2>&1` =~ /^\.\.\. unicode enabled/m ? 'utf8' : 'none'; die "Must have at least 'admin' level access to run 'p4 verify'.\n" if `p4 protects -m 2>&1` !~ /^(?:admin|super)$|table is empty/; my $out = ''; chomp, run( command => [ qw / p4 -q verify -v -q /, $_ ], buffer => \ $out ), $out !~ m,^//.* BAD!, ? print $out : 0 while ; say $lfh "\nCompleted in ${\ ( scalar( localtime ) - $start )->pretty }"; IO::File->new( $sf, '>' ); __DATA__ EOF ; $arch_kv_scr =~ s/^ //gm; # todo: have various result files be saved outside of the working jnltool-uniconv # directory. e.g. checkpoint.2-jnltool-uniconv-archive_renamer.pl # todo: clean up empty directories in old encoding tree # todo: use tempfile here set to autodelete? how to mark crash/not success # todo: first check that the output file doesn't exist. my $conv_out_fh = !$check && !$gui ? IO::File->new( $conv_out, '>:raw' ) : 0; my $arch_mv_db = $fname_int->( 'archive_rename.db' ); my $arch_mva_db = $fname_int->( 'archive_rename-archive.db' ); my $unload_v_db = $fname_int->( 'unloaded_verify.db' ); # todo: win lfn my %scripts = map { $_ => $fname->( $_ ) } qw / archive_renamer.pl archive_renamer-archive.pl archive_renamer-depot.pl archive_verify-k.pl archive_verify-kT.pl archive_trigger_files.pl client_rodir_renamer.pl unload_verify.pl unload_renamer.pl /; my $arch_mv = IO::File->new( $scripts{ 'archive_renamer.pl' }, '>:raw' ); my $arch_mva = IO::File->new( $scripts{ 'archive_renamer-archive.pl' }, '>:raw' ); my $arch_dp = IO::File->new( $scripts{ 'archive_renamer-depot.pl' }, '>:raw' ); my $unld_mv = IO::File->new( $scripts{ 'unload_renamer.pl' }, '>:raw' ); # todo: does this miss files without non-ASCII names that have Unicode expansions like the user? my $arch_kv = IO::File->new( $scripts{ 'archive_verify-k.pl' }, '>:raw' ); my $arch_kvT = IO::File->new( $scripts{ 'archive_verify-kT.pl' }, '>:raw' ); my $unld_vU = IO::File->new( $scripts{ 'unload_verify.pl' }, '>:raw' ); my $arch_script = IO::File->new( $scripts{ 'archive_trigger_files.pl' }, '>:raw' ); my $crodir_mv = IO::File->new( $scripts{ 'client_rodir_renamer.pl' }, '>:raw' ); chmod 0500, values %scripts; # Make them all executable. my $jnl_out = $out_type eq 'jnl'; my $archmv_db_cmp = sub { use bytes; my ( $l0, $l1 ) = map length, @_; ( $l0 == $l1 ) ? ( $_[ 0 ] cmp $_[ 1 ] ) : ( $l0 < $l1 ) ? -1 : 1 }; $DB_BTREE->{ compare } = $archmv_db_cmp; my ( $armv_dbh, $armv_db ) = mk_uniconv_db $arch_mv_db; my ( $armva_dbh, $armva_db ) = mk_uniconv_db $arch_mva_db; delete $DB_BTREE->{ compare }; # todo: need to keep the full lazy list for windows? $armv_dbh ->filter_store_key( $armv_fsk ); $armva_dbh->filter_store_key( $armv_fsk ); $armv_dbh ->filter_store_value( $armv_fsv ); $armva_dbh->filter_store_value( $armv_fsv ); print $arch_mv $arch_mv_scr; print $arch_mva $arch_mv_scr; print $unld_mv $unld_mv_scr; print $arch_dp $arch_mv_scr; print $arch_kv $arch_kv_scr; print $arch_kvT $arch_kv_scr; print $unld_vU $arch_kv_scr =~ s/verify -v -q/verify -v -q -U/gr; print $crodir_mv $arch_mv_scr; my ( $tdb_dbh, $tdb_db ) = mk_uniconv_db $fname_int->( 'tiny-conv.db' ); $tdb_dbh->filter_store_key( $armv_fsk ); $tdb_dbh ->filter_store_value( $armv_fsv ); my ( $udb_dbh, $udb_db ) = mk_uniconv_db $fname_int->( 'unique-check.db' ); # todo: open records.db in RO mode now. # todo: test that db.change field length is correctly 31 # todo: check that sdroot isn't the same as droot say $arch_dp uDumper [ $droot, $droot_utf8 ] if $droot ne $droot_utf8; $droot = $droot_utf8; my %depotTrans = map { ( $needs_case_copy ? bytes_lc $_ : $_ ), $_ } keys %depotMaps; status_start 'transcoding'; $recs_processed = 0; foreach my $tab ( map { $$_[ 0 ] } @tab_locs_order ) { my $rev = $tab eq 'db.rev' || $tab eq 'db.revux' || $tab eq 'db.revsx' || $tab eq 'db.revsh'; my $revux = $tab eq 'db.revux'; my $rev_archive = $tab eq 'db.revbx'; my $depot = $tab eq 'db.depot'; my $domain = $tab eq 'db.domain'; my $cfg = $tab eq 'db.config'; # Assumes that keys in the $recs_name db are sorted the same as the P4D dbs # and that they ascend in the same order they're stored in the checkpoint. my $pg = pager $dbh, $tab, 10_000; while( my $rs = $pg->() ) { foreach my $rec ( @$rs ) { status $recs_processed, $record_count if $recs_processed++ % POSIX::ceil( $record_count / 10_000 ) == 0; next if $rec->{ SKIP }; die "transcoder: record does not have charset data! " . uDumper $rec unless $rec->{ charsets }; my $orec; $orec->{ rec } = $rec->{ rec }, $rec->{ rec } = $rec->{ case_rec } if $needs_case_copy; my $had_cs = $rec->{ charset }; my %css; %css = map { map { $_->[ 2 ], 1 } @{ $rec->{ charsets }->{ $_ } } } keys %{ $rec->{ charsets } } unless $had_cs; $rec->{ charset } = [ keys %css ]->[ 0 ] if ! $had_cs && 1 == keys %css; next if $rec->{ charset } && $rec->{ charset } =~ /^(?:utf-8|utf-8-strict|utf8)$/i; # utf8 UTF-8 utf-8-strict $rec->{ rec }->{ op } = 'dv'; print $conv_out_fh to_jnl_fmt $rec->{ rec } if $jnl_out && !( $rec->{ SRC } && $rec->{ SRC } eq 'UNLOAD' ); my ( $odfile_o, $oafile_o ); ( $odfile_o, $oafile_o ) = ( $orec->{ rec }->{ REdfile }, $orec->{ rec }->{ REafile } ) if $rev || $rev_archive;# && ! $rec->{ rec }->{ REalazy }; my ( $odfile, $oafile ); ( $odfile, $oafile ) = ( $rec->{ rec }->{ REdfile }, $rec->{ rec }->{ REafile } ) if $rev || $rev_archive;# && ! $rec->{ rec }->{ REalazy }; my $odomain; $odomain = $rec->{ rec }->{ DOname } if $domain; my $odepotmap; $odepotmap = $rec->{ rec }->{ DPmap } if $depot; my $odepot; $odepot = $rec->{ rec }->{ DPname } if $depot; my $cs = $rec->{ charsets }; # todo: move this check into the key-save db routine? foreach my $col ( sort keys %$cs ) { die "Charset range not contiguous or increasing for column '$col' " . "'$rec->{ rec }->{ $col }' " . uDumper $rec if grep { $cs->{ $col }->[ $_ - 1 ]->[ 1 ] != $cs->{ $col }->[ $_ ]->[ 0 ] || $cs->{ $col }->[ $_ ]->[ 0 ] == $cs->{ $col }->[ $_ ]->[ 1 ] } 1 .. @{ $cs->{ $col } } - 1; my $a = bytes_length $rec->{ rec }->{ $col }; my $b = sum map { $_->[ 1 ] - $_->[ 0 ] } @{ $cs->{ $col } }; die "Charset range mismatch for column '$col' '$rec->{ rec }->{ $col }', " . "blen $a, ranges $b! " . uDumper $rec if $a != $b; use bytes; # todo: for substr... necessary? eval { # say STDERR "strt: '$rec->{ rec }->{ $col }': " . Dumper $cs->{ $col } if $tab eq 'db.revsx'; $rec->{ rec }->{ $col } = join '', map { my $cc = $rec->{ rec }->{ $col }; # xxx todo: why does this need to be copied? my $ss = # say STDERR "ss $_->[ 2 ]: $_->[ 0 ], $_->[ 1 ]: '" . substr( $cc, $_->[ 0 ], $_->[ 1 ] - $_->[ 0 ] ). "'" if $tab eq 'db.revsx'; # todo: use $num_octets = utf8::upgrade($string) ? utf8::encode($string)? encode 'UTF-8', decode( $_->[ 2 ], substr( $cc, $_->[ 0 ], $_->[ 1 ] - $_->[ 0 ] ), Encode::FB_CROAK ), Encode::FB_CROAK } @{ $cs->{ $col } }; # say STDERR decode 'UTF-8', "done: '$rec->{ rec }->{ $col }'\n" if $tab eq 'db.revsx'; }; local $Data::Dumper::Sortkeys = 1; die "encode/decode error col '$col': $@, " . uDumper $rec if $@; } if( $depot ) { $depotMaps{ $rec->{ rec }->{ DPname } } = $rec->{ rec }->{ DPmap } =~ s,\Q/...,,gr; $depotMaps{ bytes_lc $rec->{ rec }->{ DPname } } = $depotMaps{ $rec->{ rec }->{ DPname } } if $needs_case_copy; $depotTrans{ $odepot } = $rec->{ rec }->{ DPname }; $depotTrans{ bytes_lc $odepot } = $depotTrans{ $odepot } if $needs_case_copy; } say $arch_script "# $odfile $rec->{ rec }->{ REdfile }" if $rev && ! $rec->{ rec }->{ REalazy } && $rec->{ rec }->{ REatype } & 0x00008; my $type = $rec->{ rec }->{ REatype }; # Collapse duplicates. # todo: don't do unloaded/archived/erased files my $arch_ren_a = $rev && ! $rec->{ rec }->{ REalazy } && ( ( $type & 0x0000F ) == 0x00000 || # RCS ( $type & 0x0000F ) == 0x00001 || # binary ( $type & 0x0000F ) == 0x00003 || # compressed ( $type & 0x0000F ) == 0x00007 ) && # uncompressed $rec->{ rec }->{ REaction } != 9; my $arch_ren = $arch_ren_a && $oafile ne $rec->{ rec }->{ REafile }; my $archa_ren = $rev_archive && $oafile ne $rec->{ rec }->{ REafile }; if( $arch_ren || $archa_ren ) { my ( $os, $s ) = ( $oafile, $rec->{ rec }->{ REafile } ); my $db = $archa_ren ? $armva_db : $armv_db; my @odirs = split m,(?{ "$src\0$t" } = { src => $src, dst => $dst, type => $t } } } # todo: inspect file content and skip ones without keywords containing Unicode? say $arch_kv $rec->{ rec }->{ REafile } if $arch_ren_a && ( $type & 0x00010 || $type & 0x00020 || $type & 0x00030 ); say $arch_kvT $rec->{ rec }->{ REafile } if $rev && ! $rec->{ rec }->{ REalazy } && ( $type & 0x0000F ) == 0x00002 && ( $type & 0x00010 || $type & 0x00020 || $type & 0x00030 ) && $rec->{ rec }->{ REaction } != 9 && $oafile ne $rec->{ rec }->{ REafile }; $tdb_db->{ $oafile =~ s,^//[^/]+/,,r } = $rec->{ rec }->{ REafile } =~ s,^//[^/]+/,,r if $rev && ! $rec->{ rec }->{ REalazy } && ( $type & 0x0000F ) == 0x00002 && $rec->{ rec }->{ REaction } != 9 && $oafile ne $rec->{ rec }->{ REafile }; # Partitioned and volatile clients. # todo: depot map change? if( $domain && ( $rec->{ rec }->{ DOoptions } & 0x010000 || ( $rec->{ rec }->{ DOoptions } & 0x110000 ) == 0x110000 ) ) { say $crodir_mv uDumper [ $part_file->( $odomain ), $part_file->( $rec->{ rec }->{ DOname } ) ] } $crod_dir = $rec->{ rec }->{ CFvalue } if $cfg && $rec->{ rec }->{ CFname } eq 'client.readonly.dir' && $crod_dir_o ne $rec->{ rec }->{ CFvalue }; $crod_dir = File::Spec->catdir( $sd_root // $droot, $crod_dir ) if $cfg && $crod_dir && ! File::Spec->file_name_is_absolute( $crod_dir ); $sd_root = $rec->{ rec }->{ CFvalue } if $cfg && $rec->{ rec }->{ CFname } eq 'server.depot.root' && $sd_root_o ne $rec->{ rec }->{ CFvalue }; state $adpcrod; my $crod_dir_o8 = $cfgs{ "any\0client.readonly.dir" }; $crod_dir_o8 = File::Spec->catdir( $sd_root // $droot, $crod_dir_o8 ) if $cfg && $crod_dir && ! File::Spec->file_name_is_absolute( $crod_dir_o8 ); $adpcrod = 1, say $arch_dp uDumper [ $crod_dir_o8, $crod_dir ] if $crod_dir && ! $adpcrod && $crod_dir_o ne $crod_dir; state $adpsdro; # todo: check that sdroot isn't the same as droot $adpsdro = 1, say $arch_dp uDumper [ $sd_root_o, $sd_root ] if $sd_root && ! $adpsdro && $sd_root ne $sd_root_o; $odepotmap =~ s,\Q/...\E$,,g if $odepotmap; my $depotmap = $rec->{ rec }->{ DPmap }; $depotmap =~ s,\Q/...\E$,,g if $depotmap; # todo: include depot_root conversion here. # todo: have an archive depot version of this as well? # todo: just put this in the archive renamer - no need for it to be separate. say $arch_dp uDumper [ ( File::Spec->file_name_is_absolute( $odepotmap ) ? $odepotmap : File::Spec->catdir( ( $sd_root // $droot ), $odepotmap ) ) =~ s,\Q/...,,gr, ( File::Spec->file_name_is_absolute( $depotmap ) ? $depotmap : File::Spec->catdir( ( $sd_root // $droot ), $depotmap ) ) ] if $depot && $odepotmap ne $depotmap; if( $revux ) { print { IO::File->new( "$uldir/" . md5_hex( $odfile ) . '.dfile.UTF-8' , '>:raw' ) } $rec->{ rec }->{ REdfile }; print { IO::File->new( "$uldir/" . md5_hex( $odfile ) . '.afile.UTF-8' , '>:raw' ) } $rec->{ rec }->{ REafile }; } $rec->{ rec }->{ op } = 'pv'; # todo: does this handles case-insensitivity right? exists( $udb_db->{ $rec } ) ? die "Two records translate to one!\n\n" . uDumper( $udb_db->{ $rec } ) . "\n" . uDumper( $rec ) : ( $udb_db->{ $rec } = $rec ); if( $rec->{ SRC } && $rec->{ SRC } eq 'UNLOAD' ) { my $md5 = md5_hex $rec->{ SRC_FILE }; $ulfn = $md5, $ulfh = IO::File->new( "$uldir/$md5" , '>>:raw' ) if $ulfn ne $md5; store_fd $rec, $ulfh; next } print $conv_out_fh to_jnl_fmt $rec->{ rec } if $jnl_out; } } } status_end; $ulfh->close if $ulfh; $conv_out_fh->close; undef $udb_dbh; untie %$udb_db; unlink $fname_int->( 'unique-check.db' ); # todo: close all open fh's and unlink empty ones (like archive renamer) # Archive rename must look at depot map for paths. Also server.root # todo: ensure file metadata is copied. dates, perms etc. my $amvsub = sub { my $fh = $_[ 0 ]; sub { local $Data::Dumper::Terse = 1; local $Data::Dumper::Indent = 0; say $fh Dumper [ $arch_file->( $_[ 0 ]->{ src }, $_[ 0 ]->{ type } ), $arch_file->( $_[ 0 ]->{ dst }, $_[ 0 ]->{ type } ) ]; 0 } }; dbscan $armv_dbh , undef, $amvsub->( $arch_mv ); dbscan $armva_dbh, undef, $amvsub->( $arch_mva ); my $tdb_jnl = IO::File->new( $fname->( 'tiny.db.out.jnl' ), '>:raw' ); my $tdbl = $fname_int->( 'tiny.ckp.location' ); $tdb_dbh->sync; status_run 'scanning tiny.db archives', sub { my $tname = slurp $tdbl; my $tname_fh = open_ckp $tname; my $tname_sz = -s $tname; my $trecs = 0; parse $tname_fh, sub { status tell( $tname_fh ), $tname_sz if $trecs++ % 10_000 == 0; my ( $rec, $dfile, $depot ) = $_[ 0 ]; return 0 unless $rec->{ TIkey } =~ /[^\p{ASCII}]/; # job087326 makes TIkey ambiguous, so refuse anything but what we know for sure. say( $skipped_files uDumper [ 'no lookup, no sdroot prefix', $rec->{ TIkey }, $sd_root_o ] ), return 0 if $sd_root && $rec->{ TIkey } !~ /^\Q$sd_root_o/i; $dfile = $sd_root ? $rec->{ TIkey } =~ s/^\Q$sd_root_o//ri : $rec->{ TIkey }; # todo: files could exist in deleted depots? # todo: cache the sorted depot map list my $dm; map { # todo: $sd_sep isn't necessarily what the depot map uses my $ldm = $depotMaps_o{ $_ }; $ldm = bytes_lc $ldm if $needs_case_copy; ( $depot, $dm ) = ( $_, $ldm ) if $dfile =~ m,^\Q$sd_sep\E*\Q$ldm\E,; } reverse sort { length( $depotMaps_o{ $a } ) <=> length( $depotMaps_o{ $b } ) } keys %depotMaps_o; say( $skipped_files uDumper [ 'no lookup, no depot lookup', $sd_sep, $rec->{ TIkey }, $dfile, join "\n", map { "$_: $depotMaps_o{ $_ }" } keys %depotMaps_o ] ), return 0 if ! $depot; $dfile =~ s/^\Q$sd_sep\E*\Q$dm\///; my @pieces = split '/', $dfile; my $rev = $pieces[ -1 ]; $dfile = join '/', @pieces[ 0 .. @pieces - 2 ]; die "no dfile: " . uDumper @pieces unless $dfile; # todo: lc $dfile? if( $dfile =~ /[^\p{ASCII}]/ ) { my $dfile_db = $tdb_db->{ $dfile }; say( $skipped_files uDumper [ 'no lookup, no dfile in db', $rec->{ TIkey }, $dfile, $depot ] ), return 0 unless $dfile_db; $dfile = $dfile_db; } # todo: depot name lookup ok here? needs translated one? my $d = $needs_case_copy ? bytes_lc $depot : $depot; my $dmm = $depotMaps{ $depotTrans{ $d } }; $dmm = bytes_lc $dmm if $needs_case_copy; my $key = "$dmm/$dfile/$rev"; $key = "$sd_root$sd_sep$key" if $sd_root; $rec->{ op } = 'dv'; print $tdb_jnl to_jnl_fmt $rec; $rec->{ op } = 'pv'; $rec->{ TIkey } = $key; print $tdb_jnl to_jnl_fmt $rec; 0 } } if -e $tdbl; status_run 'rewriting unloaded files', sub { my $ul_dh = DirHandle->new( $uldir ); my @files; push @files, $_ while defined( $_ = $ul_dh->read ); @files = sort map { "$uldir/$_" } grep { !/\.[ad]file|\.rec|\.orec|^\.{1,2}$/ } @files; # todo: why doesn't this work? # my @files = grep { !/\.[ad]file|\.rec|\.orec/ } glob "$uldir/*"; # todo: make this parallel for( my $i = 0; $i < @files; $i++ ) { status $i, scalar @files if $i % 100 == 0; my $ulf = $files[ $i ]; my $fh = IO::File->new( $ulf, '<:raw' ) or die "no ulf: $ulf: $!"; my $crec = fd_retrieve $fh; $fh->seek( 0, SEEK_SET ); my $afile = slurp "$ulf.afile"; say $unld_vU IO::File->new( "$ulf.dfile.UTF-8", '<' )->getline; my $cmp = $afile =~ /\.gz$/; my ( $ckp, $ckp_name ) = $cmp ? File::Temp::tempfile : ( IO::File->new( $afile, '<:raw' ), $afile ); gunzip( $afile, $ckp ), $ckp->flush if $cmp; my $afile_dir = dirname $afile; # todo: error checking my ( $jnl_tmp, $jnl_tmp_name ) = ( IO::File->new( "$ulf.done", '>' ), "$ulf.done" ); # todo: use pigz, etc ( $jnl_tmp = new IO::Compress::Gzip $jnl_tmp_name, AutoClose => 1 or die "IO::Compress::Gzip failed: $IO::Compress::Gzip::GzipError" ) if $cmp; my @offs; # Write converted records. push( @offs, $crec->{ range } ), syswrite $jnl_tmp, to_jnl_fmt $crec->{ rec } while ! eof( $fh ) && ( $crec = fd_retrieve $fh ); #readline $ckp; #$offs[ 0 ][ 0 ] = tell $ckp if $offs[ 0 ][ 0 ] == 0; # Skip the initial nx # Write the unconverted ones. dump_range $ckp, $_, $jnl_tmp for @{ invert_ranges $ckp_name, [ sort { $$a[ 0 ] <=> $$b[ 0 ] } @offs ] }; my $orec = retrieve "$ulf.orec"; say $unld_mv uDumper [ basename( $jnl_tmp_name ), $arch_file->( slurp "$ulf.afile.UTF-8", $orec->{ REatype } ) . '/1.0' . ( $cmp ? '.gz' : '' ) ]; $ckp->close, unlink $ckp_name if $cmp; } }; $skipped_files->close; -s( $skipfiles_name ) ? WARN "Files were skipped. Inspect '$skipfiles_name'." : unlink $skipfiles_name; # coalese charset ranges, maybe down to a single charset for the entire ckp. as long as it's # one that has ASCII as a subset, conversion then becomes trival. from_to() # export_uniconv_db $recs_name; # $armv_dbh->sync; # export_uniconv_db $arch_mv_db; # $tdb_dbh->sync; # export_uniconv_db $fname_int->( 'tiny-conv.db' ); undef $dbh; untie %$recs; IO::File->new( $fname_int->( 'uniconv-complete' ), '>' ); status_heading "\nProcessing took " . ( scalar (localtime) - $start_time )->pretty; status_heading "\nConversion complete. See output: $conv_out"; 0 } sub iconv { my ( $from, $to, $mode, $buf ) = split '/', shift; ERR "USAGE: --iconv=from_charset/to_charset. See --show-encodings." unless $encs{ $from } && $encs{ $to }; $mode //= 'CROAK'; $mode = eval "Encode::FB_$mode"; ERR "Invalid mode. See 'man Encode', or use QUIET/WARN/CROAK." if $@; print encode $to, decode( $from, $buf, $mode ), $mode while sysread STDIN, $buf, $dump_buffsize; 0 } ################################################################################ sub demo { my $ckp = shift; sub indent { "\t". join "\n\t", map { split "\n", $_ } sort @_ } local $Data::Dumper::Indent = 3; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Terse = 1; my $filt_string = 'tables=db.user fields=USuser,USemail'; say << "EOT"; jnltool: stand-alone Perl checkpoint/journal processor with no external dependencies. Doesn't require a connection to a server. Demo: Users in group relengblessed: ${\ indent users_in_group 'relengblessed' } and with subgroup membership: ${\ indent users_in_group 'relengblessed', 1 } Report of the host environment: ${\ indent env_report } Can produce a raw dump of any table without scanning the entire checkpoint: ${\ indent dump_range_parsed 'db.trigger' }; Can find and parse arbitrary tables: ${\ indent Dumper shift @{ [ get_tab_recs 'db.trigger' ] } } Can filter and show partial records ('$filt_string'): ${\ indent map { "$$_[0]\t$$_[1]" } splice @{ filter_return( $filt_string ) }, 0, 5 } Can report progress for long operations: EOT ; $progress = 1; parse_test $ckp, $tab_locs{ 'db.revdx' }, 'ckp'; } sub test_assist { my $mode = shift; return status_run '...', sub { my @procs = status_agg par sub { sleep 60 }; usleep $status_child_wait while @procs = status_agg @procs } if $mode eq 'parallel'; sleep 60 } ################################################################################ sub validate_cluster_cfg { my ( $cfg, $cf, $es ) = ( shift, 'Config file' ); ERR "$cf couldn't be parsed." if not defined $cfg; ERR "Config must have a 'cluster' section." unless exists $cfg->{ cluster }; my $cname = $cfg->{ cluster }->{ 'cluster.id' }; ERR "Cluster must have a 'cluster.id'." unless $cname; # todo: check for empty groups/users/clients sections, duplicate defs, # typos in keys (like server types) my @c_svrs = map { keys %{ $cfg->{ cluster }{ $_ } } } grep { exists $cfg->{ cluster }->{ $cname }{ $_ } } keys %cluster_types; map { ERR "$cf server ID '$_' must not contain [\@#%*] wildcards or" . " nonprintable characters." if /\s+|\@|#|\%|\*|\p{IsCntrl}/ } @c_svrs; my %svrs = map { $_->{ SVid } => 1 } get_tab_recs 'db.server'; map { ERR "ServerID '$_' already exists in the checkpoint." if exists $svrs{ $_ } } @c_svrs; ERR 'path-translation section must not contain remote depots.' if grep { $_->{ DPtype } == 1 && $cfg->{ cluster }->{ 'path-translation' }->{ $_->{ DPname } } } get_tab_recs 'db.depot'; } # todo: allow on journals? sub validate_configurables { my @cs = get_tab_recs 'db.config'; my %reqs = ( ); for ( @cs ) { my ( $k, $v ) = ( $_->{ CFname }, $_->{ CFvalue } ); my $req = $reqs{ $k }; ERR "Disallowed configurable '$k=$v', expected '$req'." if defined $req and $v ne $req; } 0 } sub normalize_cluster_cfg { my $cfg = shift; $cfg->{ cluster }->{ 'preserve-config' } //= 'yes'; $cfg->{ cluster }->{ 'path-translation' } //= (); $cfg->{ 'external-servers' } //= (); foreach my $type ( keys %cluster_types ) { foreach my $svr_id ( keys %{ $cfg->{ cluster }->{ $type } } ) { for( qw / groups groups-wild users users-wild clients clients-wild / ) { $cfg->{ cluster }->{ $type }->{ $svr_id }->{ $_ } //= []; } } } } # workspace -> serverID sub expand_cdmaps { my ( $map, $cbu, $do_progress ) = @_; %groups = xform_group_recs unless %groups; my ( %cmap, %emap ); # emap is expanded map, cmap is the result my ( $gtot, $gcnt ); my $fake = 'asgakj3aj3hk1q236727SHHx'; map { my $gwild_re = join( '|', @{ $map->{ $_ }->{ 'groups-wild' } } ) // $fake; $gtot += @{ $map->{ $_ }->{ groups } }, grep /$gwild_re/, keys %groups } keys %$map if $do_progress; # %emap = ( svr1 => ( c1 => ( gs => [ g1, g2 ], us => [], cs => [] ) ) ) foreach my $svr_id ( sort keys %$map ) { my $sid = $map->{ $svr_id }; my ( $gwild_re, $uwild_re, $cwild_re ) = map { join( '|', @{ $sid->{ "$_-wild" } } ) // $fake } qw / groups users clients /; #1 ------- Collect groups/users/clients for this server. my @gs = sort ( @{ $sid->{ groups } }, grep /$gwild_re/, keys %groups ); my @us = sort ( @{ $sid->{ users } }, grep /$uwild_re/, keys %$cbu ); my @cs = sort ( @{ $sid->{ clients } }, grep /$cwild_re/, map { @$_ } values %$cbu ); #2 ------- group -> [ us ] -> [ cs ] push @{ $emap{ $_ }{ gs } }, $svr_id for map { @{ $cbu->{ $_ } } } grep { exists $cbu->{ $_ } } map { status ++$gcnt, $gtot if $do_progress; users_in_group $_ } @gs; #3 ------- [ us ] -> [ cs ] push @{ $emap{ $_ }{ us } }, $svr_id for map { @{ $cbu->{ $_ } } } grep { exists $cbu->{ $_ } } @us; #4 ------- [ cs ] push @{ $emap{ $_ }{ cs } }, $svr_id for @cs; } my ( $emap_tot, $emap_cnt ) = scalar keys %emap; foreach my $c ( sort keys %emap ) { status $emap_cnt, $emap_tot if $do_progress and ++$emap_cnt % POSIX::ceil( $emap_tot / 100 ) == 0; my ( $gs, $us, $cs ) = @{ $emap{ $c } }{ qw / gs us cs / }; die "Unexpected multiple client assignment: $c" if exists $cmap{ $c }; # First one added wins. $cmap{ $c } = $$cs[ 0 ] // $$us[ 0 ] // $$gs[ 0 ]; } [ \ %cmap, \ %emap ] } # nx record sub ckp_header { seek $jnl, 0, SEEK_SET; my $nx = readline $jnl; $nx } # nx, vv records sub jnl_header { seek $jnl, 0, SEEK_SET; my $nx = readline $jnl; my $vv = readline $jnl; # The first journal doesn't have nx or vv. ( $nx =~ /^\@nx@ / ? $nx : '' ) . ( $vv =~ /^\@vv@ / ? $vv : '' ) } sub mk_cluster { my ( $tab_locs, $preview, $ckp_name, $cfg_file, $svr_id ) = @_; $cfg_file //= ( $from_ckp // $ckp_name ) . '.yaml'; my $data_dir = File::Spec->rel2abs( $output_dir // dirname $ckp_name ); my %cluster_tabs = %$tab_locs; # As of 1047418 user.rp is journaled now, but we expect a master ckp so we'll # continue to ignore it. my @delrps = qw / db.have.rp db.user.rp db.view.rp /; my @clientdata_tables = qw / db.have db.working db.resolve /; # ~~ db.view ERR "Must specify a --server-id." unless $svr_id; eval 'use CPAN::Meta::YAML'; ERR "Please install CPAN::Meta::YAML." if $@; ERR "Config file is missing or is not a file." unless -f $cfg_file; ERR "Config file must not be empty." unless 0 < -s $cfg_file; ERR "Data directory '$data_dir' is missing or not a directory." unless -d $data_dir; my $yaml = CPAN::Meta::YAML->read( $cfg_file ); my $cfg = $yaml->[ 0 ]; validate_cluster_cfg $cfg if is_ckp; validate_configurables if is_ckp; # todo: also for journal? my %svr_to_type = map { my $t = $_; map { $_ => $t } keys %{ $cfg->{ cluster }->{ $t } } } %cluster_types; ERR "Server ID not in config." unless exists $svr_to_type{ $svr_id }; normalize_cluster_cfg $cfg; # $svr_id => ( groups => [], clients => [], users => [] ) my %cdefs = map { $_ => $cfg->{ cluster }->{ $svr_to_type{ $_ } }->{ $_ } } keys %svr_to_type; my $cluster_type = $svr_to_type{ $svr_id }; my $wksp_svr = $cluster_type eq 'workspace-server'; # my $output_fh = *STDOUT; # binmode $output_fh; my $is_gz = sub { $_[ 0 ] =~ /\.gz$/ }; my $outfile_name = sub { my ( $name, $no_default, $gzip ) = @_; # todo: rename ckp_* to out_* my $ckp_basename = basename $from_ckp // $ckp_name; $name = $cfg->{ cluster }->{ output }->{ $name } // ( $data_dir ? File::Spec->catfile( $data_dir, "$ckp_basename-$name" ) : "$ckp_name-$name" ) unless $no_default; $name = $gzip ? !$is_gz->( $name ) ? "$name.gz" : $name : $name; File::Spec->file_name_is_absolute( $name ) ? $name : File::Spec->catfile( $data_dir, $name ) }; my $outfile = sub { my $rm = ( @_ > 1 ) ? splice @_, 1, 1 : 0; my ( $name, $fh ) = $outfile_name->( @_ ); unlink $name if $rm && -f $name; # Compress the file if the extension is '.gz' regardless of --gzip-output. my $gzip = $is_gz->( $name ); ERR "Cluster result file '$name' already exists." if -f $name; return $name, IO::File->new( $name, '>:raw' ) unless $gzip; return $name, IO::File->new( "pigz - > \"$name\"", '|-:raw' ) if $has_pigz; return $name, IO::File->new( "gzip - > \"$name\"", '|-:raw' ) if $has_gzip; return $name, ( new IO::Compress::Gzip $name, AutoClose => 1, Append => 0 or die "IO::Compress::Gzip failed: $IO::Compress::Gzip::GzipError" ); }; my $ckp_result = $outfile_name->( $svr_id . ( $from_ckp ? '-' . basename $ckp_name : '' ), 1, $gzip ); ERR "Output checkpoint/journal name '$ckp_result' already exists." if -f $ckp_result; status_heading "Output file; $ckp_result\n"; my ( $ckp_outname, $ckp_fh ) = $outfile->( "$svr_id-tmp", 1, 1, $gzip ); # Necessary to keep everything written in order. $ckp_fh->autoflush( 1 ); my $start_time = localtime; # Attempt to reserve enough room for a full copy of the source checkpoint # since that's what we'd need for a server without any filtering on it. # We could be more accurate, but we'd have to calculate more up-front and # keep it in memory longer. # # Note that pre_allocate() isn't currently a guarantee. if( is_ckp && !$is_gz->( $ckp_outname ) && ! $preview ) { my $min_space = -s $ckp_name; ERR "Need at least $min_space bytes free." unless pre_allocate $min_space, $ckp_outname; # unless output is STDOUT? } ################## Preliminary processing ###################### my $cfg_chksum = uc md5_hex slurp $cfg_file; my $cfghf = $outfile_name->( 'cfg_chksum' ); my $cfg_changed = $cfg_chksum ne ( (-e $cfghf ) ? slurp $cfghf : '' ); ERR "Config checksum file missing!" if is_jnl and not -e $cfghf; status_heading 'Notice: Config file changed since last run!' if $cfg_changed && -e $cfghf; ERR "Config must not change during journal processing." if $cfg_changed and is_jnl; unlink $cfghf if -f $cfghf; print { $outfile->( 'cfg_chksum' ) } $cfg_chksum; my $cache = sub { my ( $name, $fn, $diff_on_cfg, $out ) = @_; my $file = $outfile_name->( "cache-$name" ); # tie %A, "DB_File", "filename" ; $out = $fn->(), store( $out, $file ), return $out if $diff_on_cfg && $cfg_changed || not -f $file; eval { local $SIG{ __DIE__ }; $out = retrieve $file }; exit say "Error retrieving cached file '$file': $@" if $@ or not $out; status_heading "using stored $name"; $out }; status_heading "---- collecting data ----\n"; %groups = %{ $cache->( 'groups', sub { my %gs = xform_group_recs; \ %gs } ) }; my %svrs = %{ $cache->( 'servers', sub { my %svrs = map { $_->{ SVid } => $_ } get_tab_recs 'db.server'; \ %svrs} ) }; my $cbu = $cache->( 'cbu', sub { status_run 'grouping clients by user', \ &clients_by_user } ); my ( $cmap, $cmapd ) = @{ $cache->( 'cmap', sub { status_run 'expanding client-to-server mappings', \ &expand_cdmaps, \ %cdefs, $cbu, 1 }, 1 ) }; my $client_data_maps = $cache->( 'cdmaps', sub { my %m = map { status_start "mapping $_"; my $m = [ map_table_by_client $jnl, $tab_locs, $_ ]; status_end; $_ => $m } @clientdata_tables; \ %m } ); # todo: too big for memory? my $domains = $cache->( 'domains', sub { my %d; status_run 'reading domains', sub { parse $jnl, sub { # Keep the journal processing part in sync with this. $d{ $_[ 0 ]->{ DOname } } = { DOtype => $_[ 0 ]->{ DOtype }, DOserverid => $_[ 0 ]->{ DOserverid }, DOstream => $_[ 0 ]->{ DOstream } }; 0 }, $tab_locs{ 'db.domain' } }; \ %d } ); my $sc2cn = $cache->( 'shelveno_to_client', sub { my %sc; status_run 'mapping shelved changelists to client names', sub { parse $jnl, sub { $sc{ $_[ 0 ]->{ CHkey } } = $_[ 0 ]->{ CHclient }; 0}, $tab_locs{ 'db.change' } }; \ %sc } ); my $sr = sub { my ( $msg, $table, $cbfn, $no_printfn ) = @_; my ( $ver, $tab ) = sniff_tabver $jnl, $tab_locs{ $table }; my $fn = $no_printfn ? \&to_jnl_fmt : $to_jnl_fmt_fns{ $tab }{ $ver }; status_run $msg, sub { parse $jnl, sub { my $r = $cbfn->( @_ ); print $ckp_fh ( $r ? $fn->( $r ) : '' ); 0 }, $tab_locs{ $table } } }; status_heading "\n---- writing-out extra data ----\n"; status_heading 'recording client assignments'; my %dm_cs = map { $_ => 1 } grep { ! exists $cmap->{ $_ } } map { @{ $cbu->{ $_ } } } sort keys %$cbu; { my $client_assignments = $outfile->( 'client-assignments', 1 ); my %cs; push @{ $cs{ $cmap->{ $_ } } }, $_ for keys %$cmap; @{ $cs{ $cmap->{ $_ } } } = sort @{ $cs{ $cmap->{ $_ } } } for keys %$cmap; my $yaml = CPAN::Meta::YAML->new( \ %cs ); print $client_assignments $yaml->write_string; print { $outfile->( 'client-assignments-debug', 1 ) } CPAN::Meta::YAML->new( $cmapd )->write_string; undef $cmapd; } unless( $from_ckp ) { my $dmcsfh = $outfile->( 'unassigned-clients', 1 ); map { say $dmcsfh $_ } sort keys %dm_cs; } # Function deciding where client tables go. my $cdata_fn = sub { my ( $domain, $rec ) = @_; if( $domain =~ /^\d+$/ ) { # //1234/ for shelves my $sd = $sc2cn->{ $domain }; die "No mapping for shelf '$domain'!" unless $sd; $domain = $sd; } # A checkpoint can capture inconsistent state, like a view without a domain. WARN "domain $domain not in domains list!" and return unless $domains->{ $domain }; # sub is_client() my $is_client = ( 'c' eq lc chr $domains->{ $domain }->{ DOtype } ) ? 1 : 0; my $view_global = $rec && $rec->{ table } eq 'db.view' && $is_client && !$domains->{ $domain }->{ DOstream } && $rec->{ VImapflag } != 3 && ( $svrs{ $domains->{ $domain }->{ DOserverid } } // -1 ) != 2381; # 2381 == build-server # label, user, etc my $not_client = ! $is_client; # an unassigned client my $uc = exists $dm_cs{ $domain }; # a client assigned to this server my $ac = defined( $cmap->{ $domain } ) && $cmap->{ $domain } eq $svr_id; # not a workspace server my $ds = !$wksp_svr; # client assigned to an edge server my $os = $is_client && length $domains->{ $domain }->{ DOserverid } // ''; $view_global || ( $not_client || $ds && $uc || $ac ) && !( $ds && $os ) }; status_heading 'filtering configs'; unless( $from_ckp ) { print $ckp_fh ckp_header; # Preserve the nx record for case sensitivity, etc. my $exts = $cfg->{ 'external-servers' }; my %exts_svrs = map { my $st = $_; map { $_ => $st } keys %{ $exts->{ $_ } } } keys %$exts; my %svrs_ok = map { $_->{ SVid } => $exts_svrs{ $_->{ SVid } } } grep { exists $exts_svrs{ $_->{ SVid } } and $exts_svrs{ $_->{ SVid } } ne 'commit-server' } get_tab_recs 'db.server'; $svrs_ok{ any } = 'ol thing'; # Any servers we're planning on creating (i.e. the cluster nodes) are also ok. map { $svrs_ok{ $_ } = $_ } keys %svr_to_type; $svrs_ok{ $cfg->{ cluster }->{ 'cluster.id' } } = $cfg->{ cluster }->{ 'cluster.id' }; %svrs_ok = () if $cfg->{ cluster }->{ 'preserve-config' } eq 'no'; my %saves; @saves{ qw / configurables triggers server-specs / } = ( [ 'db.config' ], [ 'db.trigger' ], [ qw / db.server db.svrview / ] ); map { map { delete $cluster_tabs{ $_ } } @{ $saves{ $_ } } } keys %saves; for my $type ( sort keys %saves ) { my $fh = $outfile->( $type, 1 ); for my $tab ( @{ $saves{ $type } } ) { my $sm = sub { my $id = shift; sub { parse $jnl, sub { my $n = $_[ 0 ]->{ $id }; my $o = exists( $svrs_ok{ $n } ) ? $ckp_fh : $fh; print $o to_jnl_fmt $_[ 0 ]; 0 }, $tab_locs{ $tab } } }; status_run "extracting $type/$tab", $tab eq 'db.trigger' ? sub { dump_range $jnl, $tab_locs{ $tab }, $fh } : $tab eq 'db.server' ? $sm->( 'SVid' ) : $tab eq 'db.svrview' ? $sm->( 'SFid' ) : $tab eq 'db.config' ? $sm->( 'CFsname' ) : sub { die 'per' } } } } close( $ckp_fh ), unlink( $ckp_outname ), return 0 if $preview; ################## Checkpoint processing ###################### my @ps_ctabs = qw / db.change db.changex /; my @wsdtabs = qw / db.revsh db.workingx db.resolvex /; @wsdtabs = () if !$wksp_svr; # todo: workingx is orphaned? # The leftover is what we bulk copy without transforms, at the end. delete @cluster_tabs{ @ps_ctabs, @delrps, @clientdata_tables, @wsdtabs, qw / db.depot db.domain db.view db.locks / }; my $dm_trans = $cfg->{ cluster }->{ 'path-translation' }; goto JOURNAL if is_jnl; status_heading "\n---- applying table translations ----\n"; status_heading 'updating db.depot map paths'; $sr->( 'translating depot root paths', 'db.depot', sub { $_[ 0 ]->{ DPmap } = exists( $dm_trans->{ $_[ 0 ]->{ DPname } } ) ? $dm_trans->{ $_[ 0 ]->{ DPname } } : $_[ 0 ]->{ DPmap }; $_[ 0 ] } ); $sr->( 'making new db.excl records', 'db.working', sub { mk_excl @_ }, 1 ); $sr->( "promoting shelves: $_", $_, \ &promote_shelves ) for @ps_ctabs; $sr->( 'assigning serverIDs to clients', 'db.domain', sub { assign_client_serverid \ %svr_to_type, $cmap, $_[ 0 ] } ); # db.view's key is a little different than the other client data tables. $sr->( 'filtering view maps', 'db.view', sub { $_[ 0 ] if $cdata_fn->( $_[ 0 ]->{ VIname }, $_[ 0 ] ) } ); $sr->( 'filtering locks', 'db.locks', sub { $_[ 0 ] if $cdata_fn->( $_[ 0 ]->{ LOclient } ) } ); if( $wksp_svr ) { # @wsdtabs $sr->( "filtering shelving record: $$_[ 0 ]", $$_[ 0 ], $$_[ 1 ] ) for ( [ 'db.revsh' , sub { $_[ 0 ] if $cdata_fn->( $_[ 0 ]->{ REchange } ) } ], [ 'db.workingx', sub { $_[ 0 ] if $cdata_fn->( $_[ 0 ]->{ WOclient } ) } ], [ 'db.resolvex', sub { $_[ 0 ] if $cdata_fn->( client_from $_[ 0 ]->{ RStfile } ) } ] ); } my $op = $progress; status_run "copying client data (@clientdata_tables)", sub { my ( $cnt, $tot ) = ( 0, 0 ); $tot += @{ $client_data_maps->{ $_ } } for keys %{ $client_data_maps }; map { status $cnt, $tot if ++$cnt % POSIX::ceil( $tot / 100 ) == 0; $progress = 0; dump_range $jnl, [ @$_[ 1 .. 2 ] ], $ckp_fh; $progress = $op } sort { $$a[ 0 ] cmp $$b[ 0 ] } grep { $cdata_fn->( $_->[ 0 ] ) } map { @{ $client_data_maps->{ $_ } } } sort keys %$client_data_maps; }; status_heading "\n---- bulk copying remaining tables ----\n"; # See uniconv for a copy of this progress logic. my @cts = sort keys %cluster_tabs; my $tlen = 0; map { my $l = length; $tlen = $l if $l > $tlen } @cts; $tlen += 5; my @tabs = map { [ $_, $cluster_tabs{ $_ } ] } @cts; my $cur_sz = 0; my $tabs_sz = range_size values %cluster_tabs; for( my $i = 0; $i < @tabs; $i++ ) { my $tab = shift @{ $tabs[ $i ] }; my $tabfmt = $tab . ' ' x ( $tlen - length $tab ); my $rs = range_size @{ $tabs[ $i ] }; my $st = sprintf '% 2u/%u % 2u%% - % 3u%% %s', $i, @tabs - 1, ( $cur_sz / $tabs_sz ) * 100, ( ( $cur_sz + $rs ) / $tabs_sz ) * 100, $tabfmt; $cur_sz += $rs; status_run $st, sub { dump_range $jnl, shift @{ $tabs[ $i ] }, $ckp_fh } } ################## Final pieces ###################### # Note that if we didn't write the trailer record here and perform a last # seek, that due to the above mix of buffered and direct writes that we'd # get bad data from tell/systell when called here. # Note that this record is hardcoded, but isn't known to be used by anything. my $tnx_ver = is_ckp() ? 1 : 3; print $ckp_fh "\@nx@ $tnx_ver 1414089913 \@$schema->{SVR_VERSION}@" . " 0 0 0 0 0 @@ @@ @@ @@ @@ \n" . "\@ex@ 16004 1414089913\n"; unless( $is_gz->( $ckp_outname ) ) { seek $ckp_fh, 0, SEEK_CUR; # Trim off any leftover pre-allocated space. truncate $ckp_fh, tell $ckp_fh; } close $ckp_fh; rename $ckp_outname, $ckp_result; my $end_time = localtime; status_heading "\nProcessing took " . ( $end_time - $start_time )->pretty; return 0; JOURNAL: # todo: preserve the vv record # todo: compile this. my $opfn = sub {}; # dv => dv, pv/rv => trans_fn # need to split on xn boundary so we are sure to get the definition of a view # line (if it's client or not) but creation could have happened in previous # block? my ( @unknown_views, @unknown_workingx, @unknown_revsh ); # Domain map. Cache for cdata_fn(). # todo: use for checkpoint processing too. my %dmap = map { $_ => 1 } grep { $cmap->{ $_ } eq $svr_id } keys %$cmap; # todo: apply server type to the logic? # Only consider the single target now since expand_cdmaps gets very slow # processing domains. %cdefs = ( $svr_id => $cfg->{ cluster }->{ $svr_to_type{ $svr_id } }->{ $svr_id } ); my $jcas = $outfile->( "${\ basename $ckp_name }-$svr_id-client-assignments" ); # Since Perl function calls are relatively slow, the record dispatch table # inlines the bodies of various utility functions. my %jnlfns = ( 'db.depot' => sub { my $rec = $_[ 0 ]; $rec->{ DPmap } = exists( $dm_trans->{ $rec->{ DPname } } ) ? $dm_trans->{ $rec->{ DPname } } : $rec->{ DPmap }; }, 'db.working' => sub { my @r; # sub mk_excl() my $woa = $_[ 0 ]->{ WOaction }; my $added = $woa == 0 || $woa == 3 || $woa == 5 || $woa == 8; if( ! $added && $_[ 0 ]->{ WOtype } & 0x00040 ) { my %nrec = ( op => $_[ 0 ]->{ op }, version => 1, table => 'db.excl', EXdfile => $_[ 0 ]->{ WOdfile }, EXclient => $_[ 0 ]->{ WOclient }, EXuser => $_[ 0 ]->{ WOuser } ); push @r, \ %nrec; } # sub client_from() my $c = substr $_[ 0 ]->{ WOcfile }, 2, -2 + index $_[ 0 ]->{ WOcfile }, '/', 2; @r, ( $dmap{ $c } //= $cdata_fn->( $c ) ? 1 : 0 ) ? $_[ 0 ] : () }, 'db.revsh' => sub { if( $wksp_svr && ! exists $sc2cn->{ $_[ 0 ]->{ REchange } } ) { push @unknown_revsh, $_[ 0 ] } else { $_[ 0 ] if !$wksp_svr || $cdata_fn->( $_[ 0 ]->{ REchange } )}}, 'db.workingx' => sub { if( $wksp_svr && ! exists $sc2cn->{ $_[ 0 ]->{ WOchange } } ) { push @unknown_workingx, $_[ 0 ] } else { $_[ 0 ] if !$wksp_svr || $cdata_fn->( $_[ 0 ]->{ WOclient } )}}, 'db.resolvex' => sub { $_[ 0 ] if !$wksp_svr || $cdata_fn->( client_from $_[ 0 ]->{ RStfile } ) }, 'db.change' => sub { promote_shelves $_[ 0 ] }, 'db.changex' => sub { # order in jnl: db.workingx, db.revsh, db.changex return promote_shelves $_[ 0 ] if !$wksp_svr; my @ret; if( is_shelved $_[ 0 ]->{ CHstatus } ) { $sc2cn->{ $_[ 0 ]->{ CHchange } } = $_[ 0 ]->{ CHclient } if $_[ 0 ]->{ op } =~ /[pr]v/; my ( @unkn_w, @unkn_r ); map { push @unkn_w, $_ unless exists $sc2cn->{ $_->{ WOclient } }; push @ret, $_ if exists( $sc2cn->{ $_->{ WOclient } } ) and $cdata_fn->( $_->{ WOclient } ) } @unknown_workingx; @unknown_workingx = @unkn_w; map { push @unkn_r, $_ unless exists $sc2cn->{ $_->{ REchange } }; push @ret, $_ if exists( $sc2cn->{ $_->{ REchange } } ) and $cdata_fn->( $_->{ REchange } ) } @unknown_revsh; @unknown_revsh = @unkn_r; delete $sc2cn->{ $_[ 0 ]->{ CHchange } } if $_[ 0 ]->{ op } eq 'dv'; } @ret, promote_shelves $_[ 0 ] }, 'db.view' => sub { push @unknown_views, $_[ 0 ] unless exists $domains->{ $_[ 0 ]->{ VIname } }; return @_ if exists( $domains->{ $_[ 0 ]->{ VIname } } ) and $cdata_fn->( $_[ 0 ]->{ VIname } ) }, 'db.locks' => sub { $_[ 0 ] if $dmap{ $_[ 0 ]->{ LOclient } } //= $cdata_fn->( $_[ 0 ]->{ LOclient } ) }, 'db.domain' => sub { my $r = $_[ 0 ]; my $name = $r->{ DOname }; my @ret; if( $r->{ op } eq 'dv' ) { delete $domains->{ $name }; delete $dmap{ $name }; if( is_client $r->{ DOtype } ) { delete $cmap->{ $name }; # todo: no owner possible? @{ $cbu->{ $r->{ DOowner } } } = grep { $_ ne $name } @{ $cbu->{ $r->{ DOowner } } }; } push @ret, $r; } # rv domains are ignored. e.g. changing the owner of a # client does not change the server it is assigned to because # it would have all the previous client state on a separate # server from the newly-assigned destination. It's assumed # that a dv is associated with the removal of all client data. if( $r->{ op } eq 'pv' ) { # Keep the checkpoint processing part in sync with this. $domains->{ $name } = { DOtype => $r->{ DOtype }, DOserverid => $r->{ DOserverid }, DOstream => $r->{ DOstream } }; $dmap{ $name } = 1; if( is_client $r->{ DOtype } ) { push @{ $cbu->{ $r->{ DOowner } } }, $name; my $lcbu; push @{ $lcbu->{ $r->{ DOowner } } }, $name; my ( $docmap, $dmd ) = @{ expand_cdmaps \ %cdefs, $lcbu, 0 }; $cmap->{ $name } = $docmap->{ $name }, say $jcas $name if defined $docmap->{ $name }; $dm_cs{ $name } = 1 unless $docmap->{ $name }; my @ac = assign_client_serverid \ %svr_to_type, $cmap, $r; push @ret, ( @ac && keys %{ $ac[ 0 ] } ) ? @ac : $r; } else { push @ret, $r } # todo: call $jnlfns{ 'db.view' }->()? my @unkn; map { push @unkn, $_ unless exists $domains->{ $_->{ VIname } }; push @ret, $_ if exists( $domains->{ $_->{ VIname } } ) and $cdata_fn->( $_->{ VIname } ) } @unknown_views; @unknown_views = @unkn; } # todo: need $cdata_fn check? push @ret, $r if $r->{ op } eq 'rv'; @ret }, ); # @clientdata_tables without db.working since it's needed to make db.excl. @jnlfns{ qw / db.have db.resolve / } = map { my $col = $_; sub { my $c = substr $_[ 0 ]->{ $col }, 2, -2 + index $_[ 0 ]->{ $col }, '/', 2; $_[ 0 ] if $dmap{ $c } //= $cdata_fn->( $c ) ? 1 : 0 } } qw / HAcfile RStfile /; @jnlfns{ keys %cluster_tabs } = map { sub { @_ } } keys %cluster_tabs; status_heading "\n---- processing journal ----\n"; # Note: only necessary until parse() understands nx/vv. # todo: disabled for now since the file name change causes the server to reject it. # print $ckp_fh jnl_header; status_run 'converting journal', sub { # todo: include @dl@, etc? parse $jnl, sub { my $fn = $jnlfns{ $_[ 0 ]->{ table } } // sub { @_ }; # todo: accumulate then syswrite? map { print $ckp_fh to_jnl_fmt $_ } grep defined && 'HASH' eq ref, $fn->( @_ ); 0 }, [ 0, -s $jnl_name ]; }; $end_time = localtime; # status_heading "\nProcessing took " . ( $end_time - $start_time )->pretty; say STDERR 'Unexpected order of views and domains: ' . Dumper( \ @unknown_views ) and exit 1 if @unknown_views; say STDERR 'Unexpected order of workingx and changex: ' . Dumper( \ @unknown_workingx ) and exit 1 if @unknown_workingx; say STDERR 'Unexpected order of revsh and changex: ' . Dumper( \ @unknown_revsh ) and exit 1 if @unknown_revsh; # truncate close $ckp_fh; rename $ckp_outname, $ckp_result; 0 } sub gen_config { eval 'use CPAN::Meta::YAML'; ERR "Please install CPAN::Meta::YAML." if $@; # Journal SVservices value to server type. my %svcs = qw / 48 broker 77 build-server 160 proxy 515 P4AUTH 1027 P4CHANGE 2307 standard 2373 replica 2381 build-server 2533 forwarding-replica 6637 edge-server 12035 commit-server 16432 workspace-router 23021 workspace-server 28419 depot-master 35141 standby 35301 forwarding-standby 51525 depot-standby 65539 local /; my %ss; ${ $ss{ $svcs{ $_->{ SVservices } } } }{ $_->{ SVid } } = { port => 1234 } for get_tab_recs 'db.server'; my %cfg = ( cluster => { 'cluster.id' => 'your_Perforce_Cluster_name', 'character-encoding' => { qw / unicode no mixed-case yes / }, 'preserve-config' => 'yes', # output => { qw / configurables cfg.ckp # server-specs svr_specs.ckp # triggers triggers.ckp # unassigned-clients uacs.txt / }, 'path-translation' => { qw ( depot0 new_depot0_root/... depot1 depot1_is_also_now_relative/... ) }, 'depot-server' => { ds0 => { qw / host svr0.example.com port 1234 / } }, 'workspace-server' => { ws0 => { qw / host wsvr0.example.com port 1234 /, groups => [ 'all_users' ] } }, }, ); # All local depot types. my %depots = map { $_->{ DPname } => $_->{ DPmap } } grep { $_->{ DPtype } != 1 } get_tab_recs 'db.depot'; $cfg{ cluster }->{ 'path-translation' } = \ %depots if keys %depots; my $yaml = CPAN::Meta::YAML->new( \ %cfg ); my $str = $yaml->write_string =~ s/^(.*)/# $1/mgr; # todo: don't emit an empty hash %cfg = ( 'external-servers' => \ %ss ); $yaml = CPAN::Meta::YAML->new( \ %cfg ); $str .= "\n\n# NOTE: existing commit-server will be removed as part " . "of the checkpoint import.\n\n" if exists $ss{ 'commit-server' }; $str .= $yaml->write_string; %groups = xform_group_recs unless %groups; return $str unless keys %groups; $str .= "\n\n# NOTE: The following is just a dump of the groups existing " . "in the checkpoint.\n\n"; %cfg = ( 'groups in checkpoint' => [ sort keys %groups ] ); $yaml = CPAN::Meta::YAML->new( \ %cfg ); $str .= $yaml->write_string =~ s/^(.*)/# $1/mgr; } ################################################################################ my @OARGV = @ARGV; my $avc = @ARGV // 0; { local $SIG{ __WARN__ }; GetOptions 'bug-check=s' => \ $bug_check, 'cfg-file=s' => \ $cfg_file, 'clientmap=s' => \ $clientmap, 'cluster-preview' => \ $cluster_preview, 'corrupt-map' => \ $crpt_map, 'corrupt-recover' => \ $crpt_rcvr, 'corrupt-report' => \ $crpt_rpt, 'E=s' => \ $dashE, 'debug=i' => \ $debug, 'delimiter=s' => \ $delim, 'demo' => \ $demo, 'depot-cleaner' => \ $depot_cleaner, 'depot-root=s' => \ $depot_root, 'depot-root-cs=s' => \ $depot_root_cs, 'detect-input-type' => \ $detect_input_type, 'dump-file=s' => \ $dump_file, 'dump-format=s' => \ $dump_format, 'dump-lines=s' => \ $dump_lines, 'dump-range=s' => \ $dump_range, 'dump-table=s' => \ $dump_table, 'env-report' => \ $env_report, 'explode-ckp' => \ $explode_ckp, 'filter=s' => \ $filter, 'from-ckp=s' => \ $from_ckp, 'gen-config' => \ $gen_config, 'gzip-output' => \ $gzip, 'help' => \ $help, 'iconv=s' => \ $iconv, 'man' => \ $man, 'mk-man=s' => \ $mk_man, 'output-dir=s' => \ $output_dir, 'parallel|j=i' => \ $parallel, 'parallel-helper=s' => \ $parallel_helper, 'par-ckp-replay=s' => \ $parallel_ckp_replay, 'parmap' => \ $parmap, 'parse-test:s' => \ $parse_test, 'print-ckp-sizes:i' => \ $print_ckp_sizes, 'print-ckp-summary' => \ $print_ckp_summary, 'progress:i' => \ $progress, 'quiet' => \ $quiet, 'server-id=s' => \ $server_id, 'show-encodings' => \ $show_encs, 'show-schema:s' => \ $show_schema, 'show-schema-rel:s' => \ $show_schema_rel, 'split=s' => \ $split, 'srv-host=s' => \ $srv_host, 'store-schema' => \ $store_schema, 'store-schema-fast' => \ $store_schema_fast, 'strict-parse' => \ $strict_parse, 'tablemap' => \ $tablemap, 'test-assist=s' => \ $test_assist, 'to-cluster' => \ $to_cluster, 'uniconv' => \ $uniconv, 'uniconv-check' => \ $uniconv_check, 'uniconv-gui' => \ $uniconv_gui, 'verify-ckp-chksum' => \ $verify_ckp_chk, 'v|version' => \ $version, 'version-num' => \ $version_num, or exit 1; } $avc = 0 if @ARGV == 1 and $avc == 1; my $usage_level = $man ? 2 : $help ? 1 : !$avc ? 0 : undef; $ext_shell = $^O eq 'MSWin32' ? 'cmd /c' : 'sh -c'; $progress //= interactive; init_term_control; umask 077; # no group/other r/w/x exit pod2usage -input => fh_from_str help, -exitval => ( $help or $man ) ? 0 : 1, -verbose => $usage_level, -width => term_width, if defined $usage_level; exit print mk_man $mk_man if $mk_man; exit say version . ' ' . self_checksum if $version; exit print version_num if $version_num; exit say fmt_encs if $show_encs; $jnl_name = shift; ERR "Unexpected arguments: @ARGV" if @ARGV > 0; $d = $debug // 0; binmode *STDIN; STDERR->autoflush( 1 ); $prog_fh = \ *STDERR; $parallel_helper //= ''; ( $dashE ) = $parallel_helper =~ /^DASHE:(.*)/ if $parallel_helper =~ /DASHE/; my $prog_fh_name = $ENV{ P4JNLTOOL_PAR_TMPFILE } // ''; $prog_fh = IO::File->new( $prog_fh_name, '>' ) or die "no parhelp tmpfile '$prog_fh_name': $!" if $parallel_helper; $prog_fh->autoflush( 1 ); $0 = 'jnltool'; $parallel //= par_default; exit iconv $iconv if $iconv; $has_pigz = 1 if `$ext_shell "pigz -h 2>&1"` =~ /Usage: pigz \[options\]/; $has_gzip = 1 if `$ext_shell "gzip --help 2>&1"` =~ /Usage: gzip \[OPTION\]\.\.\./; exit print env_report if $env_report; my $needs_source = $bug_check || $clientmap || $demo || $depot_cleaner || $detect_input_type || $dump_lines || $dump_table || $dump_range || $explode_ckp || $filter || $gen_config || $crpt_map || $crpt_rcvr || $crpt_rpt || $parallel_ckp_replay || $parmap || $parse_test || $print_ckp_sizes || $print_ckp_summary || $split || $tablemap || $to_cluster || $verify_ckp_chk || $uniconv || $uniconv_check || $uniconv_gui; ERR "Input file required." if $needs_source and not defined $jnl_name; ERR "Input file '$jnl_name' isn't a file." if $needs_source && ! -f $jnl_name; ERR "Input file must not be empty." if $needs_source and 0 == -s $jnl_name; ERR "Input file must not be compressed." if ( $jnl_name // '' ) =~ /\.gz$/ && ! $verify_ckp_chk; ERR "Unknown argument." if @_; exit test_assist $test_assist if $test_assist; %fmts = ( dump_range => \ &dump_range, perlout_fmt_parse => \ &perlout_fmt_parse, storable_fmt_parse=> \ &storable_fmt_parse, jnl_fmt_parse => \ &jnl_fmt_parse, dump_range_sep => \ &dump_range_sep ); ERR "Bad formatter" if $dump_format and not defined $fmts{ $dump_format }; $dump_format = $dump_format ? $fmts{ $dump_format } : \ &dump_range; $output_fh = *STDOUT; binmode $output_fh; open $output_fh, '>:raw', $dump_file if $dump_file; $jnl = open_ckp $jnl_name unless $show_schema || $show_schema_rel || $dashE || $store_schema; exit verify_ckp_chksum if $verify_ckp_chk; exit say is_jnl() ? 'journal' : 'checkpoint' if $detect_input_type; exit dump_lines $dump_lines if $dump_lines; exit status_run "Dumping range", $dump_format, $jnl, [ split ',', $dump_range ], $output_fh if $dump_range; exit status_run "Splitting", \ &split_jnl, $jnl, $jnl_name, $split if $split; $schema = init_schema; exit store_schema $store_schema_fast if $store_schema; exit show_schema $show_schema if $show_schema; exit show_schema_rel $show_schema_rel if $show_schema_rel; ( $max_rel, $max_ver ) = ( $schema->{ SVR_RELEASE }, $schema->{ SVR_VERSION } ); $strict_parse = 1 if $crpt_map or $crpt_rcvr or $crpt_rpt; compile_matcher; exit ( defined( $_ = eval( ( $dashE !~ /\R/ && -f( $dashE ) ) ? slurp $dashE : $dashE ) ) ? $_ : print STDERR $@ ) if $dashE; $parse_test //= ''; my $ptest_tab; ( $parse_test, $ptest_tab ) = $parse_test =~ /([^=]+)=?(.*)/; $ptest_tab //= $parse_test; exit parse_test $jnl, [ 0, -s $jnl_name ], $parse_test if $parse_test && ( $parse_test eq 'jnl-perlin' || is_jnl ); # $parse_test =~ /^jnl/; exit filter $filter, sub { print join( $delim // ' ', @_ ), "\n" }, 1 if $filter and is_jnl; if( $crpt_map ) { my @dmg = map_damage; local $Data::Dumper::Terse = 1; local $Data::Dumper::Quotekeys = 0; print Dumper \ @dmg if @dmg; exit } exit remove_damage if $crpt_rcvr; exit report_damage if $crpt_rpt; goto CLUSTER if $to_cluster and is_jnl; ERR "Checkpoint doesn't look right." unless ckp_readable $jnl; ERR "Function only allowed on checkpoint files." unless is_ckp; $ckp_ver = check_ckp_ver $jnl, $min_rel, $min_ver, $max_rel, $max_ver; @tab_locs_order = find_tables $jnl_name, $jnl; %tab_locs = map { $_->[ 0 ] => [ $_->[ 1 ], $_->[ 2 ] ] } @tab_locs_order; exit http_svr CB_RDY => sub { uniconv CKP_NAME => $jnl_name, CHECK => 0, GUI => 1, GUI_LOCK_CHECK => 1; say "Listening on http://$_[0]" }, HOST => $srv_host, CB_LOG => uniconv( CKP_NAME => $jnl_name, LOG_FN => 1 ), CB_RSP => uniconv CKP_NAME => $jnl_name, CHECK => 0, GUI => 1, GUI_LOCK_CHECK => 0 if $uniconv_gui; exit parse_test $jnl, $tab_locs{ $ptest_tab }, $parse_test if $parse_test; exit bug_check $bug_check if $bug_check; exit depot_cleaner if $depot_cleaner; exit explode_ckp $jnl_name if $explode_ckp; exit print_ckp_sizes $print_ckp_sizes if defined $print_ckp_sizes; exit print_ckp_summary if defined $print_ckp_summary; { local $Data::Dumper::Terse = 1; local $Data::Dumper::Quotekeys = 0; exit say Dumper [ status_run "mapping $clientmap", sub { map_table_by_client $jnl, \ %tab_locs, $clientmap } ] if $clientmap; exit say Dumper [ @tab_locs_order ] if $tablemap; exit say Dumper [ map { [ $_, split_table $jnl, $tab_locs{ $_ }, $parallel ] } sort keys %tab_locs ] if $parmap; } ERR "Bad table name: '$dump_table'" if $dump_table and not exists $tab_locs{ $dump_table }; # todo: disable progress if it's going to stdout and we're not in a pipe? exit status_run "Dumping $dump_table", $dump_format, $jnl, $tab_locs{ $dump_table }, $output_fh if $dump_table; my $formatter; $output_fmt = $formatter // sub { print for @_ }; exit par_svr_replay P4ROOT => $parallel_ckp_replay, PAR_HELPER => $parallel_helper if $parallel_ckp_replay; exit filter $filter, sub { print join( $delim // ' ', @_ ), "\n" }, 0 if $filter; # todo: should always lock? exit uniconv CKP_NAME => $jnl_name, CHECK => $uniconv_check ? 1 : 0, GUI => 0, GUI_LOCK_CHECK => 0, DEPOT_ROOT => ( $depot_root // '' ), DEPOT_ROOT_ENC => ( $depot_root_cs // '' ), CONVERT => $uniconv ? 1 : 0 if $uniconv or $uniconv_check; exit uniconv CKP_NAME => $jnl_name, PAR_HELPER => $parallel_helper if $parallel_helper =~ /^UNICONV_CHECK/; exit print gen_config if $gen_config; CLUSTER: exit mk_cluster \ %tab_locs, $cluster_preview, $jnl_name, $cfg_file, $server_id if $to_cluster; exit demo $jnl if $demo; ERR "Invalid parameter combination: @OARGV"; __DATA__ { SVR_RELEASE => '2024.1', SVR_RELTAB_MAP => { 58 => { 'db.archive' => 2, 'db.archmap' => 0, 'db.boddate' => 1, 'db.bodresolve' => 2, 'db.bodresolvex' => 1, 'db.bodtext' => 1, 'db.bodtextcx' => 1, 'db.bodtexthx' => 0, 'db.bodtextsx' => 2, 'db.bodtextwx' => 2, 'db.change' => 7, 'db.changeidx' => 2, 'db.changex' => 7, 'db.config' => 1, 'db.configh' => 1, 'db.counters' => 1, 'db.depot' => 2, 'db.desc' => 1, 'db.domain' => 8, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 2, 'db.fixrev' => 2, 'db.graphindex' => 2, 'db.graphperm' => 0, 'db.group' => 10, 'db.groupx' => 2, 'db.have' => 4, 'db.have.pt' => 4, 'db.have.rp' => 4, 'db.haveg' => 0, 'db.haveview' => 2, 'db.integ' => 1, 'db.integed' => 1, 'db.integedss' => 3, 'db.integtx' => 1, 'db.ixdate' => 1, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 2, 'db.job' => 1, 'db.jobdesc' => 0, 'db.jobpend' => 1, 'db.label' => 1, 'db.ldap' => 0, 'db.locks' => 3, 'db.locksg' => 3, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 3, 'db.nameval' => 1, 'db.object' => 1, 'db.property' => 1, 'db.protect' => 6, 'db.pubkey' => 1, 'db.ref' => 0, 'db.refcntadjust' => 0, 'db.refhist' => 1, 'db.remote' => 2, 'db.repo' => 5, 'db.repoview' => 2, 'db.resolve' => 2, 'db.resolveg' => 0, 'db.resolvex' => 2, 'db.rev' => 10, 'db.revbx' => 10, 'db.revcx' => 1, 'db.revdx' => 10, 'db.revfs' => 1, 'db.revhx' => 10, 'db.review' => 1, 'db.revpx' => 10, 'db.revsh' => 10, 'db.revstg' => 10, 'db.revsx' => 10, 'db.revtr' => 10, 'db.revtx' => 10, 'db.revux' => 10, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 5, 'db.sendq.pt' => 5, 'db.server' => 3, 'db.stash' => 1, 'db.storage' => 2, 'db.storageg' => 1, 'db.storagesh' => 2, 'db.storagesx' => 0, 'db.stream' => 3, 'db.streamq' => 0, 'db.streamrelation' => 0, 'db.streamview' => 2, 'db.streamviewx' => 2, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 3, 'db.templatesx' => 3, 'db.templatewx' => 3, 'db.ticket' => 1, 'db.ticket.rp' => 1, 'db.topology' => 3, 'db.traits' => 1, 'db.trigger' => 3, 'db.upgrades' => 1, 'db.upgrades.rp' => 1, 'db.user' => 8, 'db.user.rp' => 8, 'db.uxtext' => 0, 'db.view' => 2, 'db.view.rp' => 2, 'db.working' => 11, 'db.workingg' => 1, 'db.workingx' => 11 }, 57 => { 'db.archive' => 2, 'db.archmap' => 0, 'db.boddate' => 1, 'db.bodresolve' => 2, 'db.bodresolvex' => 1, 'db.bodtext' => 1, 'db.bodtextcx' => 1, 'db.bodtexthx' => 0, 'db.bodtextsx' => 2, 'db.bodtextwx' => 2, 'db.change' => 7, 'db.changeidx' => 2, 'db.changex' => 7, 'db.config' => 1, 'db.configh' => 1, 'db.counters' => 1, 'db.depot' => 2, 'db.desc' => 1, 'db.domain' => 8, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 2, 'db.fixrev' => 2, 'db.graphindex' => 2, 'db.graphperm' => 0, 'db.group' => 10, 'db.groupx' => 2, 'db.have' => 4, 'db.have.pt' => 4, 'db.have.rp' => 4, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 1, 'db.integed' => 1, 'db.integedss' => 3, 'db.integtx' => 1, 'db.ixdate' => 1, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 2, 'db.job' => 1, 'db.jobdesc' => 0, 'db.jobpend' => 1, 'db.label' => 1, 'db.ldap' => 0, 'db.locks' => 3, 'db.locksg' => 3, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 3, 'db.nameval' => 1, 'db.object' => 1, 'db.property' => 1, 'db.protect' => 6, 'db.pubkey' => 1, 'db.ref' => 0, 'db.refcntadjust' => 0, 'db.refhist' => 1, 'db.remote' => 2, 'db.repo' => 5, 'db.repoview' => 1, 'db.resolve' => 2, 'db.resolveg' => 0, 'db.resolvex' => 2, 'db.rev' => 10, 'db.revbx' => 10, 'db.revcx' => 1, 'db.revdx' => 10, 'db.revfs' => 1, 'db.revhx' => 10, 'db.review' => 1, 'db.revpx' => 10, 'db.revsh' => 10, 'db.revstg' => 10, 'db.revsx' => 10, 'db.revtr' => 10, 'db.revtx' => 10, 'db.revux' => 10, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 5, 'db.sendq.pt' => 5, 'db.server' => 3, 'db.stash' => 1, 'db.storage' => 2, 'db.storageg' => 1, 'db.storagesh' => 2, 'db.storagesx' => 0, 'db.stream' => 3, 'db.streamq' => 0, 'db.streamrelation' => 0, 'db.streamview' => 1, 'db.streamviewx' => 2, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 3, 'db.templatesx' => 3, 'db.templatewx' => 3, 'db.ticket' => 1, 'db.ticket.rp' => 1, 'db.topology' => 3, 'db.traits' => 1, 'db.trigger' => 3, 'db.upgrades' => 1, 'db.upgrades.rp' => 1, 'db.user' => 8, 'db.user.rp' => 8, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 11, 'db.workingg' => 1, 'db.workingx' => 11 }, 55 => { 'db.archive' => 1, 'db.archmap' => 0, 'db.boddate' => 0, 'db.bodresolve' => 1, 'db.bodresolvex' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 1, 'db.bodtextwx' => 1, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.configh' => 0, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 10, 'db.groupx' => 2, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integedss' => 2, 'db.integtx' => 0, 'db.ixdate' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.jobdesc' => 0, 'db.jobpend' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 1, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refcntadjust' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 4, 'db.repoview' => 1, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 1, 'db.storageg' => 0, 'db.storagesh' => 1, 'db.storagesx' => 0, 'db.stream' => 2, 'db.streamq' => 0, 'db.streamrelation' => 0, 'db.streamview' => 1, 'db.streamviewx' => 2, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 2, 'db.templatewx' => 2, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.topology' => 1, 'db.traits' => 0, 'db.trigger' => 3, 'db.upgrades' => 0, 'db.upgrades.rp' => 0, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 54 => { 'db.archive' => 1, 'db.archmap' => 0, 'db.boddate' => 0, 'db.bodresolve' => 1, 'db.bodresolvex' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 1, 'db.bodtextwx' => 1, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.configh' => 0, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 2, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integedss' => 1, 'db.integtx' => 0, 'db.ixdate' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.jobdesc' => 0, 'db.jobpend' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 1, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refcntadjust' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 4, 'db.repoview' => 1, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 1, 'db.storageg' => 0, 'db.storagesh' => 1, 'db.storagesx' => 0, 'db.stream' => 2, 'db.streamq' => 0, 'db.streamrelation' => 0, 'db.streamview' => 1, 'db.streamviewx' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 2, 'db.templatewx' => 2, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.topology' => 1, 'db.traits' => 0, 'db.trigger' => 3, 'db.upgrades' => 0, 'db.upgrades.rp' => 0, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 53 => { 'db.archive' => 1, 'db.archmap' => 0, 'db.boddate' => 0, 'db.bodresolve' => 1, 'db.bodresolvex' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 1, 'db.bodtextwx' => 1, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.configh' => 0, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integedss' => 1, 'db.integtx' => 0, 'db.ixdate' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.jobdesc' => 0, 'db.jobpend' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 1, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refcntadjust' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 4, 'db.repoview' => 1, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 1, 'db.storageg' => 0, 'db.storagesh' => 1, 'db.storagesx' => 0, 'db.stream' => 2, 'db.streamq' => 0, 'db.streamrelation' => 0, 'db.streamview' => 1, 'db.streamviewx' => 0, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 2, 'db.templatewx' => 2, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.topology' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.upgrades' => 0, 'db.upgrades.rp' => 0, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 52 => { 'db.archive' => 1, 'db.archmap' => 0, 'db.boddate' => 0, 'db.bodresolve' => 1, 'db.bodresolvex' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 1, 'db.bodtextwx' => 1, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.configh' => 0, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integedss' => 1, 'db.integtx' => 0, 'db.ixdate' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.jobdesc' => 0, 'db.jobpend' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 4, 'db.repoview' => 1, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 1, 'db.storageg' => 0, 'db.storagesh' => 1, 'db.storagesx' => 0, 'db.stream' => 2, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 2, 'db.templatewx' => 2, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.upgrades' => 0, 'db.upgrades.rp' => 0, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 51 => { 'db.archmap' => 0, 'db.bodresolve' => 1, 'db.bodresolvex' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 1, 'db.bodtextwx' => 1, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.configh' => 0, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integedss' => 1, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 3, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 1, 'db.storageg' => 0, 'db.storagesh' => 1, 'db.storagesx' => 0, 'db.stream' => 2, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 2, 'db.templatewx' => 2, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.upgrades' => 0, 'db.upgrades.rp' => 0, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 50 => { 'db.archmap' => 0, 'db.bodresolve' => 1, 'db.bodresolvex' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 1, 'db.bodtextwx' => 1, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.configh' => 0, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integedss' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 3, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 1, 'db.storageg' => 0, 'db.storagesh' => 1, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 2, 'db.templatewx' => 2, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.upgrades' => 0, 'db.upgrades.rp' => 0, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 49 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.configh' => 0, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 3, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.scanctl' => 0, 'db.scandir' => 0, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 1, 'db.storageg' => 0, 'db.storagesh' => 1, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 1, 'db.templatewx' => 1, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.upgrades' => 0, 'db.upgrades.rp' => 0, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 48 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 2, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 0, 'db.storageg' => 0, 'db.storagesh' => 0, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 1, 'db.templatewx' => 1, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 47 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 6, 'db.changeidx' => 1, 'db.changex' => 6, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.exclg' => 0, 'db.exclgx' => 0, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 2, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revstg' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.storage' => 0, 'db.storageg' => 0, 'db.storagesh' => 0, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 1, 'db.templatewx' => 1, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 46 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 5, 'db.changeidx' => 1, 'db.changex' => 5, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 1, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 2, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 4, 'db.sendq.pt' => 4, 'db.server' => 3, 'db.stash' => 0, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 0, 'db.templatewx' => 0, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 45 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 4, 'db.changeidx' => 1, 'db.changex' => 4, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveg' => 0, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 2, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 4, 'db.server' => 2, 'db.stash' => 0, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 0, 'db.templatewx' => 0, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 44 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 4, 'db.changeidx' => 1, 'db.changex' => 4, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphindex' => 0, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 2, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 4, 'db.server' => 2, 'db.stash' => 0, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 0, 'db.templatewx' => 0, 'db.ticket' => 0, 'db.ticket.rp' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 43 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 4, 'db.changeidx' => 1, 'db.changex' => 4, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 7, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.haveview' => 1, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.locksg' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 1, 'db.resolve' => 1, 'db.resolveg' => 0, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 3, 'db.server' => 2, 'db.stash' => 0, 'db.stream' => 1, 'db.submodule' => 0, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 0, 'db.templatewx' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingg' => 0, 'db.workingx' => 10 }, 42 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 4, 'db.changeidx' => 1, 'db.changex' => 4, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 6, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.graphperm' => 0, 'db.group' => 9, 'db.groupx' => 1, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.object' => 0, 'db.property' => 0, 'db.protect' => 5, 'db.pubkey' => 0, 'db.ref' => 0, 'db.refhist' => 0, 'db.remote' => 1, 'db.repo' => 0, 'db.repoview' => 1, 'db.resolve' => 1, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 2, 'db.server' => 2, 'db.stash' => 0, 'db.stream' => 1, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 0, 'db.templatewx' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingx' => 10 }, 41 => { 'db.archmap' => 0, 'db.bodresolve' => 0, 'db.bodtext' => 1, 'db.bodtextcx' => 0, 'db.bodtexthx' => 0, 'db.bodtextsx' => 0, 'db.bodtextwx' => 0, 'db.change' => 4, 'db.changeidx' => 1, 'db.changex' => 4, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 6, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.group' => 9, 'db.groupx' => 0, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.ixtexthx' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.property' => 0, 'db.protect' => 4, 'db.remote' => 1, 'db.resolve' => 1, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 1, 'db.sendq' => 2, 'db.server' => 2, 'db.stash' => 0, 'db.stream' => 1, 'db.svrview' => 0, 'db.template' => 2, 'db.templatesx' => 0, 'db.templatewx' => 0, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingx' => 10 }, 40 => { 'db.archmap' => 0, 'db.bodtext' => 1, 'db.change' => 3, 'db.changeidx' => 1, 'db.changex' => 3, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 6, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.group' => 8, 'db.groupx' => 0, 'db.have' => 3, 'db.have.pt' => 3, 'db.have.rp' => 3, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.ldap' => 0, 'db.locks' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.property' => 0, 'db.protect' => 4, 'db.remote' => 1, 'db.resolve' => 1, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 0, 'db.sendq' => 2, 'db.server' => 2, 'db.stash' => 0, 'db.stream' => 1, 'db.svrview' => 0, 'db.template' => 2, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingx' => 10 }, 39 => { 'db.archmap' => 0, 'db.bodtext' => 1, 'db.change' => 2, 'db.changex' => 2, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 6, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.group' => 8, 'db.groupx' => 0, 'db.have' => 3, 'db.have.rp' => 3, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.locks' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.property' => 0, 'db.protect' => 4, 'db.remote' => 0, 'db.resolve' => 1, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.rmtview' => 0, 'db.sendq' => 2, 'db.server' => 2, 'db.stash' => 0, 'db.stream' => 1, 'db.svrview' => 0, 'db.template' => 1, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingx' => 10 }, 38 => { 'db.archmap' => 0, 'db.bodtext' => 1, 'db.change' => 2, 'db.changex' => 2, 'db.config' => 1, 'db.counters' => 1, 'db.depot' => 1, 'db.desc' => 0, 'db.domain' => 6, 'db.excl' => 1, 'db.fix' => 1, 'db.fixrev' => 1, 'db.group' => 8, 'db.have' => 2, 'db.have.rp' => 2, 'db.integ' => 0, 'db.integed' => 0, 'db.integtx' => 0, 'db.ixtext' => 0, 'db.jnlack' => 0, 'db.job' => 0, 'db.label' => 0, 'db.locks' => 2, 'db.logger' => 0, 'db.message' => 0, 'db.monitor' => 2, 'db.nameval' => 1, 'db.property' => 0, 'db.protect' => 4, 'db.resolve' => 1, 'db.resolvex' => 1, 'db.rev' => 9, 'db.revbx' => 9, 'db.revcx' => 0, 'db.revdx' => 9, 'db.revhx' => 9, 'db.review' => 1, 'db.revpx' => 9, 'db.revsh' => 9, 'db.revsx' => 9, 'db.revtx' => 9, 'db.revux' => 9, 'db.sendq' => 2, 'db.server' => 1, 'db.stream' => 1, 'db.svrview' => 0, 'db.template' => 1, 'db.traits' => 0, 'db.trigger' => 3, 'db.user' => 7, 'db.user.rp' => 7, 'db.uxtext' => 0, 'db.view' => 1, 'db.view.rp' => 1, 'db.working' => 10, 'db.workingx' => 10 } }, SVR_VERSION => 57, 'db.archive' => { 2 => { tab_count => 4, keyAttrs => 4, name0 => 'ARafile', type0 => 'key', key0 => 'yes', name1 => 'ARarev', type1 => 'key', key1 => 'yes', name2 => 'ARdfile', type2 => 'key', key2 => 'yes', name3 => 'ARrev', type3 => 'intv', key3 => 'yes', name4 => 'ARatype', type4 => 'int' }, 1 => { tab_count => 4, keyAttrs => 4, name0 => 'ARafile', type0 => 'key', key0 => 'yes', name1 => 'ARarev', type1 => 'key', key1 => 'yes', name2 => 'ARdfile', type2 => 'key', key2 => 'yes', name3 => 'ARrev', type3 => 'intv', key3 => 'yes', name4 => 'ARatype', type4 => 'int' }, 0 => { tab_count => 4, keyAttrs => 4, name0 => 'ARafile', type0 => 'key', key0 => 'yes', name1 => 'ARarev', type1 => 'key', key1 => 'yes', name2 => 'ARdfile', type2 => 'key', key2 => 'yes', name3 => 'ARrev', type3 => 'intv', key3 => 'yes', name4 => 'ARatype', type4 => 'int' } }, 'db.archmap' => { 0 => { tab_count => 1, keyAttrs => 2, name0 => 'AMafile', type0 => 'key', key0 => 'yes', name1 => 'AMdfile', type1 => 'key', key1 => 'yes' } }, 'db.boddate' => { 1 => { tab_count => 2, keyAttrs => 2, name0 => 'BDkey', type0 => 'key', key0 => 'yes', name1 => 'BDattr', type1 => 'int', key1 => 'yes', name2 => 'BDdate', type2 => 'intv' }, 0 => { tab_count => 2, keyAttrs => 2, name0 => 'BDkey', type0 => 'key', key0 => 'yes', name1 => 'BDattr', type1 => 'int', key1 => 'yes', name2 => 'BDdate', type2 => 'intv' } }, 'db.bodresolve' => { 2 => { tab_count => 9, keyAttrs => 6, name0 => 'BRtype', type0 => 'int8', key0 => 'yes', name1 => 'BRclient', type1 => 'key', key1 => 'yes', name2 => 'BRtokey', type2 => 'key', key2 => 'yes', name3 => 'BRattr', type3 => 'int', key3 => 'yes', name4 => 'BRfromkey', type4 => 'key', key4 => 'yes', name5 => 'BRfromchange', type5 => 'intv', key5 => 'yes', name6 => 'BRbasekey', type6 => 'key', name7 => 'BRbasechange', type7 => 'intv', name8 => 'BRhow', type8 => 'int8', name9 => 'BRstate', type9 => 'int' }, 1 => { tab_count => 9, keyAttrs => 6, name0 => 'BRtype', type0 => 'int8', key0 => 'yes', name1 => 'BRclient', type1 => 'key', key1 => 'yes', name2 => 'BRtokey', type2 => 'key', key2 => 'yes', name3 => 'BRattr', type3 => 'int', key3 => 'yes', name4 => 'BRfromkey', type4 => 'key', key4 => 'yes', name5 => 'BRfromchange', type5 => 'intv', key5 => 'yes', name6 => 'BRbasekey', type6 => 'key', name7 => 'BRbasechange', type7 => 'intv', name8 => 'BRhow', type8 => 'int8', name9 => 'BRstate', type9 => 'int' }, 0 => { tab_count => 7, keyAttrs => 6, name0 => 'BRtype', type0 => 'int8', key0 => 'yes', name1 => 'BRclient', type1 => 'key', key1 => 'yes', name2 => 'BRtokey', type2 => 'key', key2 => 'yes', name3 => 'BRattr', type3 => 'int', key3 => 'yes', name4 => 'BRfromkey', type4 => 'key', key4 => 'yes', name5 => 'BRfromchange', type5 => 'intv', key5 => 'yes', name6 => 'BRbasekey', type6 => 'key', name7 => 'BRbasechange', type7 => 'intv' } }, 'db.bodresolvex' => { 1 => { tab_count => 10, keyAttrs => 6, name0 => 'BXtype', type0 => 'int8', key0 => 'yes', name1 => 'BXshelf', type1 => 'intv', key1 => 'yes', name2 => 'BXtokey', type2 => 'key', key2 => 'yes', name3 => 'BXattr', type3 => 'int', key3 => 'yes', name4 => 'BXfromkey', type4 => 'key', key4 => 'yes', name5 => 'BXfromchange', type5 => 'intv', key5 => 'yes', name6 => 'BXbasekey', type6 => 'key', name7 => 'BXbasechange', type7 => 'intv', name8 => 'BXhow', type8 => 'int8', name9 => 'BXstate', type9 => 'int', name10 => 'BXclient', type10 => 'key' }, 0 => { tab_count => 10, keyAttrs => 6, name0 => 'BXtype', type0 => 'int8', key0 => 'yes', name1 => 'BXshelf', type1 => 'intv', key1 => 'yes', name2 => 'BXtokey', type2 => 'key', key2 => 'yes', name3 => 'BXattr', type3 => 'int', key3 => 'yes', name4 => 'BXfromkey', type4 => 'key', key4 => 'yes', name5 => 'BXfromchange', type5 => 'intv', key5 => 'yes', name6 => 'BXbasekey', type6 => 'key', name7 => 'BXbasechange', type7 => 'intv', name8 => 'BXhow', type8 => 'int8', name9 => 'BXstate', type9 => 'int', name10 => 'BXclient', type10 => 'key' } }, 'db.bodtext' => { 1 => { tab_count => 3, keyAttrs => 2, name0 => 'BTkey', type0 => 'key', key0 => 'yes', name1 => 'BTattr', type1 => 'int', key1 => 'yes', name2 => 'BTbulk', type2 => 'int', name3 => 'BTtext', type3 => 'text' }, 0 => { tab_count => 2, keyAttrs => 2, name0 => 'BTkey', type0 => 'key', key0 => 'yes', name1 => 'BTattr', type1 => 'int', key1 => 'yes', name2 => 'BTtext', type2 => 'text' } }, 'db.bodtextcx' => { 1 => { tab_count => 4, keyAttrs => 4, name0 => 'BCtype', type0 => 'int8', key0 => 'yes', name1 => 'BCkey', type1 => 'key', key1 => 'yes', name2 => 'BCchange', type2 => 'intv', key2 => 'yes', name3 => 'BCattr', type3 => 'int', key3 => 'yes', name4 => 'BCtext', type4 => 'text' }, 0 => { tab_count => 4, keyAttrs => 4, name0 => 'BCtype', type0 => 'int8', key0 => 'yes', name1 => 'BCkey', type1 => 'key', key1 => 'yes', name2 => 'BCchange', type2 => 'intv', key2 => 'yes', name3 => 'BCattr', type3 => 'int', key3 => 'yes', name4 => 'BCtext', type4 => 'text' } }, 'db.bodtexthx' => { 0 => { tab_count => 4, keyAttrs => 3, name0 => 'BHtype', type0 => 'int8', key0 => 'yes', name1 => 'BHkey', type1 => 'key', key1 => 'yes', name2 => 'BHattr', type2 => 'int', key2 => 'yes', name3 => 'BHbulk', type3 => 'int', name4 => 'BHtext', type4 => 'text' } }, 'db.bodtextsx' => { 2 => { tab_count => 7, keyAttrs => 4, name0 => 'BStype', type0 => 'int8', key0 => 'yes', name1 => 'BSshelf', type1 => 'intv', key1 => 'yes', name2 => 'BSkey', type2 => 'key', key2 => 'yes', name3 => 'BSattr', type3 => 'int', key3 => 'yes', name4 => 'BStext', type4 => 'text', name5 => 'BSworkchange', type5 => 'intv', name6 => 'BSuser', type6 => 'key', name7 => 'BSaction', type7 => 'int8' }, 1 => { tab_count => 7, keyAttrs => 4, name0 => 'BStype', type0 => 'int8', key0 => 'yes', name1 => 'BSshelf', type1 => 'intv', key1 => 'yes', name2 => 'BSkey', type2 => 'key', key2 => 'yes', name3 => 'BSattr', type3 => 'int', key3 => 'yes', name4 => 'BStext', type4 => 'text', name5 => 'BSworkchange', type5 => 'intv', name6 => 'BSuser', type6 => 'key', name7 => 'BSaction', type7 => 'int8' }, 0 => { tab_count => 5, keyAttrs => 4, name0 => 'BStype', type0 => 'int8', key0 => 'yes', name1 => 'BSshelf', type1 => 'intv', key1 => 'yes', name2 => 'BSkey', type2 => 'key', key2 => 'yes', name3 => 'BSattr', type3 => 'int', key3 => 'yes', name4 => 'BStext', type4 => 'text', name5 => 'BSworkchange', type5 => 'intv' } }, 'db.bodtextwx' => { 2 => { tab_count => 7, keyAttrs => 4, name0 => 'BWtype', type0 => 'int8', key0 => 'yes', name1 => 'BWclient', type1 => 'key', key1 => 'yes', name2 => 'BWkey', type2 => 'key', key2 => 'yes', name3 => 'BWattr', type3 => 'int', key3 => 'yes', name4 => 'BWtext', type4 => 'text', name5 => 'BWworkchange', type5 => 'intv', name6 => 'BWuser', type6 => 'key', name7 => 'BWaction', type7 => 'int8' }, 1 => { tab_count => 7, keyAttrs => 4, name0 => 'BWtype', type0 => 'int8', key0 => 'yes', name1 => 'BWclient', type1 => 'key', key1 => 'yes', name2 => 'BWkey', type2 => 'key', key2 => 'yes', name3 => 'BWattr', type3 => 'int', key3 => 'yes', name4 => 'BWtext', type4 => 'text', name5 => 'BWworkchange', type5 => 'intv', name6 => 'BWuser', type6 => 'key', name7 => 'BWaction', type7 => 'int8' }, 0 => { tab_count => 5, keyAttrs => 4, name0 => 'BWtype', type0 => 'int8', key0 => 'yes', name1 => 'BWclient', type1 => 'key', key1 => 'yes', name2 => 'BWkey', type2 => 'key', key2 => 'yes', name3 => 'BWattr', type3 => 'int', key3 => 'yes', name4 => 'BWtext', type4 => 'text', name5 => 'BWworkchange', type5 => 'intv' } }, 'db.change' => { 7 => { tab_count => 12, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv', name11 => 'CHupdate', type11 => 'intv', name12 => 'CHstream', type12 => 'key' }, 6 => { tab_count => 12, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv', name11 => 'CHupdate', type11 => 'intv', name12 => 'CHstream', type12 => 'key' }, 5 => { tab_count => 11, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv', name11 => 'CHupdate', type11 => 'intv' }, 4 => { tab_count => 10, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv' }, 3 => { tab_count => 9, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key' }, 2 => { tab_count => 7, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key' }, 1 => { tab_count => 7, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key' }, 0 => { tab_count => 6, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text' } }, 'db.changeidx' => { 2 => { tab_count => 1, keyAttrs => 1, name0 => 'CDidentity', type0 => 'key', key0 => 'yes', name1 => 'CDchange', type1 => 'intv' }, 1 => { tab_count => 1, keyAttrs => 1, name0 => 'CDidentity', type0 => 'key', key0 => 'yes', name1 => 'CDchange', type1 => 'intv' }, 0 => { tab_count => 0, name0 => undef, type0 => 'MISSING' } }, 'db.changex' => { 7 => { tab_count => 12, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv', name11 => 'CHupdate', type11 => 'intv', name12 => 'CHstream', type12 => 'key' }, 6 => { tab_count => 12, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv', name11 => 'CHupdate', type11 => 'intv', name12 => 'CHstream', type12 => 'key' }, 5 => { tab_count => 11, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv', name11 => 'CHupdate', type11 => 'intv' }, 4 => { tab_count => 10, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key', name10 => 'CHaccess', type10 => 'intv' }, 3 => { tab_count => 9, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key', name8 => 'CHimporter', type8 => 'key', name9 => 'CHidentity', type9 => 'key' }, 2 => { tab_count => 7, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key' }, 1 => { tab_count => 7, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text', name7 => 'CHroot', type7 => 'key' }, 0 => { tab_count => 6, keyAttrs => 1, name0 => 'CHchange', type0 => 'intv', key0 => 'yes', name1 => 'CHkey', type1 => 'intv', name2 => 'CHclient', type2 => 'key', name3 => 'CHuser', type3 => 'key', name4 => 'CHdate', type4 => 'intv', name5 => 'CHstatus', type5 => 'int8', name6 => 'CHdesc', type6 => 'text' } }, 'db.config' => { 1 => { tab_count => 2, keyAttrs => 2, name0 => 'CFsname', type0 => 'key', key0 => 'yes', name1 => 'CFname', type1 => 'key', key1 => 'yes', name2 => 'CFvalue', type2 => 'text' }, 0 => { tab_count => 0, name0 => undef, type0 => 'MISSING' } }, 'db.configh' => { 1 => { tab_count => 8, keyAttrs => 5, name0 => 'CXsname', type0 => 'key', key0 => 'yes', name1 => 'CXname', type1 => 'key', key1 => 'yes', name2 => 'CXcount', type2 => 'int', key2 => 'yes', name3 => 'CXdate', type3 => 'intv', key3 => 'yes', name4 => 'CXserver', type4 => 'key', key4 => 'yes', name5 => 'CXuser', type5 => 'key', name6 => 'CXovalue', type6 => 'text', name7 => 'CXnvalue', type7 => 'text', name8 => 'CXcomment', type8 => 'text' }, 0 => { tab_count => 7, keyAttrs => 5, name0 => 'CXsname', type0 => 'key', key0 => 'yes', name1 => 'CXname', type1 => 'key', key1 => 'yes', name2 => 'CXcount', type2 => 'int', key2 => 'yes', name3 => 'CXdate', type3 => 'intv', key3 => 'yes', name4 => 'CXserver', type4 => 'key', key4 => 'yes', name5 => 'CXuser', type5 => 'key', name6 => 'CXovalue', type6 => 'text', name7 => 'CXnvalue', type7 => 'text' } }, 'db.counters' => { 1 => { tab_count => 1, keyAttrs => 1, name0 => 'COname', type0 => 'key', key0 => 'yes', name1 => 'COvalue', type1 => 'text' }, 0 => { tab_count => 1, keyAttrs => 1, name0 => 'COname', type0 => 'key', key0 => 'yes', name1 => 'COival', type1 => 'int' } }, 'db.depot' => { 2 => { tab_count => 4, keyAttrs => 1, name0 => 'DPname', type0 => 'key', key0 => 'yes', name1 => 'DPtype', type1 => 'int', name2 => 'DPextra', type2 => 'text', name3 => 'DPmap', type3 => 'text', name4 => 'DPobjaddr', type4 => 'text' }, 1 => { tab_count => 3, keyAttrs => 1, name0 => 'DPname', type0 => 'key', key0 => 'yes', name1 => 'DPtype', type1 => 'int', name2 => 'DPextra', type2 => 'text', name3 => 'DPmap', type3 => 'text' }, 0 => { tab_count => 3, keyAttrs => 1, name0 => 'DPname', type0 => 'key', key0 => 'yes', name1 => 'DPtype', type1 => 'int', name2 => 'DPextra', type2 => 'text', name3 => 'DPmap', type3 => 'text' } }, 'db.desc' => { 1 => { tab_count => 1, keyAttrs => 1, name0 => 'DEkey', type0 => 'intv', key0 => 'yes', name1 => 'DEdesc', type1 => 'text' }, 0 => { tab_count => 1, keyAttrs => 1, name0 => 'DEkey', type0 => 'intv', key0 => 'yes', name1 => 'DEdesc', type1 => 'text' } }, 'db.domain' => { 8 => { tab_count => 13, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOmount2', type4 => 'text', name5 => 'DOmount3', type5 => 'text', name6 => 'DOowner', type6 => 'key', name7 => 'DOupdate', type7 => 'intv', name8 => 'DOaccess', type8 => 'intv', name9 => 'DOoptions', type9 => 'int', name10 => 'DOdesc', type10 => 'text', name11 => 'DOstream', type11 => 'key', name12 => 'DOserverid', type12 => 'key', name13 => 'DOcontents', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOmount2', type4 => 'text', name5 => 'DOmount3', type5 => 'text', name6 => 'DOowner', type6 => 'key', name7 => 'DOupdate', type7 => 'intv', name8 => 'DOaccess', type8 => 'intv', name9 => 'DOoptions', type9 => 'int', name10 => 'DOdesc', type10 => 'text', name11 => 'DOstream', type11 => 'key', name12 => 'DOserverid', type12 => 'key', name13 => 'DOcontents', type13 => 'int' }, 6 => { tab_count => 13, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOmount2', type4 => 'text', name5 => 'DOmount3', type5 => 'text', name6 => 'DOowner', type6 => 'key', name7 => 'DOupdate', type7 => 'intv', name8 => 'DOaccess', type8 => 'intv', name9 => 'DOoptions', type9 => 'int', name10 => 'DOdesc', type10 => 'text', name11 => 'DOstream', type11 => 'key', name12 => 'DOserverid', type12 => 'key', name13 => 'DOpartition', type13 => 'int' }, 5 => { tab_count => 11, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOmount2', type4 => 'text', name5 => 'DOmount3', type5 => 'text', name6 => 'DOowner', type6 => 'key', name7 => 'DOupdate', type7 => 'intv', name8 => 'DOaccess', type8 => 'intv', name9 => 'DOoptions', type9 => 'int', name10 => 'DOdesc', type10 => 'text', name11 => 'DOstream', type11 => 'text' }, 4 => { tab_count => 10, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOmount2', type4 => 'text', name5 => 'DOmount3', type5 => 'text', name6 => 'DOowner', type6 => 'key', name7 => 'DOupdate', type7 => 'intv', name8 => 'DOaccess', type8 => 'intv', name9 => 'DOoptions', type9 => 'int', name10 => 'DOdesc', type10 => 'text' }, 3 => { tab_count => 11, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOmount2', type4 => 'text', name5 => 'DOmount3', type5 => 'text', name6 => 'DOowner', type6 => 'key', name7 => 'DOupdate', type7 => 'intv', name8 => 'DOaccess', type8 => 'intv', name9 => 'DOoptions', type9 => 'int', name10 => 'DOmapstate', type10 => 'int', name11 => 'DOdesc', type11 => 'text' }, 2 => { tab_count => 9, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOowner', type4 => 'key', name5 => 'DOupdate', type5 => 'intv', name6 => 'DOaccess', type6 => 'intv', name7 => 'DOoptions', type7 => 'int', name8 => 'DOmapstate', type8 => 'int', name9 => 'DOdesc', type9 => 'text' }, 1 => { tab_count => 8, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOowner', type4 => 'key', name5 => 'DOupdate', type5 => 'intv', name6 => 'DOaccess', type6 => 'intv', name7 => 'DOoptions', type7 => 'int', name8 => 'DOdesc', type8 => 'text' }, 0 => { tab_count => 7, keyAttrs => 1, name0 => 'DOname', type0 => 'key', key0 => 'yes', name1 => 'DOtype', type1 => 'int8', name2 => 'DOextra', type2 => 'text', name3 => 'DOmount', type3 => 'text', name4 => 'DOowner', type4 => 'key', name5 => 'DOupdate', type5 => 'intv', name6 => 'DOoptions', type6 => 'int', name7 => 'DOdesc', type7 => 'key' } }, 'db.excl' => { 1 => { tab_count => 2, keyAttrs => 1, name0 => 'EXdfile', type0 => 'key', key0 => 'yes', name1 => 'EXclient', type1 => 'key', name2 => 'EXuser', type2 => 'key' }, 0 => { tab_count => 0, name0 => undef, type0 => 'MISSING' } }, 'db.exclg' => { 0 => { tab_count => 5, keyAttrs => 3, name0 => 'GLrepo', type0 => 'key', key0 => 'yes', name1 => 'GLref', type1 => 'text', key1 => 'yes', name2 => 'GLdfile', type2 => 'key', key2 => 'yes', name3 => 'GLlockid', type3 => 'text', name4 => 'GLuser', type4 => 'key', name5 => 'GLcreated', type5 => 'text' } }, 'db.exclgx' => { 0 => { tab_count => 5, keyAttrs => 1, name0 => 'GKlockid', type0 => 'text', key0 => 'yes', name1 => 'GKrepo', type1 => 'key', name2 => 'GKref', type2 => 'text', name3 => 'GKdfile', type3 => 'key', name4 => 'GKuser', type4 => 'key', name5 => 'GKcreated', type5 => 'text' } }, 'db.fix' => { 2 => { tab_count => 5, keyAttrs => 2, name0 => 'FXjob', type0 => 'key', key0 => 'yes', name1 => 'FXchange', type1 => 'intv', key1 => 'yes', name2 => 'FXdate', type2 => 'intv', name3 => 'FXstatus', type3 => 'text', name4 => 'FXclient', type4 => 'key', name5 => 'FXuser', type5 => 'key' }, 1 => { tab_count => 5, keyAttrs => 2, name0 => 'FXjob', type0 => 'key', key0 => 'yes', name1 => 'FXchange', type1 => 'intv', key1 => 'yes', name2 => 'FXdate', type2 => 'intv', name3 => 'FXstatus', type3 => 'text', name4 => 'FXclient', type4 => 'key', name5 => 'FXuser', type5 => 'key' }, 0 => { tab_count => 5, keyAttrs => 2, name0 => 'FXjob', type0 => 'key', key0 => 'yes', name1 => 'FXchange', type1 => 'intv', key1 => 'yes', name2 => 'FXdate', type2 => 'intv', name3 => 'FXstat992', type3 => 'int8', name4 => 'FXclient', type4 => 'key', name5 => 'FXuser', type5 => 'key' } }, 'db.fixrev' => { 2 => { tab_count => 5, keyAttrs => 2, name0 => 'FXjob', type0 => 'key', key0 => 'yes', name1 => 'FXchange', type1 => 'intv', key1 => 'yes', name2 => 'FXdate', type2 => 'intv', name3 => 'FXstatus', type3 => 'text', name4 => 'FXclient', type4 => 'key', name5 => 'FXuser', type5 => 'key' }, 1 => { tab_count => 5, keyAttrs => 2, name0 => 'FXjob', type0 => 'key', key0 => 'yes', name1 => 'FXchange', type1 => 'intv', key1 => 'yes', name2 => 'FXdate', type2 => 'intv', name3 => 'FXstatus', type3 => 'text', name4 => 'FXclient', type4 => 'key', name5 => 'FXuser', type5 => 'key' }, 0 => { tab_count => 5, keyAttrs => 2, name0 => 'FXjob', type0 => 'key', key0 => 'yes', name1 => 'FXchange', type1 => 'intv', key1 => 'yes', name2 => 'FXdate', type2 => 'intv', name3 => 'FXstat992', type3 => 'int8', name4 => 'FXclient', type4 => 'key', name5 => 'FXuser', type5 => 'key' } }, 'db.graphindex' => { 2 => { tab_count => 8, keyAttrs => 5, name0 => 'GIid', type0 => 'int', key0 => 'yes', name1 => 'GIname', type1 => 'key', key1 => 'yes', name2 => 'GIdate', type2 => 'intv', key2 => 'yes', name3 => 'GIblobsha', type3 => 'octet20', key3 => 'yes', name4 => 'GIcommitsha', type4 => 'octet20', key4 => 'yes', name5 => 'GIflags', type5 => 'int', name6 => 'GIsize', type6 => 'int64', name7 => 'GItype', type7 => 'int', name8 => 'GIlfsoid', type8 => 'text' }, 1 => { tab_count => 8, keyAttrs => 5, name0 => 'GIid', type0 => 'int', key0 => 'yes', name1 => 'GIname', type1 => 'key', key1 => 'yes', name2 => 'GIdate', type2 => 'intv', key2 => 'yes', name3 => 'GIblobsha', type3 => 'octet20', key3 => 'yes', name4 => 'GIcommitsha', type4 => 'octet20', key4 => 'yes', name5 => 'GIflags', type5 => 'int', name6 => 'GIsize', type6 => 'int64', name7 => 'GItype', type7 => 'int', name8 => 'GIlfsoid', type8 => 'text' }, 0 => { tab_count => 5, keyAttrs => 5, name0 => 'GIid', type0 => 'int', key0 => 'yes', name1 => 'GIname', type1 => 'key', key1 => 'yes', name2 => 'GIdate', type2 => 'intv', key2 => 'yes', name3 => 'GIblobsha', type3 => 'octet20', key3 => 'yes', name4 => 'GIcommitsha', type4 => 'octet20', key4 => 'yes', name5 => 'GIflags', type5 => 'int' } }, 'db.graphperm' => { 0 => { tab_count => 5, keyAttrs => 6, name0 => 'GPname', type0 => 'key', key0 => 'yes', name1 => 'GPrepo', type1 => 'key', key1 => 'yes', name2 => 'GPref', type2 => 'key', key2 => 'yes', name3 => 'GPtype', type3 => 'int', key3 => 'yes', name4 => 'GPuser', type4 => 'key', key4 => 'yes', name5 => 'GPperm', type5 => 'int', key5 => 'yes' } }, 'db.group' => { 10 => { tab_count => 9, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int', name5 => 'GRmaxl', type5 => 'int', name6 => 'GRmaxf', type6 => 'int', name7 => 'GRtimeout', type7 => 'int', name8 => 'GRpasstimeout', type8 => 'int', name9 => 'GRmaxmem', type9 => 'int' }, 9 => { tab_count => 8, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int', name5 => 'GRmaxl', type5 => 'int', name6 => 'GRmaxf', type6 => 'int', name7 => 'GRtimeout', type7 => 'int', name8 => 'GRpasstimeout', type8 => 'int' }, 8 => { tab_count => 7, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int', name5 => 'GRmaxl', type5 => 'int', name6 => 'GRtimeout', type6 => 'int', name7 => 'GRpasstimeout', type7 => 'int' }, 7 => { tab_count => 6, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int', name5 => 'GRmaxl', type5 => 'int', name6 => 'GRtimeout', type6 => 'int' }, 6 => { tab_count => 6, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int', name5 => 'GRmaxl', type5 => 'int', name6 => 'GRtimeout', type6 => 'int' }, 5 => { tab_count => 6, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int', name5 => 'GRmaxl', type5 => 'int', name6 => 'GRtimeout', type6 => 'int' }, 4 => { tab_count => 5, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int', name5 => 'GRtimeout', type5 => 'int' }, 3 => { tab_count => 4, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int', name4 => 'GRmaxs', type4 => 'int' }, 2 => { tab_count => 3, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRtype', type2 => 'int', name3 => 'GRmaxr', type3 => 'int' }, 1 => { tab_count => 2, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes', name2 => 'GRmaxr', type2 => 'int' }, 0 => { tab_count => 1, keyAttrs => 2, name0 => 'GRuser', type0 => 'key', key0 => 'yes', name1 => 'GRgroup', type1 => 'key', key1 => 'yes' } }, 'db.groupx' => { 2 => { tab_count => 5, keyAttrs => 1, name0 => 'GXgroup', type0 => 'key', key0 => 'yes', name1 => 'GXlconf', type1 => 'text', name2 => 'GXlgrpqry', type2 => 'text', name3 => 'GXluidattr', type3 => 'text', name4 => 'GXludnattr', type4 => 'text', name5 => 'GXdesc', type5 => 'text' }, 1 => { tab_count => 4, keyAttrs => 1, name0 => 'GXgroup', type0 => 'key', key0 => 'yes', name1 => 'GXlconf', type1 => 'text', name2 => 'GXlgrpqry', type2 => 'text', name3 => 'GXluidattr', type3 => 'text', name4 => 'GXludnattr', type4 => 'text' }, 0 => { tab_count => 3, keyAttrs => 1, name0 => 'GXgroup', type0 => 'key', key0 => 'yes', name1 => 'GXlconf', type1 => 'text', name2 => 'GXlgrpqry', type2 => 'text', name3 => 'GXluidattr', type3 => 'text' } }, 'db.have' => { 4 => { tab_count => 4, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int', name4 => 'HAtime', type4 => 'intv' }, 3 => { tab_count => 4, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int', name4 => 'HAtime', type4 => 'intv' }, 2 => { tab_count => 3, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int' }, 1 => { tab_count => 3, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int' }, 0 => { tab_count => 2, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv' } }, 'db.have.pt' => { 4 => { tab_count => 4, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int', name4 => 'HAtime', type4 => 'intv' }, 3 => { tab_count => 4, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int', name4 => 'HAtime', type4 => 'intv' }, 2 => { tab_count => 3, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int' }, 1 => { tab_count => 3, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int' }, 0 => { tab_count => 2, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv' } }, 'db.have.rp' => { 4 => { tab_count => 4, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int', name4 => 'HAtime', type4 => 'intv' }, 3 => { tab_count => 4, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int', name4 => 'HAtime', type4 => 'intv' }, 2 => { tab_count => 3, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int' }, 1 => { tab_count => 3, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv', name3 => 'HAtype', type3 => 'int' }, 0 => { tab_count => 2, keyAttrs => 1, name0 => 'HAcfile', type0 => 'key', key0 => 'yes', name1 => 'HAdfile', type1 => 'key', name2 => 'HArev', type2 => 'intv' } }, 'db.haveg' => { 0 => { tab_count => 8, keyAttrs => 2, name0 => 'GFrepo', type0 => 'key', key0 => 'yes', name1 => 'GFcfile', type1 => 'key', key1 => 'yes', name2 => 'GFdfile', type2 => 'key', name3 => 'GFclient', type3 => 'key', name4 => 'GFtype', type4 => 'int', name5 => 'GFaction', type5 => 'int8', name6 => 'GFbsha', type6 => 'octet20', name7 => 'GFcommitsha', type7 => 'octet20', name8 => 'GFflags', type8 => 'int' } }, 'db.haveview' => { 2 => { tab_count => 5, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key', name5 => 'VIctype', type5 => 'int' }, 1 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' } }, 'db.integ' => { 1 => { tab_count => 8, keyAttrs => 3, name0 => 'INtfile', type0 => 'key', key0 => 'yes', name1 => 'INffile', type1 => 'key', key1 => 'yes', name2 => 'INsfrev', type2 => 'intv', key2 => 'yes', name3 => 'INefrev', type3 => 'intv', name4 => 'INtrev', type4 => 'intv', name5 => 'INhow', type5 => 'int8', name6 => 'INcomm', type6 => 'int', name7 => 'INres', type7 => 'int', name8 => 'INchange', type8 => 'intv' }, 0 => { tab_count => 8, keyAttrs => 3, name0 => 'INtfile', type0 => 'key', key0 => 'yes', name1 => 'INffile', type1 => 'key', key1 => 'yes', name2 => 'INsfrev', type2 => 'intv', key2 => 'yes', name3 => 'INefrev', type3 => 'intv', name4 => 'INtrev', type4 => 'intv', name5 => 'INhow', type5 => 'int8', name6 => 'INcomm', type6 => 'int', name7 => 'INres', type7 => 'int', name8 => 'INchange', type8 => 'intv' } }, 'db.integed' => { 1 => { tab_count => 7, keyAttrs => 6, name0 => 'INtfile', type0 => 'key', key0 => 'yes', name1 => 'INffile', type1 => 'key', key1 => 'yes', name2 => 'INsfrev', type2 => 'intv', key2 => 'yes', name3 => 'INefrev', type3 => 'intv', key3 => 'yes', name4 => 'INstrev', type4 => 'intv', key4 => 'yes', name5 => 'INetrev', type5 => 'intv', key5 => 'yes', name6 => 'INhow', type6 => 'int8', name7 => 'INchange', type7 => 'intv' }, 0 => { tab_count => 7, keyAttrs => 6, name0 => 'INtfile', type0 => 'key', key0 => 'yes', name1 => 'INffile', type1 => 'key', key1 => 'yes', name2 => 'INsfrev', type2 => 'intv', key2 => 'yes', name3 => 'INefrev', type3 => 'intv', key3 => 'yes', name4 => 'INstrev', type4 => 'intv', key4 => 'yes', name5 => 'INetrev', type5 => 'intv', key5 => 'yes', name6 => 'INhow', type6 => 'int8', name7 => 'INchange', type7 => 'intv' } }, 'db.integedss' => { 3 => { tab_count => 10, keyAttrs => 5, name0 => 'SItokey', type0 => 'key', key0 => 'yes', name1 => 'SIattr', type1 => 'int', key1 => 'yes', name2 => 'SIfromkey', type2 => 'key', key2 => 'yes', name3 => 'SIendfromchange', type3 => 'intv', key3 => 'yes', name4 => 'SIendtochange', type4 => 'intv', key4 => 'yes', name5 => 'SIstartfromchange', type5 => 'intv', name6 => 'SIstarttochange', type6 => 'intv', name7 => 'SIbasekey', type7 => 'key', name8 => 'SIbasechange', type8 => 'intv', name9 => 'SIhow', type9 => 'int8', name10 => 'SIchange', type10 => 'intv' }, 2 => { tab_count => 10, keyAttrs => 5, name0 => 'SItokey', type0 => 'key', key0 => 'yes', name1 => 'SIattr', type1 => 'int', key1 => 'yes', name2 => 'SIfromkey', type2 => 'key', key2 => 'yes', name3 => 'SIendfromchange', type3 => 'intv', key3 => 'yes', name4 => 'SIendtochange', type4 => 'intv', key4 => 'yes', name5 => 'SIstartfromchange', type5 => 'intv', name6 => 'SIstarttochange', type6 => 'intv', name7 => 'SIbasekey', type7 => 'key', name8 => 'SIbasechange', type8 => 'intv', name9 => 'SIhow', type9 => 'int8', name10 => 'SIchange', type10 => 'intv' }, 1 => { tab_count => 10, keyAttrs => 5, name0 => 'SItokey', type0 => 'key', key0 => 'yes', name1 => 'SIattr', type1 => 'int', key1 => 'yes', name2 => 'SIfromkey', type2 => 'key', key2 => 'yes', name3 => 'SIendfromchange', type3 => 'intv', key3 => 'yes', name4 => 'SIstartfromchange', type4 => 'intv', key4 => 'yes', name5 => 'SIendtochange', type5 => 'intv', name6 => 'SIstarttochange', type6 => 'intv', name7 => 'SIbasekey', type7 => 'key', name8 => 'SIbasechange', type8 => 'intv', name9 => 'SIhow', type9 => 'int8', name10 => 'SIchange', type10 => 'intv' }, 0 => { tab_count => 7, keyAttrs => 5, name0 => 'SItokey', type0 => 'key', key0 => 'yes', name1 => 'SIattr', type1 => 'int', key1 => 'yes', name2 => 'SIfromkey', type2 => 'key', key2 => 'yes', name3 => 'SIendfromchange', type3 => 'intv', key3 => 'yes', name4 => 'SIbasekey', type4 => 'key', key4 => 'yes', name5 => 'SIbasechange', type5 => 'intv', name6 => 'SIhow', type6 => 'int8', name7 => 'SIchange', type7 => 'intv' } }, 'db.integtx' => { 1 => { tab_count => 7, keyAttrs => 6, name0 => 'INtfile', type0 => 'key', key0 => 'yes', name1 => 'INffile', type1 => 'key', key1 => 'yes', name2 => 'INsfrev', type2 => 'intv', key2 => 'yes', name3 => 'INefrev', type3 => 'intv', key3 => 'yes', name4 => 'INstrev', type4 => 'intv', key4 => 'yes', name5 => 'INetrev', type5 => 'intv', key5 => 'yes', name6 => 'INhow', type6 => 'int8', name7 => 'INchange', type7 => 'intv' }, 0 => { tab_count => 7, keyAttrs => 6, name0 => 'INtfile', type0 => 'key', key0 => 'yes', name1 => 'INffile', type1 => 'key', key1 => 'yes', name2 => 'INsfrev', type2 => 'intv', key2 => 'yes', name3 => 'INefrev', type3 => 'intv', key3 => 'yes', name4 => 'INstrev', type4 => 'intv', key4 => 'yes', name5 => 'INetrev', type5 => 'intv', key5 => 'yes', name6 => 'INhow', type6 => 'int8', name7 => 'INchange', type7 => 'intv' } }, 'db.ixdate' => { 1 => { tab_count => 2, keyAttrs => 3, name0 => 'IDdate', type0 => 'intv', key0 => 'yes', name1 => 'IDattr', type1 => 'int', key1 => 'yes', name2 => 'IDvalue', type2 => 'key', key2 => 'yes' }, 0 => { tab_count => 2, keyAttrs => 3, name0 => 'IDdate', type0 => 'intv', key0 => 'yes', name1 => 'IDattr', type1 => 'int', key1 => 'yes', name2 => 'IDvalue', type2 => 'key', key2 => 'yes' } }, 'db.ixtext' => { 0 => { tab_count => 2, keyAttrs => 3, name0 => 'ITword', type0 => 'key', key0 => 'yes', name1 => 'ITattr', type1 => 'int', key1 => 'yes', name2 => 'ITvalue', type2 => 'key', key2 => 'yes' } }, 'db.ixtexthx' => { 0 => { tab_count => 3, keyAttrs => 4, name0 => 'IHtype', type0 => 'int8', key0 => 'yes', name1 => 'IHword', type1 => 'key', key1 => 'yes', name2 => 'IHattr', type2 => 'int', key2 => 'yes', name3 => 'IHvalue', type3 => 'key', key3 => 'yes' } }, 'db.jnlack' => { 2 => { tab_count => 10, keyAttrs => 1, name0 => 'JAid', type0 => 'key', key0 => 'yes', name1 => 'JAupdate', type1 => 'intv', name2 => 'JAservices', type2 => 'int64', name3 => 'JApjnum', type3 => 'int', name4 => 'JAajnum', type4 => 'int', name5 => 'JApjoff', type5 => 'int64', name6 => 'JAajoff', type6 => 'int64', name7 => 'JAjcflags', type7 => 'int', name8 => 'JAalive', type8 => 'int', name9 => 'JAsvropts', type9 => 'int', name10 => 'JAfoverSeen', type10 => 'text' }, 1 => { tab_count => 10, keyAttrs => 1, name0 => 'JAid', type0 => 'key', key0 => 'yes', name1 => 'JAupdate', type1 => 'intv', name2 => 'JAservices', type2 => 'int64', name3 => 'JApjnum', type3 => 'int', name4 => 'JAajnum', type4 => 'int', name5 => 'JApjoff', type5 => 'int64', name6 => 'JAajoff', type6 => 'int64', name7 => 'JAjcflags', type7 => 'int', name8 => 'JAalive', type8 => 'int', name9 => 'JAsvropts', type9 => 'int', name10 => 'JAfoverSeen', type10 => 'text' }, 0 => { tab_count => 8, keyAttrs => 1, name0 => 'JAid', type0 => 'key', key0 => 'yes', name1 => 'JAupdate', type1 => 'intv', name2 => 'JAservices', type2 => 'int64', name3 => 'JApjnum', type3 => 'int', name4 => 'JAajnum', type4 => 'int', name5 => 'JApjoff', type5 => 'int64', name6 => 'JAajoff', type6 => 'int64', name7 => 'JAjcflags', type7 => 'int', name8 => 'JAalive', type8 => 'int' } }, 'db.job' => { 1 => { tab_count => 4, keyAttrs => 1, name0 => 'JOjob', type0 => 'key', key0 => 'yes', name1 => 'JOuser', type1 => 'key', name2 => 'JOdate', type2 => 'intv', name3 => 'JOstatus', type3 => 'int8', name4 => 'JOdesc', type4 => 'text' }, 0 => { tab_count => 4, keyAttrs => 1, name0 => 'JOjob', type0 => 'key', key0 => 'yes', name1 => 'JOuser', type1 => 'key', name2 => 'JOdate', type2 => 'intv', name3 => 'JOstatus', type3 => 'int8', name4 => 'JOdesc', type4 => 'text' } }, 'db.jobdesc' => { 0 => { tab_count => 1, keyAttrs => 1, name0 => 'JDjob', type0 => 'key', key0 => 'yes', name1 => 'JDdesc', type1 => 'text' } }, 'db.jobpend' => { 1 => { tab_count => 4, keyAttrs => 2, name0 => 'JOjob', type0 => 'key', key0 => 'yes', name1 => 'JOuser', type1 => 'key', key1 => 'yes', name2 => 'JOdate', type2 => 'intv', name3 => 'JOstatus', type3 => 'int8', name4 => 'JOdesc', type4 => 'text' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'JOjob', type0 => 'key', key0 => 'yes', name1 => 'JOuser', type1 => 'key', key1 => 'yes', name2 => 'JOdate', type2 => 'intv', name3 => 'JOstatus', type3 => 'int8', name4 => 'JOdesc', type4 => 'text' } }, 'db.label' => { 1 => { tab_count => 2, keyAttrs => 2, name0 => 'LAname', type0 => 'key', key0 => 'yes', name1 => 'LAfile', type1 => 'key', key1 => 'yes', name2 => 'LArev', type2 => 'intv' }, 0 => { tab_count => 2, keyAttrs => 2, name0 => 'LAname', type0 => 'key', key0 => 'yes', name1 => 'LAfile', type1 => 'key', key1 => 'yes', name2 => 'LArev', type2 => 'intv' } }, 'db.ldap' => { 0 => { tab_count => 18, keyAttrs => 1, name0 => 'LDname', type0 => 'key', key0 => 'yes', name1 => 'LDhost', type1 => 'text', name2 => 'LDport', type2 => 'int', name3 => 'LDssl', type3 => 'int', name4 => 'LDtype', type4 => 'int', name5 => 'LDpattern', type5 => 'text', name6 => 'LDbasedn', type6 => 'text', name7 => 'LDfilter', type7 => 'text', name8 => 'LDscope', type8 => 'int', name9 => 'LDbinddn', type9 => 'text', name10 => 'LDbindpass', type10 => 'text', name11 => 'LDrealm', type11 => 'text', name12 => 'LDgrpbasedn', type12 => 'text', name13 => 'LDgrpfilter', type13 => 'text', name14 => 'LDgrpscope', type14 => 'int', name15 => 'LDoptions', type15 => 'int', name16 => 'LDattruid', type16 => 'text', name17 => 'LDattremail', type17 => 'text', name18 => 'LDattrname', type18 => 'text' } }, 'db.locks' => { 3 => { tab_count => 5, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOaction', type3 => 'int8', name4 => 'LOislocked', type4 => 'int', name5 => 'LOchange', type5 => 'intv' }, 2 => { tab_count => 5, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOaction', type3 => 'int8', name4 => 'LOislocked', type4 => 'int', name5 => 'LOchange', type5 => 'intv' }, 1 => { tab_count => 4, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOaction', type3 => 'int8', name4 => 'LOislocked', type4 => 'int' }, 0 => { tab_count => 3, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOislocked', type3 => 'int' } }, 'db.locksg' => { 3 => { tab_count => 5, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOaction', type3 => 'int8', name4 => 'LOislocked', type4 => 'int', name5 => 'LOchange', type5 => 'intv' }, 2 => { tab_count => 5, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOaction', type3 => 'int8', name4 => 'LOislocked', type4 => 'int', name5 => 'LOchange', type5 => 'intv' }, 1 => { tab_count => 4, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOaction', type3 => 'int8', name4 => 'LOislocked', type4 => 'int' }, 0 => { tab_count => 3, keyAttrs => 2, name0 => 'LOdfile', type0 => 'key', key0 => 'yes', name1 => 'LOclient', type1 => 'key', key1 => 'yes', name2 => 'LOuser', type2 => 'key', name3 => 'LOislocked', type3 => 'int' } }, 'db.logger' => { 0 => { tab_count => 2, keyAttrs => 1, name0 => 'LGseq', type0 => 'int', key0 => 'yes', name1 => 'LGkey', type1 => 'key', name2 => 'LGattr', type2 => 'key' } }, 'db.message' => { 0 => { tab_count => 2, keyAttrs => 2, name0 => 'MSlang', type0 => 'key', key0 => 'yes', name1 => 'MSid', type1 => 'int', key1 => 'yes', name2 => 'MSmsg', type2 => 'key' } }, 'db.monitor' => { 3 => { tab_count => 9, keyAttrs => 1, name0 => 'MOid', type0 => 'int', key0 => 'yes', name1 => 'MOuser', type1 => 'key', name2 => 'MOfunc', type2 => 'key', name3 => 'MOargs', type3 => 'key', name4 => 'MOstart', type4 => 'intv', name5 => 'MOrunstate', type5 => 'int', name6 => 'MOclient', type6 => 'key', name7 => 'MOhost', type7 => 'text', name8 => 'MOApp', type8 => 'text', name9 => 'MOlockinfo', type9 => 'text' }, 2 => { tab_count => 9, keyAttrs => 1, name0 => 'MOid', type0 => 'int', key0 => 'yes', name1 => 'MOuser', type1 => 'key', name2 => 'MOfunc', type2 => 'key', name3 => 'MOargs', type3 => 'key', name4 => 'MOstart', type4 => 'intv', name5 => 'MOrunstate', type5 => 'int', name6 => 'MOclient', type6 => 'key', name7 => 'MOhost', type7 => 'text', name8 => 'MOApp', type8 => 'text', name9 => 'MOlockinfo', type9 => 'text' }, 1 => { tab_count => 8, keyAttrs => 1, name0 => 'MOid', type0 => 'int', key0 => 'yes', name1 => 'MOuser', type1 => 'key', name2 => 'MOfunc', type2 => 'key', name3 => 'MOargs', type3 => 'key', name4 => 'MOstart', type4 => 'intv', name5 => 'MOrunstate', type5 => 'int', name6 => 'MOclient', type6 => 'key', name7 => 'MOhost', type7 => 'text', name8 => 'MOApp', type8 => 'text' }, 0 => { tab_count => 5, keyAttrs => 1, name0 => 'MOid', type0 => 'int', key0 => 'yes', name1 => 'MOuser', type1 => 'key', name2 => 'MOfunc', type2 => 'key', name3 => 'MOargs', type3 => 'key', name4 => 'MOstart', type4 => 'intv', name5 => 'MOrunstate', type5 => 'int' } }, 'db.nameval' => { 1 => { tab_count => 1, keyAttrs => 1, name0 => 'COname', type0 => 'key', key0 => 'yes', name1 => 'COvalue', type1 => 'text' }, 0 => { tab_count => 1, keyAttrs => 1, name0 => 'COname', type0 => 'key', key0 => 'yes', name1 => 'COival', type1 => 'int' } }, 'db.object' => { 1 => { tab_count => 3, keyAttrs => 1, name0 => 'OBsha', type0 => 'octet20', key0 => 'yes', name1 => 'OBtype', type1 => 'int', name2 => 'OBdata', type2 => 'octets', name3 => 'OBrefcount', type3 => 'int' }, 0 => { tab_count => 2, keyAttrs => 1, name0 => 'OBsha', type0 => 'octet20', key0 => 'yes', name1 => 'OBtype', type1 => 'int', name2 => 'OBdata', type2 => 'octets' } }, 'db.property' => { 1 => { tab_count => 6, keyAttrs => 4, name0 => 'PXname', type0 => 'key', key0 => 'yes', name1 => 'PXsequence', type1 => 'int', key1 => 'yes', name2 => 'PXatype', type2 => 'int', key2 => 'yes', name3 => 'PXscope', type3 => 'key', key3 => 'yes', name4 => 'PXvalue', type4 => 'text', name5 => 'PXdate', type5 => 'intv', name6 => 'PXuser', type6 => 'key' }, 0 => { tab_count => 6, keyAttrs => 4, name0 => 'PXname', type0 => 'key', key0 => 'yes', name1 => 'PXsequence', type1 => 'int', key1 => 'yes', name2 => 'PXatype', type2 => 'int', key2 => 'yes', name3 => 'PXscope', type3 => 'key', key3 => 'yes', name4 => 'PXvalue', type4 => 'text', name5 => 'PXdate', type5 => 'intv', name6 => 'PXuser', type6 => 'key' } }, 'db.protect' => { 6 => { tab_count => 8, keyAttrs => 1, name0 => 'PRseq', type0 => 'int', key0 => 'yes', name1 => 'PRgroup', type1 => 'int', name2 => 'PRuser', type2 => 'key', name3 => 'PRhost', type3 => 'key', name4 => 'PRperm', type4 => 'int', name5 => 'PRmapflag', type5 => 'int', name6 => 'PRdfile', type6 => 'key', name7 => 'PRsubpath', type7 => 'key', name8 => 'PRupdate', type8 => 'intv' }, 5 => { tab_count => 8, keyAttrs => 1, name0 => 'PRseq', type0 => 'int', key0 => 'yes', name1 => 'PRgroup', type1 => 'int', name2 => 'PRuser', type2 => 'key', name3 => 'PRhost', type3 => 'key', name4 => 'PRperm', type4 => 'int', name5 => 'PRmapflag', type5 => 'int', name6 => 'PRdfile', type6 => 'key', name7 => 'PRsubpath', type7 => 'key', name8 => 'PRupdate', type8 => 'intv' }, 4 => { tab_count => 6, keyAttrs => 1, name0 => 'PRseq', type0 => 'int', key0 => 'yes', name1 => 'PRgroup', type1 => 'int', name2 => 'PRuser', type2 => 'key', name3 => 'PRhost', type3 => 'key', name4 => 'PRperm8', type4 => 'int8', name5 => 'PRmapflag', type5 => 'int', name6 => 'PRdfile', type6 => 'key' }, 3 => { tab_count => 6, keyAttrs => 1, name0 => 'PRseq', type0 => 'int', key0 => 'yes', name1 => 'PRgroup', type1 => 'int', name2 => 'PRuser', type2 => 'key', name3 => 'PRhost', type3 => 'key', name4 => 'PRperm8', type4 => 'int8', name5 => 'PRmapflag', type5 => 'int', name6 => 'PRdfile', type6 => 'key' }, 2 => { tab_count => 6, keyAttrs => 1, name0 => 'PRseq', type0 => 'int', key0 => 'yes', name1 => 'PRgroup', type1 => 'int', name2 => 'PRuser', type2 => 'key', name3 => 'PRhost', type3 => 'key', name4 => 'PRperm8', type4 => 'int8', name5 => 'PRmapflag', type5 => 'int', name6 => 'PRdfile', type6 => 'key' }, 1 => { tab_count => 6, keyAttrs => 1, name0 => 'PRseq', type0 => 'int', key0 => 'yes', name1 => 'PRgroup', type1 => 'int', name2 => 'PRuser', type2 => 'key', name3 => 'PRhost', type3 => 'key', name4 => 'PRperm8', type4 => 'int8', name5 => 'PRmapflag', type5 => 'int', name6 => 'PRdfile', type6 => 'key' }, 0 => { tab_count => 5, keyAttrs => 1, name0 => 'PRseq', type0 => 'int', key0 => 'yes', name1 => 'PRuser', type1 => 'key', name2 => 'PRhost', type2 => 'key', name3 => 'PRperm', type3 => 'int', name4 => 'PRmapflag', type4 => 'int', name5 => 'PRdfile', type5 => 'key' } }, 'db.pubkey' => { 1 => { tab_count => 4, keyAttrs => 2, name0 => 'PKuser', type0 => 'key', key0 => 'yes', name1 => 'PKscope', type1 => 'key', key1 => 'yes', name2 => 'PKkey', type2 => 'text', name3 => 'PKdigest', type3 => 'octet', name4 => 'PKupdate', type4 => 'intv' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'PKuser', type0 => 'key', key0 => 'yes', name1 => 'PKscope', type1 => 'key', key1 => 'yes', name2 => 'PKkey', type2 => 'text', name3 => 'PKdigest', type3 => 'octet', name4 => 'PKupdate', type4 => 'intv' } }, 'db.ref' => { 0 => { tab_count => 4, keyAttrs => 3, name0 => 'RFrepo', type0 => 'key', key0 => 'yes', name1 => 'RFname', type1 => 'text', key1 => 'yes', name2 => 'RFtype', type2 => 'int', key2 => 'yes', name3 => 'RFref', type3 => 'octet20', name4 => 'RFsymref', type4 => 'text' } }, 'db.refcntadjust' => { 0 => { tab_count => 3, keyAttrs => 2, name0 => 'RAwalked', type0 => 'int', key0 => 'yes', name1 => 'RAsha', type1 => 'octet20', key1 => 'yes', name2 => 'RAadjustment', type2 => 'int', name3 => 'RAadjustObject', type3 => 'int' } }, 'db.refhist' => { 1 => { tab_count => 7, keyAttrs => 7, name0 => 'RHrepo', type0 => 'key', key0 => 'yes', name1 => 'RHname', type1 => 'text', key1 => 'yes', name2 => 'RHtype', type2 => 'int', key2 => 'yes', name3 => 'RHaction', type3 => 'int', key3 => 'yes', name4 => 'RHdate', type4 => 'intv', key4 => 'yes', name5 => 'RHuser', type5 => 'key', key5 => 'yes', name6 => 'RHref', type6 => 'octet20', key6 => 'yes', name7 => 'RHsymref', type7 => 'text' }, 0 => { tab_count => 7, keyAttrs => 7, name0 => 'RHrepo', type0 => 'key', key0 => 'yes', name1 => 'RHname', type1 => 'text', key1 => 'yes', name2 => 'RHtype', type2 => 'int', key2 => 'yes', name3 => 'RHaction', type3 => 'int', key3 => 'yes', name4 => 'RHdate', type4 => 'intv', key4 => 'yes', name5 => 'RHuser', type5 => 'key', key5 => 'yes', name6 => 'RHref', type6 => 'octet20', key6 => 'yes', name7 => 'RHsymref', type7 => 'text' } }, 'db.remote' => { 2 => { tab_count => 9, keyAttrs => 1, name0 => 'RMid', type0 => 'key', key0 => 'yes', name1 => 'RMowner', type1 => 'key', name2 => 'RMoptions', type2 => 'int', name3 => 'RMaddress', type3 => 'text', name4 => 'RMdesc', type4 => 'text', name5 => 'RMupdate', type5 => 'intv', name6 => 'RMaccess', type6 => 'intv', name7 => 'RMfetch', type7 => 'intv', name8 => 'RMpush', type8 => 'intv', name9 => 'RMrmtuser', type9 => 'key' }, 1 => { tab_count => 9, keyAttrs => 1, name0 => 'RMid', type0 => 'key', key0 => 'yes', name1 => 'RMowner', type1 => 'key', name2 => 'RMoptions', type2 => 'int', name3 => 'RMaddress', type3 => 'text', name4 => 'RMdesc', type4 => 'text', name5 => 'RMupdate', type5 => 'intv', name6 => 'RMaccess', type6 => 'intv', name7 => 'RMfetch', type7 => 'intv', name8 => 'RMpush', type8 => 'intv', name9 => 'RMrmtuser', type9 => 'key' }, 0 => { tab_count => 8, keyAttrs => 1, name0 => 'RMid', type0 => 'key', key0 => 'yes', name1 => 'RMowner', type1 => 'key', name2 => 'RMoptions', type2 => 'int', name3 => 'RMaddress', type3 => 'text', name4 => 'RMdesc', type4 => 'text', name5 => 'RMupdate', type5 => 'intv', name6 => 'RMaccess', type6 => 'intv', name7 => 'RMfetch', type7 => 'intv', name8 => 'RMpush', type8 => 'intv' } }, 'db.repo' => { 5 => { tab_count => 14, keyAttrs => 1, name0 => 'RPrepo', type0 => 'key', key0 => 'yes', name1 => 'RPowner', type1 => 'key', name2 => 'RPcreated', type2 => 'intv', name3 => 'RPpushed', type3 => 'intv', name4 => 'RPforked', type4 => 'key', name5 => 'RPdesc', type5 => 'text', name6 => 'RPbranch', type6 => 'text', name7 => 'RPmirror', type7 => 'text', name8 => 'RPoptions', type8 => 'int', name9 => 'RPid', type9 => 'int', name10 => 'RPgcmrrserver', type10 => 'text', name11 => 'RPgcmrrsecrettoken', type11 => 'text', name12 => 'RPgcmrrstatus', type12 => 'int8', name13 => 'RPgcmrrexcludedbranches', type13 => 'text', name14 => 'RPgcmrrhidefetchurl', type14 => 'int8' }, 4 => { tab_count => 14, keyAttrs => 1, name0 => 'RPrepo', type0 => 'key', key0 => 'yes', name1 => 'RPowner', type1 => 'key', name2 => 'RPcreated', type2 => 'intv', name3 => 'RPpushed', type3 => 'intv', name4 => 'RPforked', type4 => 'key', name5 => 'RPdesc', type5 => 'text', name6 => 'RPbranch', type6 => 'text', name7 => 'RPmirror', type7 => 'text', name8 => 'RPoptions', type8 => 'int', name9 => 'RPid', type9 => 'int', name10 => 'RPgcmrrserver', type10 => 'text', name11 => 'RPgcmrrsecrettoken', type11 => 'text', name12 => 'RPgcmrrstatus', type12 => 'int8', name13 => 'RPgcmrrexcludedbranches', type13 => 'text', name14 => 'RPgcmrrhidefetchurl', type14 => 'int8' }, 3 => { tab_count => 10, keyAttrs => 1, name0 => 'RPrepo', type0 => 'key', key0 => 'yes', name1 => 'RPowner', type1 => 'key', name2 => 'RPcreated', type2 => 'intv', name3 => 'RPpushed', type3 => 'intv', name4 => 'RPforked', type4 => 'key', name5 => 'RPdesc', type5 => 'text', name6 => 'RPbranch', type6 => 'text', name7 => 'RPmirror', type7 => 'text', name8 => 'RPoptions', type8 => 'int', name9 => 'RPid', type9 => 'int', name10 => 'RPgcmrrserver', type10 => 'text' }, 2 => { tab_count => 9, keyAttrs => 1, name0 => 'RPrepo', type0 => 'key', key0 => 'yes', name1 => 'RPowner', type1 => 'key', name2 => 'RPcreated', type2 => 'intv', name3 => 'RPpushed', type3 => 'intv', name4 => 'RPforked', type4 => 'key', name5 => 'RPdesc', type5 => 'text', name6 => 'RPbranch', type6 => 'text', name7 => 'RPmirror', type7 => 'text', name8 => 'RPoptions', type8 => 'int', name9 => 'RPid', type9 => 'int' }, 1 => { tab_count => 7, keyAttrs => 1, name0 => 'RPrepo', type0 => 'key', key0 => 'yes', name1 => 'RPowner', type1 => 'key', name2 => 'RPcreated', type2 => 'intv', name3 => 'RPpushed', type3 => 'intv', name4 => 'RPforked', type4 => 'key', name5 => 'RPdesc', type5 => 'text', name6 => 'RPbranch', type6 => 'text', name7 => 'RPmirror', type7 => 'text' }, 0 => { tab_count => 6, keyAttrs => 1, name0 => 'RPrepo', type0 => 'key', key0 => 'yes', name1 => 'RPowner', type1 => 'key', name2 => 'RPcreated', type2 => 'intv', name3 => 'RPpushed', type3 => 'intv', name4 => 'RPforked', type4 => 'key', name5 => 'RPdesc', type5 => 'text', name6 => 'RPbranch', type6 => 'text' } }, 'db.repoview' => { 2 => { tab_count => 5, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key', name5 => 'VIctype', type5 => 'int' }, 1 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' } }, 'db.resolve' => { 2 => { tab_count => 9, keyAttrs => 3, name0 => 'RStfile', type0 => 'key', key0 => 'yes', name1 => 'RSffile', type1 => 'key', key1 => 'yes', name2 => 'RSsfrev', type2 => 'intv', key2 => 'yes', name3 => 'RSefrev', type3 => 'intv', name4 => 'RSstrev', type4 => 'intv', name5 => 'RSetrev', type5 => 'intv', name6 => 'RShow', type6 => 'int8', name7 => 'RSstate', type7 => 'intv', name8 => 'RSbfile', type8 => 'key', name9 => 'RSbrev', type9 => 'intv' }, 1 => { tab_count => 9, keyAttrs => 3, name0 => 'RStfile', type0 => 'key', key0 => 'yes', name1 => 'RSffile', type1 => 'key', key1 => 'yes', name2 => 'RSsfrev', type2 => 'intv', key2 => 'yes', name3 => 'RSefrev', type3 => 'intv', name4 => 'RSstrev', type4 => 'intv', name5 => 'RSetrev', type5 => 'intv', name6 => 'RShow', type6 => 'int8', name7 => 'RSstate', type7 => 'intv', name8 => 'RSbfile', type8 => 'key', name9 => 'RSbrev', type9 => 'intv' }, 0 => { tab_count => 9, keyAttrs => 3, name0 => 'RStfile', type0 => 'key', key0 => 'yes', name1 => 'RSffile', type1 => 'key', key1 => 'yes', name2 => 'RSsfrev', type2 => 'intv', key2 => 'yes', name3 => 'RSefrev', type3 => 'intv', name4 => 'RSstrev', type4 => 'intv', name5 => 'RSetrev', type5 => 'intv', name6 => 'RShow', type6 => 'int8', name7 => 'RSres', type7 => 'int', name8 => 'RSbfile', type8 => 'key', name9 => 'RSbrev', type9 => 'intv' } }, 'db.resolveg' => { 0 => { tab_count => 5, keyAttrs => 3, name0 => 'RStfile', type0 => 'key', key0 => 'yes', name1 => 'RSffile', type1 => 'key', key1 => 'yes', name2 => 'RSbasesha', type2 => 'octet20', key2 => 'yes', name3 => 'RSwantsha', type3 => 'octet20', name4 => 'RShow', type4 => 'int8', name5 => 'RSstate', type5 => 'int' } }, 'db.resolvex' => { 2 => { tab_count => 9, keyAttrs => 3, name0 => 'RStfile', type0 => 'key', key0 => 'yes', name1 => 'RSffile', type1 => 'key', key1 => 'yes', name2 => 'RSsfrev', type2 => 'intv', key2 => 'yes', name3 => 'RSefrev', type3 => 'intv', name4 => 'RSstrev', type4 => 'intv', name5 => 'RSetrev', type5 => 'intv', name6 => 'RShow', type6 => 'int8', name7 => 'RSstate', type7 => 'intv', name8 => 'RSbfile', type8 => 'key', name9 => 'RSbrev', type9 => 'intv' }, 1 => { tab_count => 9, keyAttrs => 3, name0 => 'RStfile', type0 => 'key', key0 => 'yes', name1 => 'RSffile', type1 => 'key', key1 => 'yes', name2 => 'RSsfrev', type2 => 'intv', key2 => 'yes', name3 => 'RSefrev', type3 => 'intv', name4 => 'RSstrev', type4 => 'intv', name5 => 'RSetrev', type5 => 'intv', name6 => 'RShow', type6 => 'int8', name7 => 'RSstate', type7 => 'intv', name8 => 'RSbfile', type8 => 'key', name9 => 'RSbrev', type9 => 'intv' }, 0 => { tab_count => 9, keyAttrs => 3, name0 => 'RStfile', type0 => 'key', key0 => 'yes', name1 => 'RSffile', type1 => 'key', key1 => 'yes', name2 => 'RSsfrev', type2 => 'intv', key2 => 'yes', name3 => 'RSefrev', type3 => 'intv', name4 => 'RSstrev', type4 => 'intv', name5 => 'RSetrev', type5 => 'intv', name6 => 'RShow', type6 => 'int8', name7 => 'RSres', type7 => 'int', name8 => 'RSbfile', type8 => 'key', name9 => 'RSbrev', type9 => 'intv' } }, 'db.rev' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revbx' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revcx' => { 1 => { tab_count => 3, keyAttrs => 2, name0 => 'RXchange', type0 => 'intv', key0 => 'yes', name1 => 'RXdfile', type1 => 'key', key1 => 'yes', name2 => 'RXrev', type2 => 'intv', name3 => 'RXaction', type3 => 'int8' }, 0 => { tab_count => 3, keyAttrs => 2, name0 => 'RXchange', type0 => 'intv', key0 => 'yes', name1 => 'RXdfile', type1 => 'key', key1 => 'yes', name2 => 'RXrev', type2 => 'intv', name3 => 'RXaction', type3 => 'int8' } }, 'db.revdx' => { 10 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revfs' => { 1 => { tab_count => 3, keyAttrs => 3, name0 => 'RSdfile', type0 => 'key', key0 => 'yes', name1 => 'RSrev', type1 => 'intv', key1 => 'yes', name2 => 'RSctype', type2 => 'text', key2 => 'yes', name3 => 'REcsize', type3 => 'int64' }, 0 => { tab_count => 3, keyAttrs => 3, name0 => 'RSdfile', type0 => 'key', key0 => 'yes', name1 => 'RSrev', type1 => 'intv', key1 => 'yes', name2 => 'RSctype', type2 => 'text', key2 => 'yes', name3 => 'REcsize', type3 => 'int64' } }, 'db.revhx' => { 10 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 1, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.review' => { 1 => { tab_count => 4, keyAttrs => 2, name0 => 'RVuser', type0 => 'key', key0 => 'yes', name1 => 'RVseq', type1 => 'int', key1 => 'yes', name2 => 'RVmapflag', type2 => 'int', name3 => 'RVdfile', type3 => 'key', name4 => 'RVtype', type4 => 'int8' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'RVuser', type0 => 'key', key0 => 'yes', name1 => 'RVseq', type1 => 'int', key1 => 'yes', name2 => 'RVmapflag', type2 => 'int', name3 => 'RVdfile', type3 => 'key', name4 => 'RVtype', type4 => 'int8' } }, 'db.revpx' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revsh' => { 10 => { tab_count => 13, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REaction', type3 => 'int8', key3 => 'yes', name4 => 'REchange', type4 => 'intv', key4 => 'yes', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REishead', type3 => 'int8', key3 => 'yes', name4 => 'REaction', type4 => 'int8', key4 => 'yes', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 5, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', key2 => 'yes', name3 => 'REishead', type3 => 'int8', key3 => 'yes', name4 => 'REaction', type4 => 'int8', key4 => 'yes', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revstg' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revsx' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revtr' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revtx' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.revux' => { 10 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 9 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 8 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 7 => { tab_count => 13, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REalazy', type10 => 'int', name11 => 'REafile', type11 => 'key', name12 => 'REarev', type12 => 'RCS', name13 => 'REatype', type13 => 'int' }, 6 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 5 => { tab_count => 12, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REsize', type8 => 'int64', name9 => 'REtraitlot', type9 => 'intv', name10 => 'REafile', type10 => 'key', name11 => 'REarev', type11 => 'RCS', name12 => 'REatype', type12 => 'int' }, 4 => { tab_count => 11, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REtraitlot', type8 => 'intv', name9 => 'REafile', type9 => 'key', name10 => 'REarev', type10 => 'RCS', name11 => 'REatype', type11 => 'int' }, 3 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REmodtime', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 2 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REaction', type3 => 'int8', name4 => 'REchange', type4 => 'intv', name5 => 'REdate', type5 => 'intv', name6 => 'REdigest', type6 => 'octet', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' }, 1 => { tab_count => 10, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REdigest', type7 => 'octet', name8 => 'REafile', type8 => 'key', name9 => 'REarev', type9 => 'RCS', name10 => 'REatype', type10 => 'int' }, 0 => { tab_count => 9, keyAttrs => 2, name0 => 'REdfile', type0 => 'key', key0 => 'yes', name1 => 'RErev', type1 => 'intv', key1 => 'yes', name2 => 'REtype', type2 => 'int', name3 => 'REishead', type3 => 'int8', name4 => 'REaction', type4 => 'int8', name5 => 'REchange', type5 => 'intv', name6 => 'REdate', type6 => 'intv', name7 => 'REafile', type7 => 'key', name8 => 'REarev', type8 => 'RCS', name9 => 'REatype', type9 => 'int' } }, 'db.rmtview' => { 1 => { tab_count => 5, keyAttrs => 2, name0 => 'VRid', type0 => 'key', key0 => 'yes', name1 => 'VRseq', type1 => 'int', key1 => 'yes', name2 => 'VRmapflag', type2 => 'int', name3 => 'VRlfile', type3 => 'key', name4 => 'VRrfile', type4 => 'key', name5 => 'VRretain', type5 => 'int' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'VRid', type0 => 'key', key0 => 'yes', name1 => 'VRseq', type1 => 'int', key1 => 'yes', name2 => 'VRmapflag', type2 => 'int', name3 => 'VRlfile', type3 => 'key', name4 => 'VRrfile', type4 => 'key' } }, 'db.scanctl' => { 0 => { tab_count => 9, keyAttrs => 1, name0 => 'SCfile', type0 => 'key', key0 => 'yes', name1 => 'SCstate', type1 => 'int', name2 => 'SCseq', type2 => 'int', name3 => 'SCdirs', type3 => 'int', name4 => 'SCfiles', type4 => 'int', name5 => 'SCzeros', type5 => 'int', name6 => 'SCdirserr', type6 => 'int', name7 => 'SCpri', type7 => 'int', name8 => 'SCreqpause', type8 => 'int', name9 => 'SCerr', type9 => 'octets' } }, 'db.scandir' => { 0 => { tab_count => 2, keyAttrs => 2, name0 => 'SDlskey', type0 => 'key', key0 => 'yes', name1 => 'SDseq', type1 => 'int', key1 => 'yes', name2 => 'SDfile', type2 => 'key' } }, 'db.sendq' => { 5 => { tab_count => 20, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv', name18 => 'SQbsha', type18 => 'octet20', name19 => 'SQreposlot', type19 => 'int', name20 => 'SQsdigest', type20 => 'octet' }, 4 => { tab_count => 20, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv', name18 => 'SQbsha', type18 => 'octet20', name19 => 'SQreposlot', type19 => 'int', name20 => 'SQsdigest', type20 => 'octet' }, 3 => { tab_count => 18, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv', name18 => 'SQbsha', type18 => 'octet20' }, 2 => { tab_count => 17, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv' }, 1 => { tab_count => 14, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int' }, 0 => { tab_count => 13, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int' } }, 'db.sendq.pt' => { 5 => { tab_count => 20, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv', name18 => 'SQbsha', type18 => 'octet20', name19 => 'SQreposlot', type19 => 'int', name20 => 'SQsdigest', type20 => 'octet' }, 4 => { tab_count => 20, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv', name18 => 'SQbsha', type18 => 'octet20', name19 => 'SQreposlot', type19 => 'int', name20 => 'SQsdigest', type20 => 'octet' }, 3 => { tab_count => 18, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv', name18 => 'SQbsha', type18 => 'octet20' }, 2 => { tab_count => 17, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int', name15 => 'SQdepotrev', type15 => 'intv', name16 => 'SQchange', type16 => 'intv', name17 => 'SQdate', type17 => 'intv' }, 1 => { tab_count => 14, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int', name14 => 'SQclienttype', type14 => 'int' }, 0 => { tab_count => 13, keyAttrs => 2, name0 => 'SQtaskid', type0 => 'int', key0 => 'yes', name1 => 'SQseq', type1 => 'int', key1 => 'yes', name2 => 'SQhandle', type2 => 'text', name3 => 'SQdepotfile', type3 => 'key', name4 => 'SQclientfile', type4 => 'key', name5 => 'SQrev', type5 => 'intv', name6 => 'SQdtype', type6 => 'int', name7 => 'SQmodtime', type7 => 'intv', name8 => 'SQdigest', type8 => 'octet', name9 => 'SQsize', type9 => 'int64', name10 => 'SQlbrfile', type10 => 'key', name11 => 'SQlbrrev', type11 => 'RCS', name12 => 'SQlbrtype', type12 => 'int', name13 => 'SQflags', type13 => 'int' } }, 'db.server' => { 3 => { tab_count => 10, keyAttrs => 1, name0 => 'SVid', type0 => 'key', key0 => 'yes', name1 => 'SVtype', type1 => 'int', name2 => 'SVname', type2 => 'text', name3 => 'SVaddress', type3 => 'text', name4 => 'SVexternalAddress', type4 => 'text', name5 => 'SVservices', type5 => 'int64', name6 => 'SVdesc', type6 => 'text', name7 => 'SVuser', type7 => 'key', name8 => 'SVoptions', type8 => 'int', name9 => 'SVrplFrom', type9 => 'key', name10 => 'SVfoverSeen', type10 => 'text' }, 2 => { tab_count => 7, keyAttrs => 1, name0 => 'SVid', type0 => 'key', key0 => 'yes', name1 => 'SVtype', type1 => 'int', name2 => 'SVname', type2 => 'text', name3 => 'SVaddress', type3 => 'text', name4 => 'SVexternalAddress', type4 => 'text', name5 => 'SVservices', type5 => 'int64', name6 => 'SVdesc', type6 => 'text', name7 => 'SVuser', type7 => 'key' }, 1 => { tab_count => 6, keyAttrs => 1, name0 => 'SVid', type0 => 'key', key0 => 'yes', name1 => 'SVtype', type1 => 'int', name2 => 'SVname', type2 => 'text', name3 => 'SVaddress', type3 => 'text', name4 => 'SVservices', type4 => 'int64', name5 => 'SVdesc', type5 => 'text', name6 => 'SVuser', type6 => 'key' }, 0 => { tab_count => 5, keyAttrs => 1, name0 => 'SVid', type0 => 'key', key0 => 'yes', name1 => 'SVtype', type1 => 'int', name2 => 'SVname', type2 => 'text', name3 => 'SVaddress', type3 => 'text', name4 => 'SVservices', type4 => 'int64', name5 => 'SVdesc', type5 => 'text' } }, 'db.stash' => { 1 => { tab_count => 4, keyAttrs => 4, name0 => 'SSclient', type0 => 'key', key0 => 'yes', name1 => 'SSid', type1 => 'key', key1 => 'yes', name2 => 'SStype', type2 => 'int8', key2 => 'yes', name3 => 'SSseq', type3 => 'int', key3 => 'yes', name4 => 'SSchange', type4 => 'intv' }, 0 => { tab_count => 4, keyAttrs => 4, name0 => 'SSclient', type0 => 'key', key0 => 'yes', name1 => 'SSid', type1 => 'key', key1 => 'yes', name2 => 'SStype', type2 => 'int8', key2 => 'yes', name3 => 'SSseq', type3 => 'int', key3 => 'yes', name4 => 'SSchange', type4 => 'intv' } }, 'db.storage' => { 2 => { tab_count => 8, keyAttrs => 3, name0 => 'SRfile', type0 => 'key', key0 => 'yes', name1 => 'SRrev', type1 => 'RCS', key1 => 'yes', name2 => 'SRtype', type2 => 'int', key2 => 'yes', name3 => 'SRrefcount', type3 => 'int', name4 => 'SRdigest', type4 => 'octet', name5 => 'SRsize', type5 => 'int64', name6 => 'SRserversz', type6 => 'int64', name7 => 'SRcompcksum', type7 => 'octet', name8 => 'SRdate', type8 => 'intv' }, 1 => { tab_count => 8, keyAttrs => 3, name0 => 'SRfile', type0 => 'key', key0 => 'yes', name1 => 'SRrev', type1 => 'RCS', key1 => 'yes', name2 => 'SRtype', type2 => 'int', key2 => 'yes', name3 => 'SRrefcount', type3 => 'int', name4 => 'SRdigest', type4 => 'octet', name5 => 'SRsize', type5 => 'int64', name6 => 'SRserversz', type6 => 'int64', name7 => 'SRcompcksum', type7 => 'octet', name8 => 'SRdate', type8 => 'intv' }, 0 => { tab_count => 8, keyAttrs => 3, name0 => 'SRfile', type0 => 'key', key0 => 'yes', name1 => 'SRrev', type1 => 'RCS', key1 => 'yes', name2 => 'SRtype', type2 => 'int', key2 => 'yes', name3 => 'SRrefcount', type3 => 'int', name4 => 'SRdigest', type4 => 'octet', name5 => 'SRsize', type5 => 'int64', name6 => 'SRserversz', type6 => 'int64', name7 => 'SRcompcksum', type7 => 'int64', name8 => 'SRdate', type8 => 'intv' } }, 'db.storageg' => { 1 => { tab_count => 4, keyAttrs => 3, name0 => 'SGrepo', type0 => 'key', key0 => 'yes', name1 => 'SGsha', type1 => 'key', key1 => 'yes', name2 => 'SGtype', type2 => 'int', key2 => 'yes', name3 => 'SGrefcount', type3 => 'int', name4 => 'SGdate', type4 => 'intv' }, 0 => { tab_count => 4, keyAttrs => 3, name0 => 'SGrepo', type0 => 'key', key0 => 'yes', name1 => 'SGsha', type1 => 'key', key1 => 'yes', name2 => 'SGtype', type2 => 'int', key2 => 'yes', name3 => 'SGrefcount', type3 => 'int', name4 => 'SGdate', type4 => 'intv' } }, 'db.storagesh' => { 2 => { tab_count => 8, keyAttrs => 3, name0 => 'SRfile', type0 => 'key', key0 => 'yes', name1 => 'SRrev', type1 => 'RCS', key1 => 'yes', name2 => 'SRtype', type2 => 'int', key2 => 'yes', name3 => 'SRrefcount', type3 => 'int', name4 => 'SRdigest', type4 => 'octet', name5 => 'SRsize', type5 => 'int64', name6 => 'SRserversz', type6 => 'int64', name7 => 'SRcompcksum', type7 => 'octet', name8 => 'SRdate', type8 => 'intv' }, 1 => { tab_count => 8, keyAttrs => 3, name0 => 'SRfile', type0 => 'key', key0 => 'yes', name1 => 'SRrev', type1 => 'RCS', key1 => 'yes', name2 => 'SRtype', type2 => 'int', key2 => 'yes', name3 => 'SRrefcount', type3 => 'int', name4 => 'SRdigest', type4 => 'octet', name5 => 'SRsize', type5 => 'int64', name6 => 'SRserversz', type6 => 'int64', name7 => 'SRcompcksum', type7 => 'octet', name8 => 'SRdate', type8 => 'intv' }, 0 => { tab_count => 8, keyAttrs => 3, name0 => 'SRfile', type0 => 'key', key0 => 'yes', name1 => 'SRrev', type1 => 'RCS', key1 => 'yes', name2 => 'SRtype', type2 => 'int', key2 => 'yes', name3 => 'SRrefcount', type3 => 'int', name4 => 'SRdigest', type4 => 'octet', name5 => 'SRsize', type5 => 'int64', name6 => 'SRserversz', type6 => 'int64', name7 => 'SRcompcksum', type7 => 'int64', name8 => 'SRdate', type8 => 'intv' } }, 'db.storagesx' => { 0 => { tab_count => 4, keyAttrs => 5, name0 => 'SHdigest', type0 => 'octet', key0 => 'yes', name1 => 'SHsize', type1 => 'int64', key1 => 'yes', name2 => 'SHfile', type2 => 'key', key2 => 'yes', name3 => 'SHrev', type3 => 'RCS', key3 => 'yes', name4 => 'SHtype', type4 => 'int', key4 => 'yes' } }, 'db.stream' => { 3 => { tab_count => 11, keyAttrs => 1, name0 => 'STstream', type0 => 'key', key0 => 'yes', name1 => 'STparent', type1 => 'key', name2 => 'STtitle', type2 => 'text', name3 => 'STtype', type3 => 'int', name4 => 'STpreview', type4 => 'intv', name5 => 'STchange', type5 => 'intv', name6 => 'STcopychg', type6 => 'intv', name7 => 'STmergechg', type7 => 'intv', name8 => 'SThighchg', type8 => 'intv', name9 => 'SThash', type9 => 'int', name10 => 'STstatus', type10 => 'int', name11 => 'STparentview', type11 => 'int' }, 2 => { tab_count => 11, keyAttrs => 1, name0 => 'STstream', type0 => 'key', key0 => 'yes', name1 => 'STparent', type1 => 'key', name2 => 'STtitle', type2 => 'text', name3 => 'STtype', type3 => 'int', name4 => 'STpreview', type4 => 'intv', name5 => 'STchange', type5 => 'intv', name6 => 'STcopychg', type6 => 'intv', name7 => 'STmergechg', type7 => 'intv', name8 => 'SThighchg', type8 => 'intv', name9 => 'SThash', type9 => 'int', name10 => 'STstatus', type10 => 'int', name11 => 'STparentview', type11 => 'int' }, 1 => { tab_count => 10, keyAttrs => 1, name0 => 'STstream', type0 => 'key', key0 => 'yes', name1 => 'STparent', type1 => 'key', name2 => 'STtitle', type2 => 'text', name3 => 'STtype', type3 => 'int', name4 => 'STpreview', type4 => 'intv', name5 => 'STchange', type5 => 'intv', name6 => 'STcopychg', type6 => 'intv', name7 => 'STmergechg', type7 => 'intv', name8 => 'SThighchg', type8 => 'intv', name9 => 'SThash', type9 => 'int', name10 => 'STstatus', type10 => 'int' }, 0 => { tab_count => 4, keyAttrs => 1, name0 => 'STstream', type0 => 'key', key0 => 'yes', name1 => 'STparent', type1 => 'key', name2 => 'STtitle', type2 => 'text', name3 => 'STtype', type3 => 'int', name4 => 'STpreview', type4 => 'intv' } }, 'db.streamq' => { 0 => { tab_count => 0, keyAttrs => 1, name0 => 'SQstream', type0 => 'key', key0 => 'yes' } }, 'db.streamrelation' => { 0 => { tab_count => 3, keyAttrs => 2, name0 => 'SOindep', type0 => 'key', key0 => 'yes', name1 => 'SOdep', type1 => 'key', key1 => 'yes', name2 => 'SOtype', type2 => 'int', name3 => 'SOpv', type3 => 'int' } }, 'db.streamview' => { 2 => { tab_count => 5, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key', name5 => 'VIctype', type5 => 'int' }, 1 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' } }, 'db.streamviewx' => { 2 => { tab_count => 8, keyAttrs => 4, name0 => 'SXdfile', type0 => 'key', key0 => 'yes', name1 => 'SXvfile', type1 => 'key', key1 => 'yes', name2 => 'SXmapflag', type2 => 'int', key2 => 'yes', name3 => 'SXstream', type3 => 'key', key3 => 'yes', name4 => 'SXchange', type4 => 'text', name5 => 'SXsource', type5 => 'key', name6 => 'SXpath', type6 => 'int', name7 => 'SXcompPfs', type7 => 'text', name8 => 'SXeffCompType', type8 => 'int' }, 1 => { tab_count => 7, keyAttrs => 4, name0 => 'SXdfile', type0 => 'key', key0 => 'yes', name1 => 'SXvfile', type1 => 'key', key1 => 'yes', name2 => 'SXmapflag', type2 => 'int', key2 => 'yes', name3 => 'SXstream', type3 => 'key', key3 => 'yes', name4 => 'SXchange', type4 => 'text', name5 => 'SXsource', type5 => 'key', name6 => 'SXpath', type6 => 'int', name7 => 'SXcompPfs', type7 => 'text' }, 0 => { tab_count => 6, keyAttrs => 4, name0 => 'SXdfile', type0 => 'key', key0 => 'yes', name1 => 'SXvfile', type1 => 'key', key1 => 'yes', name2 => 'SXmapflag', type2 => 'int', key2 => 'yes', name3 => 'SXstream', type3 => 'key', key3 => 'yes', name4 => 'SXchange', type4 => 'text', name5 => 'SXsource', type5 => 'key', name6 => 'SXpath', type6 => 'int' } }, 'db.submodule' => { 0 => { tab_count => 2, keyAttrs => 2, name0 => 'SMrepo', type0 => 'key', key0 => 'yes', name1 => 'SMpath', type1 => 'key', key1 => 'yes', name2 => 'SMsubrepo', type2 => 'key' } }, 'db.svrview' => { 0 => { tab_count => 4, keyAttrs => 3, name0 => 'SFid', type0 => 'key', key0 => 'yes', name1 => 'SFtype', type1 => 'int', key1 => 'yes', name2 => 'SFseq', type2 => 'int', key2 => 'yes', name3 => 'SFmapflag', type3 => 'int', name4 => 'SFvfile', type4 => 'key' } }, 'db.template' => { 3 => { tab_count => 8, keyAttrs => 3, name0 => 'TVname', type0 => 'key', key0 => 'yes', name1 => 'TVchange', type1 => 'intv', key1 => 'yes', name2 => 'TVseq', type2 => 'int', key2 => 'yes', name3 => 'TVparent', type3 => 'key', name4 => 'TVtype', type4 => 'int', name5 => 'TVpath', type5 => 'int', name6 => 'TVvfile', type6 => 'key', name7 => 'TVdfile', type7 => 'key', name8 => 'TVcmap', type8 => 'text' }, 2 => { tab_count => 8, keyAttrs => 3, name0 => 'TVname', type0 => 'key', key0 => 'yes', name1 => 'TVchange', type1 => 'intv', key1 => 'yes', name2 => 'TVseq', type2 => 'int', key2 => 'yes', name3 => 'TVparent', type3 => 'key', name4 => 'TVtype', type4 => 'int', name5 => 'TVpath', type5 => 'int', name6 => 'TVvfile', type6 => 'key', name7 => 'TVdfile', type7 => 'key', name8 => 'TVcmap', type8 => 'text' }, 1 => { tab_count => 8, keyAttrs => 3, name0 => 'TVname', type0 => 'key', key0 => 'yes', name1 => 'TVchange', type1 => 'intv', key1 => 'yes', name2 => 'TVseq', type2 => 'int', key2 => 'yes', name3 => 'TVparent', type3 => 'key', name4 => 'TVtype', type4 => 'int', name5 => 'TVpath', type5 => 'int', name6 => 'TVvfile', type6 => 'key', name7 => 'TVdfile', type7 => 'key', name8 => 'TVccmap', type8 => 'intv' }, 0 => { tab_count => 7, keyAttrs => 3, name0 => 'TVname', type0 => 'key', key0 => 'yes', name1 => 'TVchange', type1 => 'intv', key1 => 'yes', name2 => 'TVseq', type2 => 'int', key2 => 'yes', name3 => 'TVparent', type3 => 'key', name4 => 'TVtype', type4 => 'int', name5 => 'TVpath', type5 => 'int', name6 => 'TVvfile', type6 => 'key', name7 => 'TVdfile', type7 => 'key' } }, 'db.templatesx' => { 3 => { tab_count => 12, keyAttrs => 3, name0 => 'TSshelf', type0 => 'intv', key0 => 'yes', name1 => 'TSname', type1 => 'key', key1 => 'yes', name2 => 'TSseq', type2 => 'int', key2 => 'yes', name3 => 'TSwchange', type3 => 'intv', name4 => 'TSparent', type4 => 'key', name5 => 'TStype', type5 => 'int', name6 => 'TSpath', type6 => 'int', name7 => 'TSvfile', type7 => 'key', name8 => 'TSdfile', type8 => 'key', name9 => 'TScmap', type9 => 'text', name10 => 'TSochange', type10 => 'intv', name11 => 'TSuser', type11 => 'key', name12 => 'TSaction', type12 => 'int8' }, 2 => { tab_count => 12, keyAttrs => 3, name0 => 'TSshelf', type0 => 'intv', key0 => 'yes', name1 => 'TSname', type1 => 'key', key1 => 'yes', name2 => 'TSseq', type2 => 'int', key2 => 'yes', name3 => 'TSwchange', type3 => 'intv', name4 => 'TSparent', type4 => 'key', name5 => 'TStype', type5 => 'int', name6 => 'TSpath', type6 => 'int', name7 => 'TSvfile', type7 => 'key', name8 => 'TSdfile', type8 => 'key', name9 => 'TScmap', type9 => 'text', name10 => 'TSochange', type10 => 'intv', name11 => 'TSuser', type11 => 'key', name12 => 'TSaction', type12 => 'int8' }, 1 => { tab_count => 10, keyAttrs => 3, name0 => 'TSshelf', type0 => 'intv', key0 => 'yes', name1 => 'TSname', type1 => 'key', key1 => 'yes', name2 => 'TSseq', type2 => 'int', key2 => 'yes', name3 => 'TSwchange', type3 => 'intv', name4 => 'TSparent', type4 => 'key', name5 => 'TStype', type5 => 'int', name6 => 'TSpath', type6 => 'int', name7 => 'TSvfile', type7 => 'key', name8 => 'TSdfile', type8 => 'key', name9 => 'TScmap', type9 => 'text', name10 => 'TSochange', type10 => 'intv' }, 0 => { tab_count => 9, keyAttrs => 3, name0 => 'TSshelf', type0 => 'intv', key0 => 'yes', name1 => 'TSname', type1 => 'key', key1 => 'yes', name2 => 'TSseq', type2 => 'int', key2 => 'yes', name3 => 'TSwchange', type3 => 'intv', name4 => 'TSparent', type4 => 'key', name5 => 'TStype', type5 => 'int', name6 => 'TSpath', type6 => 'int', name7 => 'TSvfile', type7 => 'key', name8 => 'TSdfile', type8 => 'key', name9 => 'TScmap', type9 => 'text' } }, 'db.templatewx' => { 3 => { tab_count => 12, keyAttrs => 3, name0 => 'TWclient', type0 => 'key', key0 => 'yes', name1 => 'TWname', type1 => 'key', key1 => 'yes', name2 => 'TWseq', type2 => 'int', key2 => 'yes', name3 => 'TWwchange', type3 => 'intv', name4 => 'TWparent', type4 => 'key', name5 => 'TWtype', type5 => 'int', name6 => 'TWpath', type6 => 'int', name7 => 'TWvfile', type7 => 'key', name8 => 'TWdfile', type8 => 'key', name9 => 'TWcmap', type9 => 'text', name10 => 'TWochange', type10 => 'intv', name11 => 'TWuser', type11 => 'key', name12 => 'TWaction', type12 => 'int8' }, 2 => { tab_count => 12, keyAttrs => 3, name0 => 'TWclient', type0 => 'key', key0 => 'yes', name1 => 'TWname', type1 => 'key', key1 => 'yes', name2 => 'TWseq', type2 => 'int', key2 => 'yes', name3 => 'TWwchange', type3 => 'intv', name4 => 'TWparent', type4 => 'key', name5 => 'TWtype', type5 => 'int', name6 => 'TWpath', type6 => 'int', name7 => 'TWvfile', type7 => 'key', name8 => 'TWdfile', type8 => 'key', name9 => 'TWcmap', type9 => 'text', name10 => 'TWochange', type10 => 'intv', name11 => 'TWuser', type11 => 'key', name12 => 'TWaction', type12 => 'int8' }, 1 => { tab_count => 10, keyAttrs => 3, name0 => 'TWclient', type0 => 'key', key0 => 'yes', name1 => 'TWname', type1 => 'key', key1 => 'yes', name2 => 'TWseq', type2 => 'int', key2 => 'yes', name3 => 'TWwchange', type3 => 'intv', name4 => 'TWparent', type4 => 'key', name5 => 'TWtype', type5 => 'int', name6 => 'TWpath', type6 => 'int', name7 => 'TWvfile', type7 => 'key', name8 => 'TWdfile', type8 => 'key', name9 => 'TWcmap', type9 => 'text', name10 => 'TWochange', type10 => 'intv' }, 0 => { tab_count => 9, keyAttrs => 3, name0 => 'TWclient', type0 => 'key', key0 => 'yes', name1 => 'TWname', type1 => 'key', key1 => 'yes', name2 => 'TWseq', type2 => 'int', key2 => 'yes', name3 => 'TWwchange', type3 => 'intv', name4 => 'TWparent', type4 => 'key', name5 => 'TWtype', type5 => 'int', name6 => 'TWpath', type6 => 'int', name7 => 'TWvfile', type7 => 'key', name8 => 'TWdfile', type8 => 'key', name9 => 'TWcmap', type9 => 'text' } }, 'db.ticket' => { 1 => { tab_count => 5, keyAttrs => 2, name0 => 'UTuser', type0 => 'key', key0 => 'yes', name1 => 'UThost', type1 => 'key', key1 => 'yes', name2 => 'UTticket', type2 => 'text', name3 => 'UTstate', type3 => 'int', name4 => 'UTtoken', type4 => 'text', name5 => 'UTupdate', type5 => 'intv' }, 0 => { tab_count => 5, keyAttrs => 2, name0 => 'UTuser', type0 => 'key', key0 => 'yes', name1 => 'UThost', type1 => 'key', key1 => 'yes', name2 => 'UTticket', type2 => 'text', name3 => 'UTstate', type3 => 'int', name4 => 'UTtoken', type4 => 'text', name5 => 'UTupdate', type5 => 'intv' } }, 'db.ticket.rp' => { 1 => { tab_count => 5, keyAttrs => 2, name0 => 'UTuser', type0 => 'key', key0 => 'yes', name1 => 'UThost', type1 => 'key', key1 => 'yes', name2 => 'UTticket', type2 => 'text', name3 => 'UTstate', type3 => 'int', name4 => 'UTtoken', type4 => 'text', name5 => 'UTupdate', type5 => 'intv' }, 0 => { tab_count => 5, keyAttrs => 2, name0 => 'UTuser', type0 => 'key', key0 => 'yes', name1 => 'UThost', type1 => 'key', key1 => 'yes', name2 => 'UTticket', type2 => 'text', name3 => 'UTstate', type3 => 'int', name4 => 'UTtoken', type4 => 'text', name5 => 'UTupdate', type5 => 'intv' } }, 'db.topology' => { 3 => { tab_count => 11, keyAttrs => 4, name0 => 'SVaddr', type0 => 'text', key0 => 'yes', name1 => 'SVdaddr', type1 => 'text', key1 => 'yes', name2 => 'SVid', type2 => 'key', key2 => 'yes', name3 => 'SVdate', type3 => 'intv', key3 => 'yes', name4 => 'SVtype', type4 => 'text', name5 => 'SVencryption', type5 => 'text', name6 => 'SVuser', type6 => 'key', name7 => 'SVlsdate', type7 => 'intv', name8 => 'SVrectype', type8 => 'int', name9 => 'SVtaddr', type9 => 'text', name10 => 'SVtdaddr', type10 => 'text', name11 => 'SVtid', type11 => 'key' }, 2 => { tab_count => 11, keyAttrs => 4, name0 => 'SVaddr', type0 => 'text', key0 => 'yes', name1 => 'SVdaddr', type1 => 'text', key1 => 'yes', name2 => 'SVid', type2 => 'key', key2 => 'yes', name3 => 'SVdate', type3 => 'intv', key3 => 'yes', name4 => 'SVtype', type4 => 'text', name5 => 'SVencryption', type5 => 'text', name6 => 'SVuser', type6 => 'key', name7 => 'SVlsdate', type7 => 'intv', name8 => 'SVrectype', type8 => 'int', name9 => 'SVtaddr', type9 => 'text', name10 => 'SVtdaddr', type10 => 'text', name11 => 'SVtid', type11 => 'key' }, 1 => { tab_count => 7, keyAttrs => 4, name0 => 'SVaddr', type0 => 'text', key0 => 'yes', name1 => 'SVdaddr', type1 => 'text', key1 => 'yes', name2 => 'SVid', type2 => 'key', key2 => 'yes', name3 => 'SVdate', type3 => 'intv', key3 => 'yes', name4 => 'SVtype', type4 => 'text', name5 => 'SVencryption', type5 => 'text', name6 => 'SVuser', type6 => 'key', name7 => 'SVlsdate', type7 => 'intv' }, 0 => { tab_count => 6, keyAttrs => 4, name0 => 'SVaddr', type0 => 'text', key0 => 'yes', name1 => 'SVdaddr', type1 => 'text', key1 => 'yes', name2 => 'SVid', type2 => 'key', key2 => 'yes', name3 => 'SVdate', type3 => 'intv', key3 => 'yes', name4 => 'SVtype', type4 => 'text', name5 => 'SVencryption', type5 => 'text', name6 => 'SVuser', type6 => 'key' } }, 'db.traits' => { 1 => { tab_count => 3, keyAttrs => 2, name0 => 'TTtraitlot', type0 => 'intv', key0 => 'yes', name1 => 'TTname', type1 => 'key', key1 => 'yes', name2 => 'TTtype', type2 => 'int', name3 => 'TTvalue', type3 => 'octets' }, 0 => { tab_count => 3, keyAttrs => 2, name0 => 'TTtraitlot', type0 => 'intv', key0 => 'yes', name1 => 'TTname', type1 => 'key', key1 => 'yes', name2 => 'TTtype', type2 => 'int', name3 => 'TTvalue', type3 => 'octets' } }, 'db.trigger' => { 3 => { tab_count => 6, keyAttrs => 1, name0 => 'TRseq', type0 => 'int', key0 => 'yes', name1 => 'TRname', type1 => 'key', name2 => 'TRmapflag', type2 => 'int', name3 => 'TRdfile', type3 => 'key', name4 => 'TRtfile', type4 => 'key', name5 => 'TRtrigger', type5 => 'int', name6 => 'TRaction', type6 => 'text' }, 2 => { tab_count => 5, keyAttrs => 1, name0 => 'TRseq', type0 => 'int', key0 => 'yes', name1 => 'TRname', type1 => 'key', name2 => 'TRmapflag', type2 => 'int', name3 => 'TRdfile', type3 => 'key', name4 => 'TRtrigger', type4 => 'int', name5 => 'TRaction', type5 => 'text' }, 1 => { tab_count => 5, keyAttrs => 1, name0 => 'TRseq', type0 => 'int', key0 => 'yes', name1 => 'TRname', type1 => 'key', name2 => 'TRmapflag', type2 => 'int', name3 => 'TRdfile', type3 => 'key', name4 => 'TRtrigger', type4 => 'int', name5 => 'TRaction', type5 => 'text' }, 0 => { tab_count => 4, keyAttrs => 1, name0 => 'TRseq', type0 => 'int', key0 => 'yes', name1 => 'TRname', type1 => 'key', name2 => 'TRmapflag', type2 => 'int', name3 => 'TRdfile', type3 => 'key', name4 => 'TRaction', type4 => 'text' } }, 'db.upgrades' => { 1 => { tab_count => 5, keyAttrs => 1, name0 => 'UPseq', type0 => 'int', key0 => 'yes', name1 => 'UPname', type1 => 'text', name2 => 'UPstate', type2 => 'int', name3 => 'UPstartdate', type3 => 'intv', name4 => 'UPenddate', type4 => 'intv', name5 => 'UPinfo', type5 => 'text' }, 0 => { tab_count => 5, keyAttrs => 1, name0 => 'UPseq', type0 => 'int', key0 => 'yes', name1 => 'UPname', type1 => 'text', name2 => 'UPstate', type2 => 'int', name3 => 'UPstartdate', type3 => 'intv', name4 => 'UPenddate', type4 => 'intv', name5 => 'UPinfo', type5 => 'text' } }, 'db.upgrades.rp' => { 1 => { tab_count => 5, keyAttrs => 1, name0 => 'UPseq', type0 => 'int', key0 => 'yes', name1 => 'UPname', type1 => 'text', name2 => 'UPstate', type2 => 'int', name3 => 'UPstartdate', type3 => 'intv', name4 => 'UPenddate', type4 => 'intv', name5 => 'UPinfo', type5 => 'text' }, 0 => { tab_count => 5, keyAttrs => 1, name0 => 'UPseq', type0 => 'int', key0 => 'yes', name1 => 'UPname', type1 => 'text', name2 => 'UPstate', type2 => 'int', name3 => 'UPstartdate', type3 => 'intv', name4 => 'UPenddate', type4 => 'intv', name5 => 'UPinfo', type5 => 'text' } }, 'db.user' => { 8 => { tab_count => 14, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv', name13 => 'USattempts', type13 => 'intv', name14 => 'USauth', type14 => 'int' }, 7 => { tab_count => 14, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv', name13 => 'USattempts', type13 => 'intv', name14 => 'USauth', type14 => 'int' }, 6 => { tab_count => 13, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv', name13 => 'USattempts', type13 => 'intv' }, 5 => { tab_count => 12, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv' }, 4 => { tab_count => 11, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv' }, 3 => { tab_count => 9, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv' }, 2 => { tab_count => 6, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text' }, 1 => { tab_count => 0, name0 => undef, type0 => 'MISSING' }, 0 => { tab_count => 5, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USojobview', type2 => 'int8', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text' } }, 'db.user.rp' => { 8 => { tab_count => 14, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv', name13 => 'USattempts', type13 => 'intv', name14 => 'USauth', type14 => 'int' }, 7 => { tab_count => 14, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv', name13 => 'USattempts', type13 => 'intv', name14 => 'USauth', type14 => 'int' }, 6 => { tab_count => 13, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv', name13 => 'USattempts', type13 => 'intv' }, 5 => { tab_count => 12, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv', name12 => 'USpassexpire', type12 => 'intv' }, 4 => { tab_count => 11, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv', name10 => 'UStype', type10 => 'int', name11 => 'USpassdate', type11 => 'intv' }, 3 => { tab_count => 9, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text', name7 => 'USstrength', type7 => 'int', name8 => 'USticket', type8 => 'text', name9 => 'USenddate', type9 => 'intv' }, 2 => { tab_count => 6, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USjobview', type2 => 'text', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text', name6 => 'USpassword', type6 => 'text' }, 1 => { tab_count => 0, name0 => undef, type0 => 'MISSING' }, 0 => { tab_count => 5, keyAttrs => 1, name0 => 'USuser', type0 => 'key', key0 => 'yes', name1 => 'USemail', type1 => 'text', name2 => 'USojobview', type2 => 'int8', name3 => 'USupdate', type3 => 'intv', name4 => 'USaccess', type4 => 'intv', name5 => 'USfullname', type5 => 'text' } }, 'db.uxtext' => { 0 => { tab_count => 2, keyAttrs => 3, name0 => 'ITword', type0 => 'key', key0 => 'yes', name1 => 'ITattr', type1 => 'int', key1 => 'yes', name2 => 'ITvalue', type2 => 'key', key2 => 'yes' } }, 'db.view' => { 2 => { tab_count => 5, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key', name5 => 'VIctype', type5 => 'int' }, 1 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' } }, 'db.view.rp' => { 2 => { tab_count => 5, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key', name5 => 'VIctype', type5 => 'int' }, 1 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' }, 0 => { tab_count => 4, keyAttrs => 2, name0 => 'VIname', type0 => 'key', key0 => 'yes', name1 => 'VIseq', type1 => 'int', key1 => 'yes', name2 => 'VImapflag', type2 => 'int', name3 => 'VIvfile', type3 => 'key', name4 => 'VIdfile', type4 => 'key' } }, 'db.working' => { 11 => { tab_count => 18, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key', name18 => 'WOstatus', type18 => 'int' }, 10 => { tab_count => 18, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key', name18 => 'WOstatus', type18 => 'int' }, 9 => { tab_count => 17, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key' }, 8 => { tab_count => 16, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int' }, 7 => { tab_count => 15, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int' }, 6 => { tab_count => 14, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv' }, 5 => { tab_count => 14, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv' }, 4 => { tab_count => 13, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOtraitlot', type13 => 'intv' }, 3 => { tab_count => 12, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet' }, 2 => { tab_count => 11, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int' }, 1 => { tab_count => 10, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOtype', type6 => 'int', name7 => 'WOaction', type7 => 'int8', name8 => 'WOchange', type8 => 'intv', name9 => 'WOmodtime', type9 => 'intv', name10 => 'WOislocked', type10 => 'int' }, 0 => { tab_count => 10, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOtype', type6 => 'int', name7 => 'WOaction', type7 => 'int8', name8 => 'WOchange', type8 => 'intv', name9 => 'WOmodtime', type9 => 'intv', name10 => 'WOislocked', type10 => 'int' } }, 'db.workingg' => { 1 => { tab_count => 20, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key', name18 => 'WOstatus', type18 => 'int', name19 => 'WObsha', type19 => 'octet20', name20 => 'WOrepo', type20 => 'key' }, 0 => { tab_count => 20, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key', name18 => 'WOstatus', type18 => 'int', name19 => 'WObsha', type19 => 'octet20', name20 => 'WOrepo', type20 => 'key' } }, 'db.workingx' => { 11 => { tab_count => 18, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key', name18 => 'WOstatus', type18 => 'int' }, 10 => { tab_count => 18, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key', name18 => 'WOstatus', type18 => 'int' }, 9 => { tab_count => 17, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int', name17 => 'WOmfile', type17 => 'key' }, 8 => { tab_count => 16, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int', name16 => 'WOctype', type16 => 'int' }, 7 => { tab_count => 15, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv', name15 => 'WOtampered', type15 => 'int' }, 6 => { tab_count => 14, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv' }, 5 => { tab_count => 14, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOsize', type13 => 'int64', name14 => 'WOtraitlot', type14 => 'intv' }, 4 => { tab_count => 13, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet', name13 => 'WOtraitlot', type13 => 'intv' }, 3 => { tab_count => 12, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int', name12 => 'WOdigest', type12 => 'octet' }, 2 => { tab_count => 11, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOvirtual', type6 => 'int', name7 => 'WOtype', type7 => 'int', name8 => 'WOaction', type8 => 'int8', name9 => 'WOchange', type9 => 'intv', name10 => 'WOmodtime', type10 => 'intv', name11 => 'WOislocked', type11 => 'int' }, 1 => { tab_count => 10, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOtype', type6 => 'int', name7 => 'WOaction', type7 => 'int8', name8 => 'WOchange', type8 => 'intv', name9 => 'WOmodtime', type9 => 'intv', name10 => 'WOislocked', type10 => 'int' }, 0 => { tab_count => 10, keyAttrs => 1, name0 => 'WOcfile', type0 => 'key', key0 => 'yes', name1 => 'WOdfile', type1 => 'key', name2 => 'WOclient', type2 => 'key', name3 => 'WOuser', type3 => 'key', name4 => 'WOhrev', type4 => 'intv', name5 => 'WOwrev', type5 => 'intv', name6 => 'WOtype', type6 => 'int', name7 => 'WOaction', type7 => 'int8', name8 => 'WOchange', type8 => 'intv', name9 => 'WOmodtime', type9 => 'intv', name10 => 'WOislocked', type10 => 'int' } }, dl => { 0 => { tab_count => 4, other => 1, table => 'dl', version => 0, name0 => 'DLlbrFile', type0 => 'key', key0 => 'yes', name1 => 'DLlbrRev', type1 => 'key', key1 => 'yes', name2 => 'DLlbrType', type2 => 'int', name3 => 'DLcommand', type3 => 'key', name4 => 'DLfilespec', type4 => 'key' } }, ex => { 0 => { tab_count => 1, other => 1, table => 'ex', version => 0, name0 => 'EXpid', type0 => 'int64', name1 => 'EXtime', type1 => 'int64' } }, mx => { 0 => { tab_count => 1, other => 1, table => 'mx', version => 0, name0 => 'MXpid', type0 => 'int64', name1 => 'MXtime', type1 => 'int64' } }, pk => { 0 => { tab_count => 4, other => 1, table => 'pk', version => 0, name0 => 'PKdepotName', type0 => 'key', key0 => 'yes', name1 => 'PKrepoName', type1 => 'key', key1 => 'yes', name2 => 'PKaccess', type2 => 'int', name3 => 'PKcommand', type3 => 'key', name4 => 'PKfilespec', type4 => 'key' } }, 'tiny.db' => { 0 => { tab_count => 1, name0 => 'TIkey', type0 => 'key', key0 => 'yes', name1 => 'TIvalue', type1 => 'octets' } }, vv => { 0 => { tab_count => 3, other => 1, table => 'vv', version => 0, name0 => 'VVversion', type0 => 'int', name1 => 'VVtable', type1 => 'key', name2 => 'VVkey', type2 => 'key', name3 => 'VVvalue', type3 => 'key' } } }