-- Warning: Uses fgldbslib.4gl DEFINE params RECORD dbname STRING, filename STRING, compat SMALLINT, igncve SMALLINT, verbose SMALLINT END RECORD CONSTANT toolname = "fgldbsch" CONSTANT fext_coltypes = ".sch" CONSTANT fext_colvalid = ".val" CONSTANT fext_colattrs = ".att" #------------------------------------------------------------------------------- MAIN DEFINE i, s SMALLINT DEFINE fn STRING DEFER INTERRUPT DEFER QUIT IF num_args()==0 THEN CALL display_usage() EXIT PROGRAM 1 END IF CALL fgldbsch_init() -- Check if program was called by fglschema script IF cmdarg_option_used("om") THEN CALL parse_fglschema_options() ELSE CALL parse_fgldbsch_options() END IF IF ( params.dbname IS NULL OR LENGTH(params.dbname) = 0 ) THEN CALL display_usage() EXIT PROGRAM 1 END IF CALL fgldbsch_set_parameter("dbname",params.dbname) LET s = fgldbsch_connect() IF s<>0 THEN DISPLAY "Connection to database '", params.dbname, "' failed." IF SQLCA.SQLCODE<>0 THEN DISPLAY SQLCA.SQLCODE, ": ", SQLERRMESSAGE ELSE DISPLAY s, ": ", err_get(s) END IF EXIT PROGRAM 1 END IF IF fgldbsch_get_db_type() IS NULL THEN DISPLAY "Could not identify the database type..." EXIT PROGRAM 1 END IF IF params.compat AND fgldbsch_get_db_type() == "IFX" THEN CALL fgldbsch_set_parameter( "systabs", "Y" ) END IF LET int_flag=FALSE LET quit_flag=FALSE IF params.filename IS NULL THEN LET params.filename = params.dbname LET i = params.dbname.getIndexOf("@",2) -- "dbname@informixserver" IF i>0 THEN LET params.filename = params.dbname.subString(1,i-1) END IF LET i = params.dbname.getIndexOf("+",2) -- "dbsource+driver='xxx'" IF i>0 THEN LET params.filename = params.dbname.subString(1,i-1) END IF END IF IF params.verbose THEN DISPLAY "" DISPLAY toolname DISPLAY "----------------------------------------------------------------" DISPLAY "" DISPLAY "Extracting database schema from '", params.dbname, "'" DISPLAY " Database server type detected : ", fgldbsch_get_db_type() DISPLAY "" END IF IF fgldbsch_extract() < 0 THEN DISPLAY "Could not extract database schema..." EXIT PROGRAM 1 END IF LET fn = params.filename, fext_coltypes CALL output_schema(fn) IF params.compat THEN DISPLAY "File \"", fn, "\" created." END IF IF fgldbsch_get_db_type() == "IFX" THEN CALL extract_upscol() END IF LET s = fgldbsch_disconnect() CALL fgldbsch_fini() END MAIN #------------------------------------------------------------------------------- FUNCTION parse_fglschema_options() DEFINE i INTEGER DEFINE opt, tmp STRING LET params.compat = TRUE CALL fgldbsch_set_parameter("compat","Y") FOR i=1 TO num_args() LET tmp = arg_val(i) IF tmp.getCharAt(1) == "-" THEN LET opt = tmp.subString(2, tmp.getLength()) CASE opt WHEN "om" -- ignore WHEN "c" CALL fgldbsch_set_parameter("cvmeth", "BBBBBBBBBBBBBB") WHEN "r" LET params.igncve = TRUE OTHERWISE CALL display_usage() EXIT PROGRAM 1 END CASE ELSE LET params.dbname = tmp END IF END FOR END FUNCTION #------------------------------------------------------------------------------- FUNCTION parse_fgldbsch_options() -- Warning: om option just used to detect old mode usage (see scripts) IF cmdarg_option_check( "*|V|h|H|v|om|ct|db+|dv+|un+|up+|ow+|tn+|cv+|of+|ie|cu|cl|cc|st" ) != 0 THEN CALL display_usage() EXIT PROGRAM 1 END IF IF cmdarg_option_used( "V" ) THEN DISPLAY "WARNING: Version info is displayed by calling script!" EXIT PROGRAM 0 END IF IF cmdarg_option_used( "h" ) THEN CALL display_usage() EXIT PROGRAM 0 END IF IF cmdarg_option_used( "H" ) THEN CALL display_help() EXIT PROGRAM 0 END IF IF cmdarg_option_used( "ct" ) THEN CALL fgldbsch_display_convtables() EXIT PROGRAM 0 END IF LET params.dbname = cmdarg_option_param("db") LET params.filename = cmdarg_option_param( "of" ) LET params.igncve = cmdarg_option_used( "ie" ) LET params.verbose = cmdarg_option_used( "v" ) IF params.verbose THEN CALL fgldbsch_set_parameter("verbose", "Y") END IF CALL fgldbsch_set_parameter("dbdriver", cmdarg_option_param("dv")) CALL fgldbsch_set_parameter("dbowner", cmdarg_option_param("ow")) CALL fgldbsch_set_parameter("username", cmdarg_option_param("un")) CALL fgldbsch_set_parameter("userpswd", cmdarg_option_param("up")) CALL fgldbsch_set_parameter("tabname", cmdarg_option_param("tn")) CASE WHEN cmdarg_option_used( "cc" ) CALL fgldbsch_set_parameter("casens","C") WHEN cmdarg_option_used( "cu" ) CALL fgldbsch_set_parameter("casens","U") WHEN cmdarg_option_used( "cl" ) CALL fgldbsch_set_parameter("casens","L") END CASE IF cmdarg_option_used( "st" ) THEN CALL fgldbsch_set_parameter( "systabs", "Y" ) END IF IF cmdarg_option_used( "cv" ) THEN CALL fgldbsch_set_parameter("cvmeth", cmdarg_option_param("cv")) END IF END FUNCTION #------------------------------------------------------------------------------- FUNCTION output_schema(fn) DEFINE fn STRING DEFINE c,x INTEGER DEFINE tmp STRING DEFINE ch base.Channel DEFINE cl DYNAMIC ARRAY OF RECORD tabname STRING, colname STRING, nattype STRING, ifxtype INTEGER, ifxleng INTEGER, ordinal INTEGER END RECORD LET ch = base.Channel.create() CALL ch.setDelimiter("^") CALL ch.openFile(fn,"w") WHILE fgldbsch_next_table() == 1 IF params.verbose THEN DISPLAY "Table : ", fgldbsch_get_table_name() END IF LET x=1 FOR c=1 TO fgldbsch_get_column_count() CALL fgldbsch_get_column_info(c) RETURNING cl[x].tabname, cl[x].colname, cl[x].nattype, cl[x].ifxtype, cl[x].ifxleng LET cl[x].ordinal = c IF cl[x].ifxtype = -1 THEN LET tmp = SFMT("Table \"%1.%2\" has not a valid datatype [%3]", cl[x].tabname, cl[x].colname, cl[x].nattype) IF params.compat THEN IF params.igncve THEN CONTINUE FOR ELSE DISPLAY "Warning: ", tmp, " - table is ignored." CONTINUE WHILE END IF ELSE IF params.igncve THEN DISPLAY "Warning: ", tmp, " - table is ignored." CONTINUE WHILE ELSE DISPLAY "ERROR: ", tmp EXIT PROGRAM 1 END IF END IF END IF LET x=x+1 END FOR FOR c=1 TO x-1 CALL ch.write([cl[c].tabname,cl[c].colname,cl[c].ifxtype,cl[c].ifxleng,cl[c].ordinal]) END FOR END WHILE CALL ch.close() END FUNCTION FUNCTION extract_upscol() DEFINE fn STRING DEFINE ct INTEGER WHENEVER ERROR CONTINUE SELECT COUNT(*) INTO ct FROM syscolval WHENEVER ERROR STOP IF status = 0 THEN LET fn = params.filename, fext_colvalid UNLOAD TO fn DELIMITER "^" SELECT UNIQUE tabname, colname, attrname, attrval FROM syscolval ORDER BY tabname, colname IF params.compat THEN DISPLAY "File \"", fn, "\" created." END IF END IF WHENEVER ERROR CONTINUE SELECT COUNT(*) INTO ct FROM syscolatt WHENEVER ERROR STOP IF status = 0 THEN LET fn = params.filename, fext_colattrs UNLOAD TO fn DELIMITER "^" SELECT UNIQUE tabname, colname, seqno, color, inverse, underline, blink, left, def_format, condition FROM syscolatt ORDER BY tabname, colname, seqno IF params.compat THEN DISPLAY "File \"", fn, "\" created." END IF END IF END FUNCTION FUNCTION display_usage() DISPLAY "Usage: ", toolname, " [options]" DISPLAY " -V : Display version information." DISPLAY " -h : Display this help." DISPLAY " -H : Display complete help." DISPLAY " -v : Verbose mode. Default is silent." DISPLAY " -ct : Show data type conversion tables." DISPLAY " -db name : Database name." DISPLAY " -dv name : Database driver." DISPLAY " -un user : Database User name." DISPLAY " -up pswd : Database User password." DISPLAY " -ow name : Database schema/tables owner." DISPLAY " -tn name : Extract specific table schema." DISPLAY " -cv strg : Specify conversion method." DISPLAY " -of name : Output filename prefix. Default is database name." DISPLAY " -ie : Ignore tables with columns that cannot be converted." DISPLAY " -cu : Generate upper case names." DISPLAY " -cl : Generate lower case names." DISPLAY " -cc : Generate case sensitive names." DISPLAY " -st : Generate database system tables." END FUNCTION FUNCTION display_help() CALL display_usage() DISPLAY "" DISPLAY "This program generates database schema description files from system" DISPLAY "catalog tables." DISPLAY "" DISPLAY "List of generated files:" DISPLAY " . schemaname.sch : Column definitions with data types." DISPLAY " . schemaname.val : Validation rules and attributes." DISPLAY " . schemaname.att : Video attributes (upscol compatibility)." DISPLAY "" DISPLAY "The database type is detected automatically after connection, you do" DISPLAY "not have to specify any database server type." DISPLAY "" DISPLAY "You must at least give the database name with the '-db' option. For" DISPLAY "example : ' -db stores '." DISPLAY "" DISPLAY "If the operating system user is not the database user, you can give" DISPLAY "a database login and password with the '-un' and '-up' options." DISPLAY "" DISPLAY "For some databases you may need to specify the schema owner otherwise" DISPLAY "you may get all the system tables too. Use the '-ow' option to give" DISPLAY "the database schema owner." DISPLAY "" DISPLAY "By default table and column names are converted to lower case letters" DISPLAY "to enforce compatibility with Informix. But you can force lower case," DISPLAY "upper case or case sensitive generation by giving the '-cl', '-cu' or" DISPLAY "'-cc' options." DISPLAY "" DISPLAY "Data types generation in schema files can be controlled with the -cv" DISPLAY "option. Each character position of the string passed to this option" DISPLAY "corresponds to a data type in the conversion table." DISPLAY "For example, ' -cv AAB' indicates that the first and second data type" DISPLAY "in the conversion table must use the convertion A, while the third" DISPLAY "type uses conversion B." DISPLAY "When using X as conversion code, the columns using the corresponding" DISPLAY "data types will be ignored and not written to the .sch file. This is" DISPLAY "particularly useful in the case of auto-generated columns like with" DISPLAY "SQL Server's uniqueidentifier data type, using DEFAULT NEWID()." DISPLAY "Run this program with the '-ct' option to see the conversion tables." DISPLAY "" DISPLAY "Use the -tn option to extract the description of only one specific" DISPLAY "table from the database. This option is useful if you have created" DISPLAY "new tables in your database, and you need to extract the schema of" DISPLAY "the new tables without overwriting the existing schema files." DISPLAY "" DISPLAY "Some data types may not be converted to BDL types, and cause an" DISPLAY "error during schema extraction. To ignore unsupported types, use" DISPLAY "the -ie option (you still get warnings)." DISPLAY "" DISPLAY "Use the -st option to include system tables in the schema. System" DISPLAY "tables description are not needed if you do not explicitly use" DISPLAY "them in the programs." DISPLAY "" END FUNCTION #------------------------------------------------------------------------------- FUNCTION cmdarg_option_used(optname) DEFINE optname STRING -- The option name without '-' RETURN ( cmdarg_option_index(optname)>0 ) END FUNCTION FUNCTION cmdarg_option_param(optname) DEFINE optname STRING -- The option name without '-' DEFINE optidx INTEGER DEFINE paramval STRING -- Can be a directory path ! LET paramval = NULL LET optidx = cmdarg_option_index(optname) IF (optidx>0) AND (optidx+ = option has parameter (next arg must not use -) -- * = simple string is allowed as argument FUNCTION cmdarg_option_check(optlist) DEFINE optlist STRING -- The possible options (v|x|cv|of+|*) DEFINE optspec STRING DEFINE optpara STRING DEFINE i, j INTEGER DEFINE indiv, found INTEGER DEFINE tok base.StringTokenizer DEFINE optarr DYNAMIC ARRAY OF RECORD optname STRING, hasparam INTEGER END RECORD LET tok = base.StringTokenizer.create(optlist, "|") WHILE tok.hasMoreTokens() LET optspec = tok.nextToken() IF optspec = "*" THEN LET indiv = TRUE CONTINUE WHILE END IF LET i = optspec.getIndexOf("+",1) CALL optarr.appendElement() IF i > 0 THEN LET optarr[optarr.getLength()].hasparam = 1 LET optspec = optspec.substring(1,i-1) END IF LET optarr[optarr.getLength()].optname = optspec END WHILE FOR i=1 TO num_args() LET optspec = arg_val(i) IF NOT cmdarg_option_isopt(optspec) THEN IF indiv THEN CONTINUE FOR ELSE RETURN i END IF END IF LET optspec = optspec.substring(2,optspec.getLength()) LET found = FALSE FOR j=1 TO optarr.getLength() IF optarr[j].optname == optspec THEN LET found = TRUE LET optpara = arg_val(i+1) IF optarr[j].hasparam THEN IF optpara IS NULL OR cmdarg_option_isopt(optpara) THEN LET found = FALSE ELSE LET i = i + 1 END IF ELSE IF optpara IS NOT NULL AND NOT cmdarg_option_isopt(optpara) THEN LET found = FALSE END IF END IF EXIT FOR END IF END FOR IF NOT found THEN RETURN i END IF END FOR RETURN 0 END FUNCTION