476 lines
15 KiB
Plaintext
476 lines
15 KiB
Plaintext
-- 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<num_args()) THEN
|
|
-- Get value ( follows option argument )
|
|
LET paramval = arg_val(optidx+1)
|
|
END IF
|
|
RETURN paramval
|
|
END FUNCTION
|
|
|
|
FUNCTION cmdarg_option_index(optname)
|
|
DEFINE optname STRING -- The option name without '-'
|
|
DEFINE idx, cnt, optidx INTEGER
|
|
DEFINE fopt STRING
|
|
LET optidx = -1
|
|
LET cnt = num_args()
|
|
LET fopt = "-", optname -- UNIX convention
|
|
FOR idx=1 TO cnt
|
|
IF (arg_val(idx)=fopt) THEN
|
|
LET optidx = idx
|
|
EXIT FOR
|
|
END IF
|
|
END FOR
|
|
RETURN optidx
|
|
END FUNCTION
|
|
|
|
FUNCTION cmdarg_option_isopt(name)
|
|
DEFINE name STRING
|
|
RETURN ( name.getCharAt(1) == "-" )
|
|
END FUNCTION
|
|
|
|
-- Syntax:
|
|
-- <option>+ = 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
|
|
|