#! /bin/sh # This is a shell archive. Type 'sh ' to unpack. echo x - README cat >README <<'MKSHAR_EOF' Here are several utilities that I find useful and wished to share with the Informix community. Most I have written, others were written for us by Informix support personnel under contract, proper attribution is given below. ---------------------------------------------------------------------------- Utility: aspace Synopsis: Replacement/wrapper for tbspace/onspaces which can track chunks across multiple instances preventing overlapping chunks from being added even if the new chunk overlaps a chunk in another instance or is a symbolic link pointing to a device/file already being used. Author(s): Jacob Salomon Art S. Kagel Version(s) supported: Online 5.0x, ODS 6, ODS 7.xx+. File(s): aspace - User script - requires KSH or BASH. lschunks.awk - Underlying awk script. printchunks - script to report on the aspace database's contents grouped by server (TBCONFIG/ONCONFIG value). printchunks.p - perl script that does the formatting for printchunks. .chunkList - Current aspace database file. .chunkList.bu.*- Historical aspace database files. Comments: Only need to be careful if you have 5.0x and 6/7 versions on the same machine to unset TBCONFIG when working on the 6/7 instance or ONCONFIG when working on the 5.0x instance so that aspace does not get confused. Usage: Use the -AA option to create the initial database of existing chunks. This needs to be run against each engine on the machine. Once the database exists it will be maintained EXCEPT after deleting chunks or dbspaces since aspace does not implement the onspace -d option you will have to delete the .chunkList file and run aspace -AA again against each engine after a drop. The -CC option is a test to see if a chunk would create a conflict and -CI can be used to force in a chunk added via onspaces or onmonitor. Aspace supports the onspaces -c and -a options and calls onspaces after checking for conflicts, if onspaces reports success (it may fail for example if the device is not large enough to hold the chunk or if the filesystem for a COOKED chunk is too full) then aspace updates its database (the .chunkList file). Note that major/minor numbers of device files sometimes change between boots depending on the OS and volume manager, if this happens the next time you run aspace it will update the database with the new numbers before checking for any conflict, this is noted with a warning about each such chunk. The check and update can be forced using aspace -CV. ---------------------------------------------------------------------------- Utility: dbsavail.ec Synopsis: Printout total space and available space by dbspace name with total. Creates a stored procedure, dbsavail() to do most work and you can execute that directly also to see the report. Author(s): Art S. Kagel Version(s) supported: 7.0x File(s): dbsavail.ec Comments: There is a shell/perl script version of this included also which works on 5.0x. Since the script is based on onstat/tbstat it is only up to date as of the last time the reserved pages were flushed. ---------------------------------------------------------------------------- Utility: dbsavail.sh Synopsis: Printout total space and available space by dbspace name with total. Author(s): Art S. Kagel Jake Salomon Version(s) supported: 5.0x/6.0x/7.xx+ File(s): dbsavail.sh dbsavail.sh.64bit dbsavail.p2 Comments: Requires a UNIX-like sort utility, KSH, and PERL. If you have a 64bit version of IDS (versions ending in .FC*) you will need to use the .64bit version of the script as the format of the onstat report it interprets has changed in those releases. ---------------------------------------------------------------------------- Utility: dbstruct.ec Synopsis: Produces "C", ESQL/C, and structured FORTRAN data structures from a database/table schema. Author(s): Art S. Kagel Version(s) supported: 5.0x/6/7.xx+ File(s): dbstruct.ec Comments: ---------------------------------------------------------------------------- Utility: defragtable.ec Synopsis: Compresses a table causing the table to live in as few extents as possible. Author(s): Art S. Kagel Version(s) supported: 5.0x/6/7.xx+ File(s): defragtable.ec Comments: ---------------------------------------------------------------------------- Utility: dostats.ec Dostats has been removed. An updated version is part of the package utils2_ak the version formerly included here was seriously outdated. ---------------------------------------------------------------------------- MKSHAR_EOF echo x - aspace cat >aspace <<'MKSHAR_EOF' #!/usr/bin/ksh # apsace - Shell script to replace tbspaces/onspaces by a call to an # awk script - lschunks.awk. # Purpose - Add dbspaces and chunks with the proviso that no chunk # overlaps a chunk of another OnLine instance. This is supported by a # flat file in the informix home directory. $INFORMIXDIR if no use # here, since different instances may be using different values for # this environmant variable. # # As for the location of the awk script itself - this script assumes it # is in the informix home directory as well. The user should feel free # to adjust the script for his environment. # # Usage - Use this command as you would use tbspaces or onspaces # Limitation - This cannot [yet] be used to drop a dbspace. # #----------------------------------------------------------------------- aspacehelp() { # Internal shell function to display user help echo "" echo Usages to wrap onspaces calls: echo $cm -a spacename -p pathname -o offset -s size [-m path offset] echo $cm "-c {-d dbspace [-t] | -b blobspace -g pagesize}" echo "\t-p pathname -o offset -s size [-m path offset]" echo "" echo Specialized usages: echo $cm -AA echo "\tAdd the current instance's chunks to the list." echo $cm -CC -p pathname -o offset -s size echo "\tValidate parameters for overlap, don't update list." echo $cm -CI -p pathname -o offset -s size echo "\tValidate parameters for overlap, update list." echo $cm -CV echo "\tValidate current chunk list for changes in inode & major/minor " echo "\tdevice numbers due to reboot and update." } # # Actual script execution starts here. # Some basic initializations. # Get location of this script so we can find lschunks.awk later. shcm=$0 cm=${shcm##*/} edir=${shcm%/*} # It is in current directory, force that. if [[ $shcm == $edir ]]; then edir=.; fi # echo shcm=$shcm # echo cm=$cm # echo edir=$edir lc=0 mirr_pos="0" awk_pgunit="" awk_mpath="" awk_moffset="" # First parameter determines the expected next parameters a_state="0" # Assume a parameter screw-up case $1 in -c) a_state="SPACE" # Planning to create a space opt_string="cd:tb:g:p:o:s:m" ;; -a) a_state="CHUNK" # Planning to add a chunk to an exting opt_string="a:p:o:s:m" # dbspace/blobspace/tempspace ;; -AA) a_state="ALL" ;; -AC) echo Use aspace command to add a chunk to a dbspace ;; -AD) echo Use aspace command to a dbspace to the system ;; -AT) echo Use aspace command to a temp dbspace to the system ;; -AB) echo Use aspace command to a blobspace to the system ;; -CC) a_state="CHECK" ;; -CI) a_state="INSERT" ;; -CV) a_state="VERIFY" ;; *) echo Option $1 is not available for $0 or for the onspaces command bad_param=1 ;; esac # # Passed the first round of validations? # if [ $a_state = "0" ] then echo $0: Invalid option entered. aspacehelp exit 1 fi # # OK, still here. So I can start looking at parameters. # bad_param=1 # Pessimist - set WRONG indicator # The following is a long CASE statement, containing a nested case. # (Just pointing this out to the squeamish.) case $a_state in ALL) awk_option=-AA bad_param=0 # Allow us to proceed ;; VERIFY) awk_option=-CV bad_param=0 # OK to proceed ;; CHECK | INSERT) # Requires 3 more positional parameters if [[ -n "$2" && -n "$3" && -n "$4" ]] then awk_path=$2 awk_offset=$3 awk_size=$4 bad_param=0 # Allow us to proceed else echo -CC or -CI command requires 3 additional parameters bad_param=1 # Don't allow us to proceed fi if [ $a_state = "CHECK" ] then # Time to separate CC and CI logic awk_option=-CC else awk_option=-CI fi bad_param=0 # Allow us to proceed ;; # SPACE | CHUNK ) # Adding a dbspace/blobspace/temp or chunk # Adding DBSPACE/CHUNK to the system. Check & place params bad_param=0 # For this loop, start optimiztic echo optstring=$opt_string while getopts $opt_string lc_option do case $lc_option in "c") # Create a dbspace awk_option=-AD ;; "d") # Name of the dpspace awk_dbspace=$OPTARG ;; "t") # Make that a temp dbspace awk_option=-AT ;; "b") # Make that dbspace a BLOB space awk_option=-AB awk_dbspace=$OPTARG # Capture the name of the space ;; "g") # Blobspace must specify blob-page size awk_pgunit=$OPTARG # Capture blob-page size from OPTARG ;; "p") # Path name of device/file awk_path=$OPTARG ;; "o") # Offset of the chunk into the path awk_offset=$OPTARG ;; "s") # Size of the [primary] chunk awk_size=$OPTARG ;; "m") # Specifying 2 mirror parameters. getopts no direct help mirr_pos=$OPTIND # Isolate position of -m option in line ;; "a") # Adding a chunk [& mirror] to an existing dbspace awk_option=-AC awk_dbspace=$OPTARG ;; *) # Any other option echo Option $lc_option not recognized bad_param=1 ;; esac done ;; esac # # It is, of course, possible that a user omitted a parameter from the # commands. This will show up either within the lschunks awk-script or # in the execution of onspaces. Some parameter errors got exposed # here, of course. if [ $bad_param = 1 ] then aspacehelp exit 1 fi # # One piece of unfinished business: the -m parameter, if used, had 2 # operands. getopts can't retrieve 2+ operands so I do it manually. # if [ $mirr_pos -ne 0 ] then shift $mirr_pos # Get mirror parameter in position $1 $2 awk_mpath=$1 awk_moffset=$2 fi # # We now have all paramaters we need to form the awk command line. # Assume the awk-script is in the same place we found this script # ifmxhome=`grep informix /etc/passwd|cut -f6 -d:` awk_command="awk -f ${edir}/lschunks.awk -- " # Auspicious start case $awk_option in -AA | -CV) awk_command=${awk_command}${awk_option} ;; -CC | -CI ) awk_command=${awk_command}" $awk_option" awk_command=${awk_command}" $awk_path $awk_offset $awk_size" ;; -AC | -AD | -AT | -AB) awk_command=${awk_command}" $awk_option" awk_command=${awk_command}" $awk_dbspace $awk_pgunit" awk_command=${awk_command}" $awk_path $awk_offset $awk_size" awk_command=${awk_command}" $awk_mpath $awk_moffset" # Recall that if not a blob space, awk_pgunit was null. ;; esac echo $awk_command # Just to reassure the user of what I'm executing $awk_command # Hit it! MKSHAR_EOF echo x - lschunks.awk cat >lschunks.awk <<'MKSHAR_EOF' # lschunks.awk - awk script to maintain a database of chunks used by # ALL instances of OnLine on the current machine. The file name is # fixed as ~informix/.chunkList and uses the following format: # inodeNum trueInum offset k_length pathName informixdir configFile # inodeNum: I-Node number of the file/device containing the chunk # trueInum: The i-node number after chasing down symbolic links # offset: Offset, in K, of the chunk # length: Length, in K, of the chunk # mirror: P/M - Primary chunk or a mirror? # pathname: Kinda obvious, IMO ;-) # configfile: The value of $ONCONFIG or $TBCONFIG for the instance # using that chunk # servername: The value of INFORMIXSERVER, if appropriate # informixdir: The value of $INFORMIXDIR for the instance using that # chunk # # The fields , as a set, must be unique # within the file but this is not enough - I will be checking for # overlap as I build the array. # # The path parameters are what you would use to enter one of the # following commands: # tbspaces to add a dbspace or chunk # onspaces to add a dbspace or chunk # # Command options: # -AA Adds All of the current chunk definitions to the chunk list, # provided that no chunk inthe current instance conflicts with # any existing chunks. # -AC dbspace path offset length [m-path m-offset] # Adds a Chunk (and it's mirror) to the list, as well as running # the appropriate tbspaces/onspaces command, provided there is # no conflict. # -AD dbspace path offset length [m-path m-offset] # Creates a dbspace - its primary chunk [& mirror]. Otherwise, # identical to AC # -AT dbspace path offset length [m-path m-offset] # Creates a TEMP dbspace - primary chunk [& mirror]. Otherwise, # identical to AC # -AB blobspace pg_unit path offset length [m-path m-offset] # Creates a blobspace - its primary chunk [& mirror]. Otherwise # identical to -AC and AD. # -CC path offfset length # Checks if the proposed chunk would conflict with anything # already in the chunks list. # -CI path offset length Mirror # Performs same check as -CC, then inserts parms into chunk list # without adding chunk to system. Use this to register chunks # already in the OnLine system. # -CV # Just verify the current list and update inodes and/or major/minor # numbers if needed. # BEGIN { # Initialize the necesary environment # awkkfile = "lschunks.awk"; if (ARGC < 2) { usage(); exit (1); } lfname = infxhome = idir = icfg = iserv = spcmd = stcmd = ""; page_size = ""; if ((lc = init_params()) < 0) { usage(); # Remind user how to do it exit 1; # Bad operation - get out } if ((lc = init_env()) < 0) # Initialize variables with env info exit 1; # Hope it worked! list_size = init_list(); # Gather up the chunks list, as is # validate parameters and get to work! if (parm_optype == "-AA") chunks_added = add_current(); # validate current system else if (parm_optype == "-AC") chunks_added = generate_one("C"); # Validate params, add to list else if (parm_optype == "-AD") chunks_added = generate_one("D"); # Validate params, add to list else if (parm_optype == "-AB") chunks_added = generate_one("B"); # Validate params, add to list else if (parm_optype == "-AT") chunks_added = generate_one("T"); # Validate params, add to list else if (parm_optype == "-CC") chunks_valid = check_chunk(parm_path, parm_offset, parm_length); # Validate params, don't add else if (parm_optype == "-CI") chunks_valid = insert_chunk(parm_path, parm_offset, parm_length, parm_mirror); # Validate, add, don't run tbspaces else if (parm_optype == "-CV") print "Verify complete."; chunks_valid = 0; exit 0; } # #===================================================================== # init_params() - Initialize a set of global variables according to the # parameter list on the line. # Parameters: None, but implicitly uses the ARGV[] array # Local variables: # rval - Just a return value for validity. 0 if valid, -1 of bad op # Sets global variables: # parm_optype Type of operation # parm_dbspace Name of dbspace to add/expand # parm_path Path name of device # parm_offset Offset into device # parm_length Length of chunk # parm_mpath Path of mirror for chunk # parm_moffset offset of mirror for chunk # parm_pgunit page unit for adding a blobspace # parm_mirror Set only for CI - P or M for mirror status of a chunk #===================================================================== function init_params(rval){ rval = 0; # Start optimistic; parm_optype = ARGV[1]; # Operation type # # Note - no special processing for -AA. Just checking on valid # operation # if (parm_optype == "-AA") # Wants to generate/append to list rval = 0; # Dummy operation for the IF else if (parm_optype ~ "-A[CDT]") # if adding chunk, creating dbspace { # or creating a temp space parm_dbspace = ARGV[2]; # name of dbspace parm_path = ARGV[3]; # Name of device/file parm_offset = ARGV[4]; # Offset into the device parm_length = ARGV[5]; # Kinda obvious! parm_mpath = ARGV[6]; # Mirror, null if not specified parm_moffset = ARGV[7]; # Mirror as well. Null if not specified } else if (parm_optype == "-AB") # If creating a blob space, params are { # slightly different. parm_dbspace = ARGV[2]; # name of dbspace parm_pgunit = ARGV[3]; # Page unit of the blob space parm_path = ARGV[4]; # Name of device/file parm_offset = ARGV[5]; # Offset into the device parm_length = ARGV[6]; # Kinda obvious! parm_mpath = ARGV[7]; # Mirror, null if not specified parm_moffset = ARGV[8]; # Mirror as well. Null if not specified } else # # Function init_params() - Continued if (parm_optype ~ "-C[CI]") # Just want to check on a proposed chunk { # or register (insert) a chunk in list parm_path = ARGV[2]; # Name of device/file parm_offset = ARGV[3]; # Offset into the device parm_length = ARGV[4]; # Kinda obvious! parm_mirror = ARGV[5]; # Copy the mirror status (May be null) if (parm_mirror !~ "[PM]") # If not P or M parm_mirror = "?"; # Leave unknown; not all that important } else if (parm_optype == "-CV") { rval = 0 } else { rval = -1; # No valid operation. } return (rval); # Inform caller of status } # #===================================================================== # init_env() - Function to get vital info about the current environment. # Obtains such vitals such as: # - Full path name of the list file # - Environment variables INFORMIXDIR, TBCONFIG/ONCONFIG, # INFORMIXSERVER (if appropriate) # # Parameters: None. # Local Variables: # cmdl - Command line string # ifxsub - String representing a subdirectory # lc - loop counter or other spare variable # Sets global variables: # lfname - List File name # infxhome - Home directory for user informix # idir - Value of INFORMIXDIR # icfg - Value of ONCONFIG or TBCONFIG # iserv - Value of INFORMIXSERVER (if relevant) # spcmd - Command to use - onspaces or tbspaces # stcmd - Command to use - onstat or tbstat # page_size - Size of a page in this system, in K. # list_file - Name of file that holds the list of chunks #===================================================================== function init_env(cmdl, ifxsub, lc) { # To locate the list file, I need informix's home directory # cmdl = "grep informix /etc/passwd|cut -f6 -d:" # informix's home lc = cmdl | getline infxhome; # dir; Execute, capture output close(cmdl); # Close command line pipeline if (lc < 1) # If getline failed or EOF { printf("init_env(): <%s | getline> Failed; Error <%d>\n", cmdl, lc); return -1; # indicate failure } ifxsub = "etc"; # Help piece together path names # of ON/TB-config file lfname = ".chunkList"; # Naked file name, w/no directory list_file = infxhome "/" lfname; # *This* is the name of list file # # init_env() _ Continued # # To help maintain the .chunkList file, I need the values of # INFORMIXDIR and ONCONFIG (TBCONFIG for 5* systems). # config_env = "ONCONFIG"; # Assume DSA for the nonce idir = getenv("INFORMIXDIR"); # Get these environment variables icfg = getenv("ONCONFIG"); # ONCONFIG overrides TBCONFIG if (icfg == "") # but if no ONCONFIG { config_env = "TBCONFIG"; # DSA assumption was wrong icfg = getenv("TBCONFIG"); # then get value of TBCONFIG iserv = "(Pre-DSA)"; # make sure it looks "NON-DSA" } if (iserv == "") # If I have not set this above iserv = getenv("INFORMIXSERVER"); # Should be blank for 5.0 env if ((idir == "") || icfg == "") # If vital vars are not set { print "INFORMIXDIR and TB/ON-CONFIG must be set" ; return (-2); # Complain and get out } cfgpath = idir "/" ifxsub "/" icfg; # Full path of config file cmdl = "test -f '" cfgpath "'"; # Test if the config file exists if ((not_there = system(cmdl)) != 0) # using test -f { print "Cannot access file: <" cfgpath ">" ; exit 2; } if (config_env == "ONCONFIG") # If we are in a DSA environment { spcmd = "onspaces"; # Set up to use these commands stcmd = "onstat"; } else # Pre-DSA - Use tb-version { spcmd = "tbspaces"; stcmd = "tbstat"; } # Piece together command to find the buffer size cmdl = "grep BUFFSIZE " cfgpath "| awk '{print $2}' " lc = cmdl | getline page_size; close (cmdl) if (lc != 1) { printf("init_env(): <%s | getline> failed: Error <%d>\n", cmdl, lc); return (-3); } if ((page_size != 2048) && (page_size != 4096)) { page_size = 2048; printf("Defaulting to pagesize %d\n", page_size); } else printf("Using pagesize %d\n", page_size); page_size /= 1024; # I only want number of K in a page } # #===================================================================== # init_list() - Function to read the list file into an array (really a # set of arrays - awk is not kind to structures.) # Also sets up an array of major & minor device numners # indexed on inode numbers. # Parameters: None # Local Variables: # base_inum Inode number of the a path in the chunks list # trueinum True i-node number of that path, after chasing symbolic # links. # koffset The offset of that chunk onto the path name - in K # klength The length of that chunk # k_combine The "key" value for this chunk-list entry # lc Loop counter and overall useful variable # current_tin True inumber of the path in current loop round #===================================================================== function init_list(base_inum, trueinum, koffset, klength, k_combine, lc, current_tin) { changes = 0; lc = 0; # Use as entry counter while ((getline < list_file) > 0) # Read existing file, if present { base_inum = $1; trueinum = $2; koffset = $3; klength = $4; k_combine = trueinum "-" koffset "-" klength; # Key for list list_inum[k_combine] = base_inum; list_tinum[k_combine] = trueinum; list_offset[k_combine] = koffset; list_length[k_combine] = klength; list_end[k_combine] = koffset + klength - page_size; # Offset, in K, of the last page of the chunk list_mirror[k_combine] = $5; # Mirror status list_path[k_combine] = $6; list_icfg[k_combine] = $7; list_server[k_combine] = $8; list_idir[k_combine] = $9; current_tin = get_tinode(list_path[k_combine]); # Get true inode value # # Function init_list() - Continued # Note that someone may have re-linked a device/file # if (current_tin != list_tinum[k_combine]) { # If it has been re-linked printf("Warning: Path <%s> link or device has been changed\n", list_path[k_combine]); printf("\t from <%s> to <%s>\n", list_tinum[k_combine], current_tin); list_tinum[k_combine] = current_tin; # Copy new true value list_inum[k_combine] = get_inode(list_path[k_combine]); # and refresh the direct inumber changes++; } lc++; # Count up number of entries } close(list_file); if (changes) { output_all(); changes = 0; } return (lc); # Let caller know how many chunks # (List file may not exist yet) } # #===================================================================== # add_current - Function to add the current instances chunks to the # list. Of course, each chunk will be checked against # the existing list. # Parameters: None # Local Variables: # lc Local counter # getspace_cmd The completed tbstat or onstat command # chunk_flag indicates that the command is returning chunk data now # ovlstat Indicates we found an overlap in the current row # ovl_whoops Indicates we found an overlap somewehere # rval Return value # # Returns: # - If no overlap, the number of chunks in current instance # - If overlap, -(number of overlaps detected) #===================================================================== function add_current(lc, getspace_cmd, chunk_flag, ovlstat, ovl_whoops) { getspace_cmd = stcmd " -d"; # tbstat or onstat -d chunk_flag = 0; lc = ovl_whoops =0; # Initialize counters while (getspace_cmd | getline) # loop to get onstat output { if ($1 == "Chunks") # Reached the line that says "Chunks" { chunk_flag = 1; # Indicate that we are now entertaining continue; # Skip the chunk list columns heading } if (chunk_flag == 1) # Previous line said "Chunks" { chunk_flag = 2; # Indicate we're past that as well continue; # skip heading line for list of chunks } if (chunk_flag != 2) # If we're not yet up to the chunk list continue; # then don't bother with this line. # Still here: We're into the chunk list in the onstat output # # Note that getline sets NF. Last data on line is path of chunk # if (NF < 8) # On "totals" line @ end of chunks list continue; # quit this final round cur_path = $NF; # Get path name flag_field_num = NF - 1; # as well as field num for chunk flags cur_offset = $4 * page_size; # and offset, in K cur_length = $5 * page_size; # and size, in K cur_flag = $flag_field_num; # # Function add_current() - Continued cur_inode = get_inode(cur_path); # Get inumber; reference cur_tinode = get_tinode(cur_path); # & true inumber cur_combine = cur_tinode "-" cur_offset "-" cur_length # Above is the key to the entry in the table. if ((scan_overlap(cur_path, cur_offset, cur_length)) != 0) { ovl_whoops++; continue; # Tally & go for the next record } # Still here in this round of the loop: The chunk does not # overlap anything in the chunks list. Sooo.... # list_inum[cur_combine] = cur_inode; # add this entry to the list_tinum[cur_combine] = cur_tinode; # internal chunk list list_offset[cur_combine] = cur_offset; # array list_length[cur_combine] = cur_length; list_end[cur_combine] = list_offset[cur_combine] + list_length[cur_combine] - page_size; # Calculate end for overlap # Primary or mirror list_mirror[cur_combine] = substr( cur_flag, 1, 1 ); list_path[cur_combine] = cur_path; list_icfg[cur_combine] = icfg; # These 3 items are not list_server[cur_combine] = iserv; # really needed but will list_idir[cur_combine] = idir; # ease I/O later. # lc++; # Count how many legit chunks we found } close (getspace_cmd); # Conserve file descriptors # # OK, list has been built. Note that if the current instance # already has overlapping chunks (screw-up with links), they will be # detected above. # # Now, about a return value: I'd like to return the number of legit # chunks. But if there was ANY overlap, even internal, I don't # want to add this instance to the chunks list. So here's the deal: # If no overlaps, return the number of chunks in this instance. # Otherwise, return negative(number of overlaps detected). # if (ovl_whoops > 0) # Any overlaps detected? rval = - ovl_whoops; # Yes - Whoops! else { rval = lc; # No - set for count of chunks output_all(); # and [re]write out entire array } return (rval) } # #===================================================================== # generate_one() - Generate one or two entries in the chunks list. # This function also generates the tbspaces/onspaces # command to actually insert the entries into the list. # Parameters: # gen_type: D for creating a DBspace, # B to generate a BLOBspace, # T to generate a TEMP space, # C for adding a chunk to a DBspace. # In all cases, mirror parameters are handled. # Local Variables: # space_cmd String that actually generates the add chunk or create # dbspace command. # lc Loop counter. # rval A return value - 0, 1 or 2 _ Number of chunk entries # added tothe list. # Globals Used: The parm_* variables initialized in init_params #===================================================================== function generate_one(gen_type, lc, space_cmd, rval) { rval = 0; # Set this now - pessimistically if (gen_type ~ "[CTD]") { # tb/onspaces -c to create [temp] dbspace or add a chunk # Piece together onspaces command if (gen_type == "C") # If just adding a chunk to an { # existing dbspapce/BLOBspace space_cmd = spcmd " -a " parm_dbspace; } else { # Creating dbspace - maybe TEMP space_cmd = spcmd " -c -d " parm_dbspace; if (gen_type == "T") # If it happens to be a TEMP space space_cmd = space_cmd " -t"; # att TEMP flag # Resume composing onspaces command } # Above took care of minor differences in onspaces command for # creating DBspace or adding a chunk to it. Rest of command # is same for all 3 possibilities: Dbspace, TempSpace, AddChunk # space_cmd = space_cmd " -p " parm_path " -o " parm_offset \ " -s " parm_length if (parm_mpath != "") # If mirroring is involved { space_cmd = space_cmd " -m " parm_mpath " " parm_moffset } } # # Function generate_one() - Continued else if (gen_type == "B") { # Creating a blob space space_cmd = spcmd " -c -b "; # Start piecing command together space_cmd = space_cmd parm_dbspace " -g " parm_pgunit \ " -p " parm_path " -o " parm_offset \ " -s " parm_length if (parm_mpath != "") # If mirroring is involved { space_cmd = space_cmd " -m " parm_mpath " " parm_moffset } } else { printf ("generate_one(%s) - Bad parameter\n", gen_type); usage(); } # onspaces command has been generated. But before I use it # I want to check if primary or mirror chunk pass muster. # lc = scan_overlap(parm_path, parm_offset, parm_length); if (lc > 0) # If we have overlap return (rval); # no chunks will be added if (parm_mpath != "") # If a mirror chunk is involved { lc = scan_overlap(parm_mpath, parm_moffset, parm_length); if (lc > 0) # If we have overlap return (rval); # no chunks will be added } # At this point, the tb/onspaces command is ready to run. # lc = system(space_cmd); # Execute the command if (lc == 0) # If command worked - space added! { rval += append_one(parm_path, parm_offset, parm_length, "P"); if (parm_mpath != "") # If mirrors are involved { rval += append_one(parm_mpath, parm_moffset, parm_length, "M"); } } # rval has counted the number of chunks added output_all(); # Rewrite the chunks list file return (rval); } # #===================================================================== # check_chunk() - Simply check if a proposed chunk would overlap # anything in the current list. # Parameters: # p_path # p_offset # p_length # # Returns: negative number if it would overlap, 0 if no overlap # Local Variables: # rval return value #===================================================================== function check_chunk(p_path, p_offset, p_length, rval) { rval = 0; # Start optimistic rval = - scan_overlap(p_path, p_offset, p_length); return (rval); # All messages are taken care of. } # # #===================================================================== # insert_chunk() - Insert the given parameters into the list, provided # they don't overlap other chunks. # This function is used for registering a chunk already in an OnLine # instance. # Parameters: # p_path # p_offset # p_length # p_mirror P or M # # Returns: negative number if it would overlap, 0 if no overlap # Local Variables: # rval return value #===================================================================== function insert_chunk(p_path, p_offset, p_length, p_mirror, rval) { rval = check_chunk(p_path, p_offset, p_length); if (rval == 0) { rval = append_one(p_path, p_offset, p_length, p_mirror); output_all(); } return (rval) ; } # #===================================================================== # append_one() - Place the given parameters in the list, in memory only # Parameters: # path Name of the file/device # offset C'mon already! Ain't this obvious? Anyway, this is in K. # length # pm "P" for primary, "M" for mirror # Local variables: # loc_inum Local I-node numbers # loc_tinum Local true inode number # loc_key Norse god of mischief (OK, local key value) # Globals used: # list_size Number of entries in the list - may be 0 #===================================================================== function append_one(path, offset, llength, pm, loc_inum, loc_tinum, loc_key) { loc_inum = get_inode(path); # Get the simple i-node number loc_tinum = get_tinode(path); # and true inode number loc_key = loc_tinum "-" offset "-" llength list_inum[loc_key] = loc_inum; list_tinum[loc_key] = loc_tinum; list_offset[loc_key] = offset; list_length[loc_key] = llength; list_mirror[loc_key] = pm; list_path[loc_key] = path list_icfg[loc_key] = icfg; # Remember the ON/TBCONFIG param list_server[loc_key] = iserv; # the $INFORMIXSERVER name list_idir[loc_key] = idir; # and $INFOMRIXDIR list_size++; # Bump to next possible entry return (list_size); # Return new size of list } # #===================================================================== # output_all() - copy entire array to the list file # Local variables: # l_path, l_cgf, l_server, l_idir # All these are integers - length of the longest instance of # each of these fields. Used for formatting. # bu_file - Name of backup file for the chunks list # cmdl - Spare variable for shell commands # sort_cmd - A sort command line to output the data in the desired # order. # ix - Loop counter & space variable #===================================================================== function output_all(l_path, l_icgf, l_server, l_idir, bu_file, cmdl, sort_cmd, ix) { # OK, here's the plan - I will scan the list to get the widest # value for each entry in the list. I will use this value to format # the list on the way out. # Note also - I will sort the list on the way out as well - by # true-inode, offset, length # l_path = l_icfg = l_server = l_idir = 0; sort_cmd = "sort +1 -4 -n > " list_file; cmdl = "test -f '" list_file "'"; # Before I backup list file, is # there a list file to back up? if ((ix = system(cmdl)) == 0) { # If there is a list file to back up # Get date, time as yyyy mm dd hh mm ss "date +'%Y %m %d %H %M %S'" | getline; bu_file = list_file ".bu." \ $1 "-" $2 "-" $3 "-" $4 ":" $5 ":" $6; # We have the name of the backup chunks-list file. cmdl = "mv " list_file " " bu_file; if ((ix = system(cmdl)) != 0) { # If backup command has failed printf ("Unable to create backup file <%s>; error %d\n", bu_file, ix); printf("Output operation aborted\n") exit (lc); # Give up now } } # OK, preliminaries are taken care of. # # Function output_all() - Continued # # Scan for longest strings for nice formatting. for (ix in list_tinum) { if (length(list_path[ix]) > l_path) l_path = length(list_path[ix]); if (length(list_icfg[ix]) > l_icfg) l_icfg = length(list_icfg[ix]); if (length(list_server[ix]) > l_server) l_server = length(list_server[ix]); if (length(list_idir[ix]) > l_idir) l_idir = length(list_idir[ix]); } for (ix in list_tinum) { printf("%10d %10s %10d %10d %c %*s %*s %*s %*s\n", list_inum[ix], list_tinum[ix], list_offset[ix], list_length[ix], list_mirror[ix], l_path, list_path[ix], l_icfg, list_icfg[ix], l_server, list_server[ix], l_idir, list_idir[ix]) | sort_cmd ; } close(sort_cmd); } # #===================================================================== # scan_overlap() - See if a proposed chunk overlaps the existing items # in the list. # Parameters: # path: Path name of the file/device # offset: Offset, in K, into the file/device # length: Length (K) of the proposed chunk # Local Variables: # lc - Loop counter # tn - true inode number # whoops - Number of overlaps detected # Globals: # The arrays of list items. # cfgpath - The full path name of the operating tb/onconfig file # idir - The operating $INFORMIXDIR # (Not iserv - this can change between runs - Aliasing, y'know) # Returns: The number of overlaps #===================================================================== function scan_overlap(path, offset, llength, lc, tn, whoops) { whoops = 0; tn = get_tinode(path); # Get true inode number of path for (lc in list_tinum) { # Before we complain of an overlap, make sure the chunk being # proposed is not already in the list in the operating instance. # If I don't check for this, then I will be complaining about # every existing chunk in the operating environment whenever I # run the -AA function. In order to ascertain (loooove those # fancy words! ;) that I'm looking at a chunk in my current # operating environment, it has to pass some stringent tests: # if ( (idir == list_idir[lc]) \ && (icfg == list_icfg[lc]) \ && (path == list_path[lc]) \ && (offset == list_offset[lc]) \ && (llength == list_length[lc]) ) continue; # Comparing a chunk against itself; # don't test for overlap. if (overlap(list_tinum[lc], list_offset[lc], list_length[lc], tn, offset, llength)) { ovl_msg_e(list_path[lc], list_offset[lc], list_length[lc], list_tinum[lc], path, offset, llength, tn, cfgpath, list_server[lc]) whoops++; } } return whoops; } # # scan_overlap() - Additional notes # # Note: Regarding the test for same-chunk/instance that I performed # before checking for overlap: I checked for identical: # - $INFORMIXDIR # - $TB/ONCONFIG # - pathname, offset and length. # # This leave one minor loophole in the lschunks script: # Someone trying to add a chunk in the current instance that exactly # overlays an existing chunk. I did not want to make this function # dependent on whether the command was AA or AC. I am leaving this # hole in the code because when we execute the onspaces command to add # this exact-overlay chunk, the command will fail with the message that # "chunk exists". We won't add it to the list anyway becuase onspaces # will return a non-0 value to us. # #===================================================================== # overlap - Function to determine if set of chunk parameters overlaps # the specified chunk. # Issues error message for every incident of overlap # Parameters: # ck_inum: I-number of the existing chunk device # ck_offs: Offset of existing chunk # ck_len: Length of existing chunk # p_inum: I-number of the proposed chunk device # p_offs: Offset of proposed chunk # p_len: Length of proposed chunk # Local variables: # ck_end: Offset of end of tested chunk # p_end: End of proposed chunk # Global Variables: # inod_mmnums[] - Array of major & minor numbers, indexed on true # inode numbers. # # Returns: # 1 if they overlap, 0 if they don't #===================================================================== function overlap(ck_inum, ck_offs, ck_len, p_inum, p_offs, p_len, ck_end, p_end, rval) { ck_end = ck_offs + ck_len - page_size; # End-offset; existing chunk p_end = p_offs + p_len - page_size; # End offset; proposed chunk # I now have start/end for chunk. I can test if the proposed # chunk coincides with the chunk currently being examined # Test: If either the beginning or end of the proposed chunk # lies between the extrema of another chunk with the same # inode number, we have an overlap. If the inode numbers are not # the same, perhaps the major & minor numbers of the inodes are the # same, so we check for that as well. rval = 0; # Start optimistic! Assume no overlap # if ( ( (p_inum == ck_inum) \ # || (inod_mmnums[p_inum] == inod_mmnums[ck_inum]) ) \ if ( p_inum == ck_inum \ && ( (p_offs >= ck_offs && p_offs <= ck_end) \ ||(p_end >= ck_offs && p_end <= ck_end) ) \ ) { rval = 1; # Yup - there is an overlap } return rval; } # #===================================================================== # getenv(): Function to mimic the same-named C function # Parameter: Name of an environment variable # Local: lcmd - String to form a command # rval - value to be returned to caller #===================================================================== function getenv(envname, lcmd, rval){ lcmd = "echo $" envname ; # Let shell echo the value lcmd | getline rval; # capture in local variable close(lcmd); # Close the pipe return rval; } # # #===================================================================== # ovl_msg_e() - Function to output an error message regarding an # overlap. # Parameters: The paths, offsets, and lengths of the proposed and # existing chunks. #===================================================================== function ovl_msg_e(e_path, e_off, e_len, e_ti, p_path, p_off, p_len, p_ti, cfg, srv){ errmg1 = sprintf("Overlap: Current chunk: <%s-%d-%d>[%s]", p_path, p_off, p_len, p_ti); errmg2 = sprintf("\t Versus registered chunk: <%s-%d-%d>[%s]", e_path, e_off, e_len, e_ti); errmg3 = sprintf("In instance TBCONFIG: %s, INFORMIXSERVER: %s", cfg, srv); print errmg1 ; print errmg2 ; print errmg3 ; } # #===================================================================== # # get_inode() - Get the inode number of a named file # Parameters: # - fname: Name of the file # Local variables: # - lcmd: Command string # - rval; Return value #===================================================================== function get_inode(fname, itype, lcmd, rval){ lcmd = "ls -i " fname " | awk '{print $1}'" lcmd | getline rval; # Capture inumber close(lcmd); return rval; } # #===================================================================== # get_tinode() - Get the true inode number and update/augment the # inod_mmnums[] and inod_tpath[] arrays with the # major/minor numbers and the true path name (after # Parameters: # path: This should be obvious # Returns: The true inode number of the file, after chasing all # possible symbolic links. # Side effects: # Arrays inod_mmnums[] and inod_tpath[] have been augmented. # Note that a repeat call with the same path name results in a # harmless (if a bit inefficient) rewrite of the same entries in # the arrays. # # Local variables: # rval Return Value # cur_devtyp Device type character of the current file # savedFS A saved copy of the special variable FS, because this # function requires its own exotic field separator. # lscmd A string to contain the ls command # Globals used: # inod_mmnums Array: Major,minor numbers of the device # inod_tpath Array: True path name correcponding to a device/file #===================================================================== function get_tinode(path, rval, cur_devtyp, savedFS, lscmd) { rval = ""; # Start with no return value savedFS=FS; # Save the field separator FS="[, ]*"; # Be delimited by either blank or comma # # The * is to combine spaces as 1 delimiter. It would also combine # multiple commas as a single delimiter but for the ls command, I # don't have to worry about multiple commas. I'm also not really # concerened about commas embedded in the file name. However, such # wierd device names do call into question the usefulness of the # inod_tpath array. # lscmd = "ls -ilL " path; # Command line to get info, chase links lscmd = lscmd "|sed -e s/\"^[ ]*\"//g"; lscmd | getline; # Capture necessary file info rval = $1; # True inode number to return to caller cur_devtyp = substr($2, 1, 1); # Get first letter of perm mask # # Function get_tinode() - Continued if (cur_devtyp ~ "[bc]") # Is it block or char device? { inod_mmnums[rval] = sprintf("%d-%d", $6, $7); # Special device: string is major-num,minor-num inod_tpath[rval] = $11; # Get true path name rval = inod_mmnums[rval]; } else { inod_mmnums[rval] = rval; # non-device - just repeat inumber inod_tpath[rval] = $10; # Get true path name } close(lscmd); FS = savedFS; # Restore field separator return rval; } # #===================================================================== # usage() - function to output help text # Parameters: None # Local Variables: # msg: Array of string variables to hold message text # lc, lcc: Loop counters # cmfile: Assumed name of this awk script #===================================================================== function usage(msg, lc, cmfile) { lc = 0; # Start low cmfile = "[n]awk -f lschunks.awk -- " msg[++lc] = "Usage"; msg[++lc] = cmfile " -AA (Adds current chunks to list)"; msg[++lc] = cmfile " -AC dbspace path offset length " \ " [mpath moffset]"; msg[++lc] = "\t (Adds 1 chunk [and mirror] to the list)"; msg[++lc] = cmfile " -AD dbspace path offset length" \ " [mpath moffset]"; msg[++lc] = "\t (Adds a dbspace with appropriate parameters)"; msg[++lc] = cmfile " -AT dbspace path offset length" \ " [mpath moffset]"; msg[++lc] = "\t (Adds temp dbspace with appropriate parameters)"; msg[++lc] = cmfile " -AB blobspace page-unit" \ " path offset length [mpath moffset]"; msg[++lc] = "\t (Adds a BLOBspace with appropriate parameters)"; msg[++lc] = cmfile " -CC path offset length"; msg[++lc] = "\t (Checks proposed chunk parameters for conflict" \ " but does not add to list)"; msg[++lc] = cmfile " -CI path offset length {P|M}"; msg[++lc] = "\t (Checks proposed chunk parameters for conflict," \ " and adds it to list."; msg[++lc] = "\t This command does not run tbspaces/onspaces)"; msg[++lc] = cmfile " -CV"; msg[++lc] = "\t (Validate current chunk list for changes in inode & major/minor"; msg[++lc] = "\t device numbers due to reboot and update."; msg[++lc] = "\t This command does not run tbspaces/onspaces)"; for (lcc =1; lcc < lc; lcc++) printf ("%s\n", msg[lcc]); } MKSHAR_EOF echo x - printchunks cat >printchunks <<'MKSHAR_EOF' #! /usr/bin/ksh sort +6b -7 +5b -6 +3n -4 ~informix/.chunkList | printchunks.p ##asksort -w -f7///C,6,3///N -i ~informix/.chunkList -o- -S 2>/dev/null | printchunks.p MKSHAR_EOF echo x - printchunks.p cat >printchunks.p <<'MKSHAR_EOF' #! /usr/bin/perl eval "exec /usr/opt/gnu/bin/perl -S $0 $*" if $running_under_some_shell; # this emulates #! processing on NIH machines. # (remove #! line above if indigestible) eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift; # process any FOO=bar switches $cnfg = ' '; $lines = 0; while (<>) { ($Fld1,$Fld2,$Fld3,$Fld4,$Fld5,$Fld6,$Fld7,$Fld8,$Fld9) = split(' ', $_, 999); if ($cnfg ne $Fld7) { #??? $cnfg = $Fld7; $srvr = $Fld8; $sdir = $Fld9; printf "\n\nServer: %s Config: %s INFORMIXDIR: %s\n\n", $srvr, $cnfg, $sdir; printf " INODE(TRUEIN) TY OFFSET SIZE PATHNAME\n"; printf " ------------- -- -------- -------- -----------------------------\n"; } $inod = $Fld1; $tnod = $Fld2; $offs = $Fld3; $size = $Fld4; $type = $Fld5; $path = $Fld6; $real = readlink $path; if ($inod eq $tnod) { printf " %5.5d(%6.6s) %-2.2s %8d %8d %s\n", $inod, $tnod, $type, $offs, $size, $path; } else { printf " %5.5d(%6.6s) %-2.2s %8d %8d %s(%s)\n", $inod, $tnod, $type, $offs, $size, $path, $real; } $lines += 1; } if ($lines == 0) { printf "Chunk file not found or empty!\n"; } MKSHAR_EOF echo x - dbsavail.ec cat >dbsavail.ec <<'MKSHAR_EOF' #include #include #include EXEC SQL INCLUDE sqlca; int main( int argc, char **argv ) { EXEC SQL BEGIN DECLARE SECTION; string dbsname[32]; dec_t total_size, free_size; char flag; long pagesize; EXEC SQL END DECLARE SECTION; char tot_str[25], free_str[25], tmp[25]; dec_t sum_size, sum_free; int ret; EXEC SQL DATABASE sysmaster; if (sqlca.sqlcode) { fprintf( stderr, "Error opening database: sqlcode: %d, ISAM: %d.\n", sqlca.sqlcode, sqlca.sqlerrd[1] ); return sqlca.sqlcode; } EXEC SQL DECLARE fred CURSOR FOR execute procedure dbsavail(); if (sqlca.sqlcode) { fprintf( stderr, "Error declaring cursor: sqlcode: %d, ISAM: %d.\n", sqlca.sqlcode, sqlca.sqlerrd[1] ); return sqlca.sqlcode; } again: EXEC SQL OPEN fred; if (sqlca.sqlcode == -674 && sqlca.sqlerrd[1] == -111) { /* Stored procedure not there, create it and try again. */ EXEC SQL EXECUTE IMMEDIATE " \ create procedure dbsavail() \n\ returning char(18), char(1), int, decimal(16,0), \n\ decimal(16,0) ; \n\ \n\ define dbname char(18); \n\ define aflag char(1); \n\ define total_size, free_size decimal(16,0); \n\ define temp, blob smallint; \n\ define size int; \n\ \n\ foreach \n\ select d.name, d.is_temp, d.is_blobspace, t.prtpage/1024, \n\ sum(chksize) size, sum(nfree) free \n\ into dbname, temp, blob, size, total_size, free_size \n\ from sysmaster:sysdbspaces d, sysmaster:syschunks c, \n\ sysmaster:sysdbstab t \n\ where d.dbsnum = c.dbsnum \n\ and c.dbsnum = t.dbsnum \n\ group by 1,2,3,4 \n\ order by free \n\ \n\ if (temp == 1) then \n\ let aflag = 'T'; \n\ elif (blob == 1) then \n\ let aflag = 'B'; \n\ else \n\ let aflag = 'N'; \n\ end if \n\ \n\ return dbname, aflag, size, total_size, free_size with resume; \n\ end foreach; \n\ end procedure; \n\ "; if (sqlca.sqlcode) { fprintf( stderr, "Error creating stored procedure: sqlcode: %d, ISAM: %d @%d.\n", sqlca.sqlcode, sqlca.sqlerrd[1], sqlca.sqlerrd[4] ); return sqlca.sqlcode; } goto again; } else if (sqlca.sqlcode) { fprintf( stderr, "Error opening cursor: sqlcode: %d, ISAM: %d.\n", sqlca.sqlcode, sqlca.sqlerrd[1] ); return sqlca.sqlcode; } printf( "\n%-18.18s\t%-12s\t%-12s\n", "Dbspace", "Total Pages", "Free Pages" ); printf( "%-18.18s\t%-12s\t%-12s\n", "------------------", "------------", "------------" ); deccvint( 0, &sum_size ); deccvint( 0, &sum_free ); do { EXEC SQL FETCH fred INTO :dbsname, :flag, :pagesize, :total_size, :free_size; if (sqlca.sqlcode == 0) { dectoasc( &total_size, tmp, 12, 0 ); ldchar( tmp, 12, tot_str ); if ((ret = decadd( &total_size, &sum_size, &sum_size ))) { fprintf( stderr, "Error adding size: %d.\n", ret ); } dectoasc( &free_size, tmp, 12, 0 ); ldchar( tmp, 12, free_str ); if ((ret = decadd( &free_size, &sum_free, &sum_free ))) { fprintf( stderr, "Error adding free: %d.\n", ret ); } if (flag == 'T') { printf( "%-18s\t%12s\t%12s TempSpace\n", dbsname, tot_str, free_str ); } else if (flag == 'B') { printf( "%-18s\t%12s\t%12s BlobSpace(%dK Pages)\n", dbsname, tot_str, free_str, pagesize ); } else { printf( "%-18s\t%12s\t%12s\n", dbsname, tot_str, free_str ); } dectoasc( &sum_size, tmp, 12, 0 ); ldchar( tmp, 12, tot_str ); dectoasc( &sum_free, tmp, 12, 0 ); ldchar( tmp, 12, free_str ); /* printf( "\t\t%-12s\t%-12s\n", tot_str, free_str ); */ } } while (sqlca.sqlcode == 0); if (sqlca.sqlcode != 100) { fprintf( stderr, "Error fetching cursor: sqlcode: %d, ISAM: %d.\n", sqlca.sqlcode, sqlca.sqlerrd[1] ); return sqlca.sqlcode; } printf( "%-18.18s\t%-12s\t%-12s\n", "------------------", "------------", "------------" ); dectoasc( &sum_size, tmp, 12, 0 ); ldchar( tmp, 12, tot_str ); dectoasc( &sum_free, tmp, 12, 0 ); ldchar( tmp, 12, free_str ); printf( "%-18s\t%12s\t%12s\n\n", "Totals:", tot_str, free_str ); return 0; } MKSHAR_EOF echo x - dbsavail.sh cat >dbsavail.sh <<'MKSHAR_EOF' #! /usr/bin/ksh if [[ -x $INFORMIXDIR/bin/onstat ]] then STAT=onstat Name=${ONCONFIG##onconfig.} else STAT=tbstat Name=${TBCONFIG##tbconfig.} fi if [[ -z $Name ]]; then Name=out; fi $STAT -d | tee ~informix/${STAT}.d.$Name | cut -c10-15,14-16,26-34,35-43,64-,45-51 |/bb/bin/dbsavail.p2 MKSHAR_EOF echo x - dbsavail.sh.64bit cat >dbsavail.sh.64bit <<'MKSHAR_EOF' #! /usr/bin/ksh # Version of dbsavail.sh for 64 bit Implementations of IDS (versions ending FC) # if [[ -x $INFORMIXDIR/bin/onstat ]] then STAT=onstat Name=${ONCONFIG##onconfig.} else STAT=tbstat Name=${TBCONFIG##tbconfig.} fi if [[ -z $Name ]]; then Name=out; fi $STAT -d | tee ~informix/${STAT}.d.$Name | cut -c18-23,22-24,34-42,43-51,72-,53-59 |/bb/bin/dbsavail.p2 MKSHAR_EOF echo x - dbsavail.p2 cat >dbsavail.p2 <<'MKSHAR_EOF' #!/usr/bin/perl eval "exec /usr/bin/perl -S $0 $*" if $running_under_some_shell; # this emulates #! processing on NIH machines. # (remove #! line above if indigestible) eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift; # process any FOO=bar switches $, = ' '; # set output field separator $\ = "\n"; # set output record separator $OK = 0; while (<>) { ($Fld1,$Fld2,$Fld4,$Fld3,$dbs,$Fld5) = split(' ', $_, 999); if ($OK == 1) { if ( $Fld1 == 0) { $OK = 2; next; } $D = substr( $Fld3, 0, 1 ); if ( $D eq "\~") { $fr{$Fld2} += substr( $Fld3, 1, length $Fld3 ); # $ty{$Fld2} = "B"; $ps{$Fld2} = (2 * $Fld4) / $dbs; } else { $fr{$Fld2} += $Fld3; # $ty{$Fld2} = "T"; $ps{$Fld2} = 2; } $tt{$Fld2} += $Fld4; if (int($Fld1) > 0 && int($Fld2) > $max) { $max = $Fld2; } } else { if (OK == 0) { if ($dbs eq "B" || $dbs eq "T") { $fn{$Fld1} = $Fld5; if ($dbs eq "B") { $ty{$Fld1} = "B"; } else { $ty{$Fld1} = "T"; } } else { $fn{$Fld1} = $dbs; $ty{Fld1} = "P"; } } } if (/free/) { $OK = 1; } } select(STDOUT); $| = 1; $total=0; $totfree=0; printf "%-18.18s\t%s\t%s\n", ' dbspace', 'total pgs ', 'pages free'; printf "%-18.18s\t%s\t%s\n", '-----------------', '----------', '----------'; # # This is my sort utility substitute appropriate sort pipe # open(SORT, "|asksort -w -f3//a/N,1 -i- -o- 2>/dev/null"); open(SORT, "|sort +2 -3n +0 -1n 2>/dev/null"); for ($i = 1; $i <= $max; $i++) { if ( $tt{$i} > 0 ) { $B = $ty{$i}; $total += $tt{$i}; $totfree += $fr{$i}; if ( $B eq "B" ) { printf SORT "%-18.18s\t%10.1d\t%10.1d %dK Blob Pages\n", $fn{$i}, $tt{$i}, $fr{$i}, $ps{$i}; } elsif ( $B eq "T" ) { printf SORT "%-18.18s\t%10.1d\t%10.1d TempSpace\n", $fn{$i}, $tt{$i}, $fr{$i}, $ps{$i}; } else { printf SORT "%-18.18s\t%10.1d\t%10.1d\n", $fn{$i}, $tt{$i}, $fr{$i}; } } } close(SORT); printf "%-18.18s\t%s\t%s\n", '-----------------', '----------', '----------'; printf "%-18.18s\t%10.1d\t%10.1d\n", "Totals:", $total, $totfree; MKSHAR_EOF echo x - defragtable.ec cat >defragtable.ec <<'MKSHAR_EOF' /* defragtable.ec - recreate table with one extent large enough to hold all data */ /* RCS Header: ----------- $Header$ RCS Log: -------- $Log$ */ #include #include #include #include #include #include "blob.h" #include "sqlca.h" #include "locator.h" static char RCSHeader[] = "$Header$"; static char RCSWhat[] = "$What$ $Date$"; extern struct sqlca_s sqlca; int main( int argc, char **argv ) { char command[2049], ans[1025], c, dbspace[1025]; pid_t pid; long extent, nexts, ii = 0; int compress = 0, dropit = 0, PCT = 10; EXEC SQL BEGIN DECLARE SECTION; long totpages, nextents, usedpages; char sqlcmd[4096]; char idatabase[19], itable[19], ntable[19], ttable[19], dbs[19], tbn[19]; EXEC SQL END DECLARE SECTION; dbspace[0] = (char)0; while( (c = getopt( argc, argv, "cdh?m:n:" )) != EOF) { switch (c) { case 'h': case '?': usage( argv[0] ); exit(0); break; case 'c': compress = 1; break; case 'd': dropit = 1; break; case 'm': strcpy( dbspace, optarg ); break; case 'n': PCT = atoi( optarg ); if (PCT <= 0) { fprintf( stderr, "Extent growth % (%s) invalid (1->100).\n", optarg ); usage( argv[0] ); exit(12); } break; default: usage( argv[0] ); exit(13); } } if (optind >= argc) { usage( argv[0] ); exit(1); } strcpy( idatabase, argv[optind++] ); strcpy( itable, argv[optind++] ); sprintf( ntable, "%1.16s_u", itable ); sprintf( command, "dbschema -d %s -t %s -ss /tmp/uschema.%d 2>/dev/null 1>&2\n", idatabase, itable, pid = getpid() ); printf( "Building schema for new table.\n" ); if (system( command )) { fprintf( stderr, "Cannot continue: dbschema failed!\n" ); exit(9); } sprintf( command, "sed \"s/%s/%s/g\ns/revoke/--revoke/\" /tmp/uschema.%d " ">/tmp/uschemam.%d 2>/dev/null\n", itable, ntable, pid, pid, pid ); if (system( command )) { fprintf( stderr, "Cannot continue: sed failed!\n" ); exit(9); } EXEC SQL DATABASE sysmaster; EXEC SQL SELECT dbsname, tabname, count(*), sum(size) INTO :dbs, :tbn, :nextents, :totpages FROM sysextents WHERE tabname = :itable AND dbsname = :idatabase GROUP BY dbsname, tabname; fprintf( stderr, "Number of extents: %d (total size = %d pages).\n", nextents, totpages ); extent = totpages * 2; if (extent < 16) extent = 16; nexts = (extent * PCT) / 100; if (nexts < 8) nexts = 8; EXEC SQL DATABASE :idatabase; if (SQLCODE) { fprintf( stderr, "Cannot open database <%s>, sqlcode=%d, isam=%d.\n", idatabase, sqlca.sqlcode, sqlca.sqlerrd[1] ); exit( 2 ); } if (compress) { printf( "Update statistics (Y/N)? " ); fgets( ans, 1025, stdin ); if (ans[0] == 'Y' || ans[0] == 'y') { sprintf( sqlcmd, "UPDATE STATISTICS low for table %s", itable ); EXEC SQL EXECUTE IMMEDIATE :sqlcmd; if (sqlca.sqlcode != 0) { fprintf( stderr, "Cannot update statistics.\n" ); exit(10); } } sprintf( sqlcmd, "SELECT npused FROM systables WHERE tabname = \"%s\"", itable ); EXEC SQL PREPARE stmt FROM :sqlcmd; if (sqlca.sqlcode != 0) { fprintf( stderr, "Cannot PREPARE statement for #pages.\n" ); exit(10); } EXEC SQL EXECUTE stmt INTO :usedpages; if (sqlca.sqlcode != 0) { fprintf( stderr, "Cannot fetch # pages used.\n" ); exit(10); } printf( "Compressing table from %d pages to %d pages.\n", totpages, usedpages ); extent = usedpages * 2; if (extent < 16) extent = 16; nexts = (extent * PCT) / 100; if (nexts < 8) nexts = 8; } if (dbspace[0] != (char)0) { sprintf( command, "awk 'BEGIN{ constr=0; }" "/extent size/{" " sub( \"extent size [0-9]*\", \"extent size %d\" );" " sub( \"next size [0-9]*\", \"next size %d\" );" " if (index( $0, \" in \") == 0) {" " sub( \" [)] \", \" ) in %s \" );" " } else {" " sub( \" in [a-zA-Z0-9_]* \", \" in %s \" );" " }" "}" "/create index /{" " sub( \" on \", \"u on \" );" "}" "/primary key /{" " sub( \"$\", \"u\" );" "}" "/add constraint/{ constr = 1; }" "/[)];/{" " if (constr == 1) {" " sub( \"[)];\", \"u);\" );" " constr == 0;" " }" "}" "{print $0}' /tmp/uschemam.%d " ">/tmp/uschema.%d 2>/dev/null\n", extent, nexts, dbspace, dbspace, pid, pid ); } else { sprintf( command, "awk 'BEGIN{ constr=0; }" "/extent size/{" " sub( \"extent size [0-9]*\", \"extent size %d\" );" " sub( \"next size [0-9]*\", \"next size %d\" );" "}" "/create index /{" " sub( \" on \", \"u on \" );" "}" "/primary key /{" " sub( \"$\", \"u\" );" "}" "/add constraint/{ constr = 1; }" "/[)];/{" " if (constr == 1) {" " sub( \"[)];\", \"u);\" );" " constr == 0;" " }" "}" "{print $0}' /tmp/uschemam.%d " ">/tmp/uschema.%d 2>/dev/null\n", extent, nexts, pid, pid ); } if (system( command )) { fprintf( stderr, "Cannot continue: awk failed!\n" ); exit(9); } sprintf( command, "awk '/create index/{print $0 >\"/tmp/uschemai.%d\"; " "print \"--\", $0; next; }{print $0;}' " "/tmp/uschema.%d >/tmp/uschemat.%d", pid, pid, pid ); if (system( command )) { fprintf( stderr, "Cannot continue: awk2 failed!\n" ); exit(9); } sprintf( command, "dbaccess %s - /dev/null 2>&1\n", idatabase, pid ); printf( "Creating new table <%s>\n", ntable ); if (system( command )) { fprintf( stderr, "Cannot continue: dbaccess failed!\n" ); exit(14); } printf( "Attempting to acquire a shared lock on original table.\n" ); sprintf( sqlcmd, "LOCK TABLE %s IN SHARE MODE;", itable ); EXEC SQL EXECUTE IMMEDIATE :sqlcmd; if (SQLCODE) { fprintf( stderr, "Cannot acquire a table lock, sqlcode=%d, isam=%d.\n", sqlca.sqlcode, sqlca.sqlerrd[1] ); exit( 13 ); } printf( "Copying data to new table.\n" ); sprintf( sqlcmd, "INSERT INTO %s SELECT * FROM %s", ntable, itable ); EXEC SQL EXECUTE IMMEDIATE :sqlcmd; if (SQLCODE) { fprintf( stderr, "Cannot copy data from table <%s>, sqlcode=%d, isam=%d.\n", itable, sqlca.sqlcode, sqlca.sqlerrd[1] ); exit( 3 ); } printf( "Data copied. Creating indexes.\n" ); sprintf( command, "dbaccess %s - /dev/null 2>&1\n", idatabase, pid ); if (system( command )) { fprintf( stderr, "Cannot continue: dbaccess failed!\n" ); exit(14); } sprintf( ttable, "%1.16s_t", itable ); printf( "Renaming table %s to ==>> %s\n", itable, ttable ); sprintf( sqlcmd, "RENAME TABLE %s TO %s", itable, ttable ); EXEC SQL EXECUTE IMMEDIATE :sqlcmd; if (SQLCODE) { fprintf( stderr, "Cannot rename table <%s>, sqlcode=%d, isam=%d.\n", itable, sqlca.sqlcode, sqlca.sqlerrd[1] ); exit( 3 ); } printf( "Renaming table %s to ==>> %s\n", ntable, itable ); sprintf( sqlcmd, "RENAME TABLE %s TO %s", ntable, itable ); EXEC SQL EXECUTE IMMEDIATE :sqlcmd; if (SQLCODE) { fprintf( stderr, "Cannot rename table <%s>, sqlcode=%d, isam=%d, putting old " "table back.\n", ntable, sqlca.sqlcode, sqlca.sqlerrd[1] ); sprintf( sqlcmd, "RENAME TABLE %s TO %s", ttable, itable ); EXEC SQL EXECUTE IMMEDIATE :sqlcmd; if (SQLCODE) { fprintf( stderr, "Could not restore original table <%s> from temp " "name <%s>.\n", itable, ttable ); } else fprintf( stderr, "Original table <%s> restored.\n", itable ); exit( 3 ); } if (!dropit) { fprintf( stdout, "Drop original table? " ); fgets( ans, 1025, stdin ); if (ans[0] == 'Y' || ans[0] == 'y') dropit = 1; else dropit = 0; } if (dropit) { sprintf( sqlcmd, "DROP TABLE %s", ttable ); EXEC SQL EXECUTE IMMEDIATE :sqlcmd; if (SQLCODE) { fprintf( stderr, "%s\n%*s\n", sqlcmd, sqlca.sqlerrd[4], "^" ); fprintf( stderr, "Cannot drop original table, sqlcode=%d, isam=%d, pos=%d.\n", sqlca.sqlcode, sqlca.sqlerrd[2], sqlca.sqlerrd[4] ); exit(11); } } EXEC SQL DISCONNECT ALL; sprintf( command, "/tmp/uschema.%d", pid ); unlink( command ); sprintf( command, "/tmp/uschemam.%d", pid ); unlink( command ); return 0; } void usage( char *prog ) { fprintf( stderr, "usage: %s [-c] [-d] [-m dbspace] [-n grow%%] \n", prog ); fprintf( stderr, "\t-c - compress table to eliminate unused data and index " "pages.\n" ); fprintf( stderr, "\t-d - drop original table without prompting (default: " "prompt).\n" ); fprintf( stderr, "\t-m - place new copy of table in specified dbspace\n" "\t (default: place new copy of table in same dbspace).\n" ); fprintf( stderr, "\t-n - set size of next extent (table growth percent) to grow%%.\n" "\t (default: grow table by 10%% of current size when needed)\n" ); } MKSHAR_EOF echo x - dbstruct.ec cat >dbstruct.ec <<'MKSHAR_EOF' /* Program Id: dbstruct Program Description: this program will generate a "C" struct for a specified database or a set of tables. Author: Art S. Kagel from schema.ec originally taken from Rick Friedman's dbschema_cc for Sybase Date: May of 1992 Version: 1.0 Comments: Revisions: Usage: dbstruct [-F] [-h hostname] -d databasename [-t tablename] [filename] RCS Header ---------- $Header: /home/kagel/utils/RCS/dbstruct.ec,v 1.3 1996/08/06 18:39:52 kagel Exp $ RCS Log ------- $Log: dbstruct.ec,v $ # Revision 1.3 1996/08/06 18:39:52 kagel # Added -B flag to represent DATETIME and INTERVAL as Bloomberg Datetime. # # Revision 1.3 1996/08/06 18:39:52 kagel # Added -B flag to represent DATETIME and INTERVAL as Bloomberg Datetime. # # Revision 1.2 1995/05/04 15:07:53 kagel # Added ESQL and FORTRAN support I think?!?!?! # # Revision 1.1 1994/09/02 15:16:09 kagel # Initial revision # # Revision 1.6 1993/07/29 19:51:52 kagel # Added RCS headers. # # Revision 1.5 1993/05/11 12:31:50 kagel # *** empty log message *** # # Revision 1.5 1993/05/11 12:31:50 kagel # *** empty log message *** # # Revision 1.4 1993/05/10 14:10:59 kagel # *** empty log message *** # # Revision 1.3 1993/05/05 13:45:51 kagel # *** empty log message *** # # Revision 1.2 1993/05/04 20:09:37 kagel # Fixed definition of datetime range type FRACTION was "FRACT". # # Revision 1.1 1993/05/04 17:39:19 kagel # Initial revision # Revision 1.2 93/01/17 09:52:50 ask Missed a change to the extent size warning. Revision 1.1 93/01/17 09:47:26 ask Initial revision */ #include #include #if defined(__sysv__) || defined(i386) #include #else #include #endif EXEC SQL INCLUDE sqlca; EXEC SQL INCLUDE sqltypes; EXEC SQL INCLUDE datetime; static char rcs_header[] = "$Header: /home/kagel/utils/RCS/dbstruct.ec,v 1.3 1996/08/06 18:39:52 kagel Exp $"; struct sqlca_s sqlca; EXEC SQL BEGIN DECLARE SECTION; struct { char tabname[19]; long tabid; long partnum, fextsize, nextsize; char locklevel[2]; long npused; char tabtype[2]; int nrows; } systables, prev; struct { long tabid; char colname[19]; short colno; short coltype; short collength; int colmin; int colmax; } syscolumns; struct { char idxname[19]; long tabid; char idxtype[2], clustered[2]; short part[16]; } sysindexes; struct { long constrid; char constrname[19]; long tabid; char constrtype[2], idxname[19]; } sysconstraints; int isconstraint; struct { short dbsid; char dbsname[19]; } sys_dbspaces[32]; struct { char servername[19], dbname[19], tabname[19]; } syssyntable; struct { char viewtext[65]; } sysviews; char databasename[19], host[19], statement[2048], connection[41]; char database[38], tablename[19], dbspace[57]; EXEC SQL END DECLARE SECTION; extern int optind; extern char *optarg; char *a_space, *strchr(); char dtparts[7][10] = { "YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND", "FRACTION" }; static char columns[1024][19]; #define TBSFACT 16777216L #define FALSE (0) #define TRUE (!FALSE) void usage( char *program ); void ucase( char *target, char *source ); main(int argc, char *argv[]) { static pass_no = 1; int i, j, c, errflag=0, dflag=0, tflag=0, hflag=0, allows_nulls; long colwidth, pt1, pt2, fr1, fr2; FILE *outfile = stdout; char filename[1025], part1[10], part2[10], upname[19]; static char tempstr[1024]; int FORTRAN = 0, ESQL = 0, VCHAR = 1, BBDATE = 0; prev.tabname[0] = (char)0; strcpy( tablename, "*" ); host[0] = (char)0; while ((c = getopt(argc, argv, "d:t:h:FefB")) != EOF){ switch(c) { case 'F': FORTRAN = 1; break; case 'B': BBDATE = 1; break; case 'e': ESQL = 1; break; case 'f': VCHAR = 0; break; case 'd': if (dflag) errflag++; else{ dflag++; strncpy(databasename, optarg, 18); databasename[18] = (char)0; } break; case 't': if (tflag) errflag++; else{ tflag++; strncpy(tablename, optarg, 18); tablename[18] = (char)0; } break; case 'h': if (hflag) errflag++; else{ hflag++; strncpy(host, optarg, 18); host[18] = (char)0; } break; case '?': default: errflag++; break; } } if (optind < argc) { strcpy( filename, argv[optind] ); outfile = fopen( filename, "w" ); if (!outfile) { fprintf( stderr, "Cannot open output: %s.\n", filename ); exit(3); } } if (errflag || dflag == 0){ usage( argv[0] ); exit (1); } if(strcmp(databasename,"")==0 || ( tflag == 1 && strcmp(tablename,"")==0)){ usage( argv[0] ); exit (1); } strcpy( connection, databasename ); if (hflag) { strcat( connection, "@" ); strcat( connection, host ); EXEC SQL CONNECT TO :connection; } else { EXEC SQL DATABASE :connection; } if ( sqlca.sqlcode ) { fprintf( stderr, "Cannot access host (%s) & database (%s); error=%d\n", host, databasename, sqlca.sqlcode ); rexit(1); } /* Select for tables and columns. */ strcpy( statement, "SELECT a.tabname, a.tabid, a.partnum, " ); strcat( statement, "a.fextsize, a.nextsize" ); strcat( statement, ", a.locklevel, b.colname, b.colno, b.coltype" ); strcat( statement, ", b.collength, a.npused, a.tabtype " ); strcat( statement, ", colmin, colmax, nrows " ); strcat( statement, "FROM systables a, outer syscolumns b " ); strcat( statement, "WHERE a.tabid = b.tabid " ); if (!tflag) strcat( statement, "AND a.tabid >= 100 " ); strcat( statement, "AND a.tabname MATCHES ? " ); strcat( statement, "ORDER BY a.tabid, b.colno; " ); EXEC SQL PREPARE stmt1 FROM :statement; if (sqlca.sqlcode) { fprintf( stderr, "Cannot PREPARE select statement for table data, error: %d.\n", sqlca.sqlcode ); fprintf( stderr, "Error at character %d.\nStatement is:\n", sqlca.sqlerrd[4] ); fprintf( stderr, statement ); rexit(2); } EXEC SQL DECLARE a1 CURSOR FOR stmt1; if (sqlca.sqlcode ) { fprintf( stderr, "Cannot declare a1 cursor; Error = %d\n", sqlca.sqlcode ); rexit(1); } EXEC SQL OPEN a1 USING :tablename; if (sqlca.sqlcode == SQLNOTFOUND) { fprintf( stderr, "No table matching \"%s\".\n", tablename ); rexit(3); } else if ( sqlca.sqlcode ) { fprintf( stderr,"Cannot open a1 cursor; Error = %d\n", sqlca.sqlcode ); rexit(1); } if (BBDATE) if (FORTRAN) fprintf( outfile, " INCLUDE '/bbinc/Finclude/blpdates.inc'\n" ); else fprintf( outfile, "#include \"/bbinc/Cinclude/blpdates.h\"\n" ); i = 0; if (ESQL) fprintf( outfile, "EXEC SQL BEGIN DECLARE SECTION;\n" ); for(;;) { EXEC SQL fetch a1 into :systables.tabname, :systables.tabid, :systables.partnum, :systables.fextsize, :systables.nextsize, :systables.locklevel, :syscolumns.colname, :syscolumns.colno, :syscolumns.coltype, :syscolumns.collength, :systables.npused, :systables.tabtype, :syscolumns.colmin, :syscolumns.colmax, :systables.nrows; if (sqlca.sqlcode && sqlca.sqlcode != SQLNOTFOUND ) { fprintf( stderr, "Error fetching a1 cursor;Error=%d\n", sqlca.sqlcode); rexit(1); } else { if ((a_space=strchr( systables.tabname, ' ')) != 0) a_space[0] = '\0'; if ((sqlca.sqlcode && sqlca.sqlcode == SQLNOTFOUND) || strcmp( prev.tabname, systables.tabname ) != 0) { ucase( upname, systables.tabname ); if (!pass_no) { if (prev.tabtype[0] == 'T') { if (FORTRAN) { fprintf( outfile, " end structure\n" ); fprintf( outfile, " record/%s_t/ %s\n", prev.tabname, prev.tabname ); fprintf( outfile, " common/%s_c/ %s\n", prev.tabname, prev.tabname ); } else fprintf( outfile, "} %s_t;\n", prev.tabname ); } } else pass_no = 0; i = 0; if (sqlca.sqlcode && sqlca.sqlcode == SQLNOTFOUND) break; if (systables.tabtype[0] == 'T') { if (FORTRAN) fprintf( outfile, "\n structure/%s_t/\n", upname ); else fprintf( outfile, "\ntypedef struct %s_S {\n", upname ); } } } if (systables.tabtype[0] != 'T') { memcpy( &prev, &systables, sizeof prev ); continue; } strtok( syscolumns.colname, " " ); syscolumns.coltype &= SQLTYPE; if ( syscolumns.coltype == SQLCHAR ) if (FORTRAN) fprintf( outfile, " character*%d %s\n", syscolumns.collength, syscolumns.colname ); else if (ESQL) if (VCHAR) fprintf( outfile, "\tstring %s[%d];\n", syscolumns.colname, syscolumns.collength + 1); else fprintf( outfile, "\tfixchar %s[%d];\n", syscolumns.colname, syscolumns.collength ); else fprintf( outfile, "\tchar %s[%d];\n", syscolumns.colname, syscolumns.collength ); else if ( syscolumns.coltype == SQLSMINT ) if (FORTRAN) fprintf( outfile, " integer*2 %s\n", syscolumns.colname ); else fprintf( outfile, "\tshort %s;\n", syscolumns.colname ); else if ( syscolumns.coltype == SQLINT ) if (FORTRAN) fprintf( outfile, " integer*4 %s\n", syscolumns.colname ); else fprintf( outfile, "\tlong %s;\n", syscolumns.colname ); else if ( syscolumns.coltype == SQLFLOAT) if (FORTRAN) fprintf( outfile, " real*8 %s\n", syscolumns.colname ); else fprintf( outfile, "\tdouble %s;\n", syscolumns.colname ); else if ( syscolumns.coltype == SQLSMFLOAT) if (FORTRAN) fprintf( outfile, " real*4 %s\n", syscolumns.colname ); else fprintf( outfile, "\tfloat %s;\n", syscolumns.colname ); else if ( syscolumns.coltype == SQLDECIMAL) if (FORTRAN) fprintf( outfile, "C record/dec_t/ %s !NOT SUPPORTED YET\n", syscolumns.colname ); else fprintf( outfile, "\tdec_t %s;\n", syscolumns.colname ); else if ( syscolumns.coltype == SQLSERIAL) { if (FORTRAN) fprintf( outfile, " integer*4 %s; ! SERIAL\n", syscolumns.colname ); else fprintf( outfile, "\tlong %s; /* SERIAL */\n", syscolumns.colname ); } else if ( syscolumns.coltype == SQLDATE) if (FORTRAN) fprintf( outfile, " integer*4 %s ! INFORMIX DATE\n", syscolumns.colname ); else fprintf( outfile, "\ttime_t %s; /* DATE */\n", syscolumns.colname ); else if ( syscolumns.coltype == SQLMONEY) if (FORTRAN) fprintf( outfile, "C record/dec_t/ %s ! MONEY - NOT YET SUPPORTED\n", syscolumns.colname ); else fprintf( outfile, "\tdec_t %s; /* MONEY */\n", syscolumns.colname ); else if ( syscolumns.coltype == SQLDTIME) { if (BBDATE) if (FORTRAN) fprintf( outfile, " record/BB_DateTime_t/ %s " "! BLOOMBERG DATETIME\n", syscolumns.colname ); else fprintf( outfile, "\tBB_DateTime_t %s; /* BLOOMBERG DATETIME */\n", syscolumns.colname ); else if (FORTRAN) fprintf( outfile, "C record/dtime_t/ %s " "! DATETIME - NOT YET SUPPORTED\n", syscolumns.colname ); else fprintf( outfile, "\tdtime_t %s; /* DATETIME */\n", syscolumns.colname ); } else if ( syscolumns.coltype == SQLINTERVAL) { if (BBDATE) if (FORTRAN) fprintf( outfile, "C record/BB_DateTime_t/ %s " "! INTERVAL - NOT YET SUPPORTED\n", syscolumns.colname ); else fprintf( outfile, "\tintrvl_t %s; /* INTERVAL */\n", syscolumns.colname ); else if (FORTRAN) fprintf( outfile, "C record/intrvl_t/ %s " "! INTERVAL - NOT YET SUPPORTED\n", syscolumns.colname ); else fprintf( outfile, "\tintrvl_t %s; /* INTERVAL */\n", syscolumns.colname ); } else if ( syscolumns.coltype == SQLVCHAR) if (FORTRAN) fprintf( outfile, " character*%d %s\n", syscolumns.collength % 256, syscolumns.colname ); else if (ESQL) fprintf( outfile, "\tstring %s[%d];\n", syscolumns.colname, (syscolumns.collength % 256) + 1 ); else fprintf( outfile, "\tchar %s[%d];\n", syscolumns.colname, (syscolumns.collength % 256) + 1 ); else if ( syscolumns.coltype == SQLBYTES || syscolumns.coltype == SQLTEXT) { if (FORTRAN) fprintf( outfile, "C BLOB Fields no supported by dbstruct.\n" ); else fprintf( outfile, "\tloc_t %s;\n", syscolumns.colname ); } else if (FORTRAN) fprintf( outfile, "C UNKNOWN TYPE(%d) for COLUMN (%s) */\n", syscolumns.coltype, syscolumns.colname ); else fprintf( outfile, "/* UNKNOWN TYPE(%d) for COLUMN (%s) */\n", syscolumns.coltype, syscolumns.colname ); memcpy( &prev, &systables, sizeof prev ); } if (ESQL) fprintf( outfile, "EXEC SQL END DECLARE SECTION;\n" ); } rexit(int stat) { if (stat ) { EXEC SQL close a1; if (sqlca.sqlcode) { fprintf( stderr, "cannot close a1 cursor;Error=%d\n", sqlca.sqlcode); } } EXEC SQL CLOSE DATABASE; EXEC SQL DISCONNECT ALL; exit(stat); } void usage( char *program ) { fprintf(stderr, "Usage:\t%s [-F] [-h host] -d database [-t table] [-e [-f]] [-B] [filename]\n", program ); fprintf( stderr, "\t-B - Represent DATETIME and INTERVAL columns as Bloomberg Datetime.\n" ); fprintf( stderr, "\t-F - generate FORTRAN STRUCTURES rather than \"C\".\n" ); fprintf( stderr, "\t-e - generate ESQL-C style include file.\n" ); fprintf( stderr, "\t-f - generate 'fixchar' type for strings in ESQL headers rather than\n\t 'string' type.\n" ); fprintf( stderr, "\tHost defaults to current host.\n" ); fprintf( stderr, "\tTable may contain valid \"matches\" clause wildcards.\n" ); fprintf( stderr, "\tTable defaults to all user tables %s\n\n", "(excluding system catalog tables.)" ); } void ucase( char *target, char *source ) { while (*source) *target++ = toupper( *source++ ); *target = (char)0; } MKSHAR_EOF