3817 lines
127 KiB
Plaintext
3817 lines
127 KiB
Plaintext
|
*****************************************************************************
|
||
|
*
|
||
|
* Routine to convert a uniVerse file into an SQL table
|
||
|
*
|
||
|
* Module %M% Version %I% Date %H%
|
||
|
*
|
||
|
* (c) Copyright 1998 Ardent Software Inc. - All Rights Reserved
|
||
|
* This is unpublished proprietary source code of Ardent Software Inc.
|
||
|
* The copyright notice above does not evidence any actual or intended
|
||
|
* publication of such source code.
|
||
|
*
|
||
|
*******************************************************************************
|
||
|
*
|
||
|
* Maintenance log - insert most recent change descriptions at top
|
||
|
*
|
||
|
* Date.... GTAR# WHO Description........................................
|
||
|
* 10/14/98 23801 SAP Change copyrights.
|
||
|
* 09/09/98 23597 TFH Avoid endless loop expanding @/@KEY/@SELECT phrases
|
||
|
* 03/31/98 21228 TFH Justific'n = L/T/U/none and no SQLTYPE ==> VARCHAR
|
||
|
* 07/03/97 21036 TFH If phantom, don't set "page-wait" flag
|
||
|
* 06/03/97 20695 TFH Add "change datatype" editing feature
|
||
|
* 05/23/97 20695 TFH Many release 9.4 changes to CONVERT.SQL (see spec)
|
||
|
* 10/15/96 17743 TFH Convert more than 99 fields to columns (see MAXC)
|
||
|
* 07/02/96 18760 EAP For RESTORE close files before renaming them
|
||
|
* 03/13/96 17797 AGM Replace 'SH -c' with OS.EXEC
|
||
|
* 01/31/95 17671 LAG Windows NT port
|
||
|
* 12/28/95 17827 TFH Additional changes for MVASSOC option (GTAR 17669)
|
||
|
* 12/11/95 17669 TFH Add MVASSOC option, which makes every non-associated
|
||
|
* multi-valued field into an association
|
||
|
* 12/08/95 17744 TFH Support GEN.SAVE option
|
||
|
* 12/06/95 17692 TFH Identifiers starting with lowercase letter are legal
|
||
|
* 11/14/95 17641 TFH If RESTORE, do DROP TABLE...CASCADE
|
||
|
* 10/03/95 17438 TFH Handle format such as 12QR3E1
|
||
|
* 10/02/95 15808 TFH Handle format of 0L
|
||
|
* 09/26/95 17405 TFH Handle conversion of form MD0x where x is non-digit
|
||
|
* 06/27/95 16608 TFH Don't use @SCHEMA to find out if we're in a schema;
|
||
|
* use proper CREATE.FILE syntax if pick-type flavor
|
||
|
* 11/02/94 15251 TFH Remove ability to enter new filename when exiting
|
||
|
* 10/25/94 15199 TFH Fix problem with Pick association (C;n;n;n)
|
||
|
* 10/21/94 15164 TFH Preserve "Key" indication if an ASSOC is defined
|
||
|
* in more than one synonym of the same field
|
||
|
* 10/14/94 15118 TFH If LPTR, send *all* CREATE TABLE messgs to printer
|
||
|
* 10/07/94 15090 TFH Discard dict fields with Pick correlatives; change
|
||
|
* "SHOW" to "INFO", "P" to "S"; add SQLDEF time stamp
|
||
|
* 09/30/94 15018 TFH Use sdml's return code to see if table was created
|
||
|
* 09/29/94 15028 TFH Disallow the conversion of type 25 files to tables
|
||
|
* 09/19/94 14958 TFH If SQLTYPE (in dict) contains garbage, ignore it.
|
||
|
* 09/18/94 14942 TFH Force association columns to be multivalued,
|
||
|
* handle single-quotes correctly in COL.HDG etc,
|
||
|
* fix bug in check for duplicating dictionary entry
|
||
|
* 09/08/94 14870 TFH Handle LOC = '00' or '', other minor cleanup
|
||
|
* 09/01/94 14569 TFH Handle PICK flavor better
|
||
|
* 08/30/94 14569 TFH Handle overlapping associations
|
||
|
* 08/28/94 14569 TFH Make sure column and table names are unique
|
||
|
* 08/25/94 14569 TFH Initial version
|
||
|
*
|
||
|
*******************************************************************************
|
||
|
* June 1997
|
||
|
* CONVERT.SQL (SQL-ization utility)
|
||
|
*
|
||
|
$OPTIONS DEFAULT
|
||
|
|
||
|
$INCLUDE UNIVERSE.INCLUDE MACHINE.NAME
|
||
|
|
||
|
* Set printer page-handling mode to pause after printing a page full of lines
|
||
|
* (this feature is normally off in Pick-flavor accounts).
|
||
|
**** DO THIS ONLY IF NOT RUNNING AS A PHANTOM *******************************
|
||
|
SYS1005 = system(1005)
|
||
|
if not(system(25)) then assign 1 to system(1005)
|
||
|
|
||
|
******************
|
||
|
** Windows NT port
|
||
|
**
|
||
|
DEFFUN IS.FULLPATH(FILESPEC) CALLING "*IS.FULLPATH"
|
||
|
DEFFUN IS.EQPATHS(FILESPEC.A, FILESPEC.B) CALLING "*IS.EQPATHS"
|
||
|
DEFFUN GET.DIRNAME(FILESPEC) CALLING "*GET.DIRNAME"
|
||
|
DEFFUN GET.BASENAME(FILESPEC) CALLING "*GET.BASENAME"
|
||
|
**
|
||
|
******************
|
||
|
|
||
|
prompt ''
|
||
|
CMDNAME = "CONVERT.SQL" ; * Name of this command
|
||
|
CURREVCODE = "REV#1" ; * Current Rev number of file format of SQLDEF file
|
||
|
MAXSLEEP = 5 ; * Number of 2-second intervals to sleep awaiting freeing of lock
|
||
|
MAXLINES = 1000 ; * Max number of lines in CREATE EXISTING TABLE statement
|
||
|
TOPMAX = 500 ; * Max field number that can be converted to a column
|
||
|
MAXC = 49 ; * Array size for storing information about every field (this is
|
||
|
* adjusted upward if stored fields with AMC > 49 are found)
|
||
|
MAXNEW = 49 ; *Max number of newly created dictionary column-name entries (this
|
||
|
* is adjusted upward if stored fields with AMC > 49 are found)
|
||
|
MAXWARN = 100 ; * Max number of warnings to be displayed when analyzing dict.
|
||
|
WARNX = 1 ; * Next available position in the WARNINGS array
|
||
|
|
||
|
FILENM = ""
|
||
|
ARGA = ""
|
||
|
DISP = 1
|
||
|
SHOW = 0
|
||
|
DEBUG = 0
|
||
|
DEBUGFIRST = 0
|
||
|
LPTR = 0
|
||
|
SAVE = 0
|
||
|
NEWDEF = 0
|
||
|
CRTAB = 0
|
||
|
TEST = 0
|
||
|
TESTEDIT = 0
|
||
|
CRXDONE = 0
|
||
|
BERX = 0
|
||
|
|
||
|
******************
|
||
|
** Windows NT port
|
||
|
**
|
||
|
UV.ROOT = SYSTEM(32)
|
||
|
**
|
||
|
******************
|
||
|
|
||
|
* Determine what CREATE.FILE syntax to use
|
||
|
FLAVOR = system(1001)
|
||
|
if FLAVOR = 2 or FLAVOR = 8 or FLAVOR = 16 then
|
||
|
CREATE.SYNTAX = "PICK"
|
||
|
end else
|
||
|
CREATE.SYNTAX = ""
|
||
|
end
|
||
|
|
||
|
* Process command line arguments
|
||
|
get(arg.) FILENM else goto VERUSER
|
||
|
gosub NEXTARG
|
||
|
begin case
|
||
|
case ARGL = ""
|
||
|
goto VERUSER
|
||
|
case ARGL = "TESTEDIT" ; * With this option, which is for internal testing
|
||
|
* only, interactive editing commands can be
|
||
|
* submitted as DATA statements in the test
|
||
|
* paragraph; the final DATA statement should
|
||
|
* contain the "Q" command (or "X" or "X.SAVEDATA").
|
||
|
* Up to 25 commands can be processed with this
|
||
|
* mechanism.
|
||
|
TEST = 1
|
||
|
BADOPT = 0
|
||
|
TESTEDIT = 1
|
||
|
dim INPUTS(26)
|
||
|
mat INPUTS = ""
|
||
|
INCOUNT = 1
|
||
|
loop
|
||
|
LPEND = len(@DATA.PENDING)
|
||
|
while LPEND <> 0 do
|
||
|
input NXX
|
||
|
while NXX # "Q" and INCOUNT <= 25 do
|
||
|
if NXX[1,1] = "*" then continue
|
||
|
INPUTS(INCOUNT) = NXX
|
||
|
INCOUNT += 1
|
||
|
repeat
|
||
|
INPUTS(INCOUNT) = "Q"
|
||
|
INCOUNT = 1
|
||
|
case ARGL = "TEST"
|
||
|
TEST = 1
|
||
|
BADOPT = 0
|
||
|
case ARGL = "CREATE"
|
||
|
CRTAB = 1
|
||
|
BADOPT = 0
|
||
|
case ARGL = "INFO" or ARGL = "RESTORE" or ARGL = "RESTOREDATA"
|
||
|
ARGA = ARGL
|
||
|
BADOPT = 0
|
||
|
case LPTR = 1
|
||
|
BADOPT = 6
|
||
|
case DEBUG = 1
|
||
|
DEBUGFIRST = 1
|
||
|
end case
|
||
|
|
||
|
loop
|
||
|
until BADOPT
|
||
|
gosub NEXTARG
|
||
|
until ARGL = "" or BADOPT
|
||
|
repeat
|
||
|
|
||
|
if BADOPT = 1 then stop "Illegal command line argument: ":ARGL
|
||
|
if BADOPT = 2 then stop ARGL:" option is only legal with TEST or CREATE"
|
||
|
if BADOPT = 3 then stop "Duplicated command line argument: ":ARGL
|
||
|
if BADOPT = 4 then stop ARGL:" option is only legal with CREATE"
|
||
|
if BADOPT = 5 then
|
||
|
stop "If DEBUG is first argument after filename it must be the last"
|
||
|
end
|
||
|
if BADOPT = 6 then
|
||
|
stop "LPTR option is not legal as first argument after filename"
|
||
|
end
|
||
|
goto VERUSER
|
||
|
|
||
|
* This subroutine gets the next command line argument
|
||
|
* Output: ARGL is set to empty if there are no more command line arguments
|
||
|
* or: ARGL contains the next command line argument if there is one
|
||
|
* BADOPT = 0 if ARGL is a valid option; in this case, the proper
|
||
|
* flag is set: NEWDEF, DISP, SHOW, SAVE, LPTR, or DEBUG
|
||
|
* BADOPT = 1 if the argument (ARGL) is not a valid option keyword
|
||
|
* BADOPT = 2 if this option (GEN, SHOW, BRIEF) is illegal w this action
|
||
|
* BADOPT = 3 if the argument (ARGL) is a duplicated option
|
||
|
* BADOPT = 4 if this option SAVEDATA is illegal with this action
|
||
|
* BADOPT = 5 if DEBUG is the first argument and another arg is found
|
||
|
NEXTARG:
|
||
|
BADOPT = 0
|
||
|
ARGL = ""
|
||
|
get(arg.) ARGL else return
|
||
|
if DEBUGFIRST then BADOPT = 5 ; return
|
||
|
ARGL = upcase(ARGL)
|
||
|
begin case
|
||
|
case ARGL = "GEN"
|
||
|
if not(CRTAB or TEST) then BADOPT = 2
|
||
|
if NEWDEF then BADOPT = 3 else NEWDEF = 1
|
||
|
case ARGL = "BRIEF"
|
||
|
if not(CRTAB or TEST) then BADOPT = 2
|
||
|
if DISP = 0 then BADOPT = 3 else DISP = 0
|
||
|
case ARGL = "SHOW"
|
||
|
if not(CRTAB or TEST) then BADOPT = 2
|
||
|
if SHOW then BADOPT = 3 else SHOW = 1
|
||
|
case ARGL = "SAVEDATA"
|
||
|
if not(CRTAB) then BADOPT = 4
|
||
|
if SAVE then BADOPT = 3 else SAVE = 1
|
||
|
case ARGL = "LPTR"
|
||
|
if LPTR then BADOPT = 3 else LPTR = 1
|
||
|
case ARGL = "DEBUG"
|
||
|
if DEBUG then BADOPT = 3 else DEBUG = 1
|
||
|
case 1
|
||
|
BADOPT = 1
|
||
|
end case
|
||
|
return ; *from NEXTARG
|
||
|
|
||
|
* Verify that user is a registered SQL user
|
||
|
VERUSER:
|
||
|
if LPTR then printer on
|
||
|
AUTH = @AUTHORIZATION
|
||
|
open "UV_USERS" to FVAR else stop "Can't open UV_USERS"
|
||
|
read TVAR from FVAR,AUTH then
|
||
|
close FVAR
|
||
|
end else
|
||
|
close FVAR
|
||
|
stop "Can't run ":CMDNAME:": '":AUTH:"' is not an SQL User"
|
||
|
end
|
||
|
|
||
|
* Verify that this account is an SQL schema
|
||
|
VERACCT:
|
||
|
PATH = @PATH
|
||
|
PATH = trim(PATH)
|
||
|
IF OS.TYPE = "UNIX" THEN
|
||
|
if PATH[1] = "/" then PATH = PATH[1,len(PATH)-1]
|
||
|
END ELSE
|
||
|
if PATH[1] = "/" OR PATH[1] = "\" then PATH = PATH[1,len(PATH)-1]
|
||
|
END
|
||
|
* Find out if this account is a schema; put its name (or empty) in SCH
|
||
|
gosub Getschinfo
|
||
|
if len(SCH) <= 0 then
|
||
|
stop "Can't run ":CMDNAME:": '":PATH:"' is not an SQL Schema"
|
||
|
end
|
||
|
|
||
|
* Verify that user has write permission to this account's directory
|
||
|
VERDIR:
|
||
|
TCH = OS.EXEC: ' "': TOUCH.CMD : ' dUmMy.newSQL"'
|
||
|
RMV = OS.EXEC: ' "' : RM.CMD : ' dUmMy.newSQL"'
|
||
|
TVAR = ""
|
||
|
execute TCH capturing TVAR
|
||
|
if len(TVAR) > 1 then
|
||
|
stop "Can't run ":CMDNAME:": No UNIX write permission on '":PATH:"'"
|
||
|
end
|
||
|
execute RMV capturing TVAR
|
||
|
|
||
|
* Verify that user has write permission to the VOC
|
||
|
VERVOC:
|
||
|
VPATH = PATH:"/VOC"
|
||
|
openpath VPATH to VOCVAR else
|
||
|
stop "Can't run ":CMDNAME:": Can't open '":VPATH:"'"
|
||
|
end
|
||
|
LSLEEP = MAXSLEEP
|
||
|
VERVOC1:
|
||
|
if LSLEEP <= 0 then
|
||
|
close VOCVAR
|
||
|
stop "Can't run ":CMDNAME:": '":VPATH:"' is locked"
|
||
|
end
|
||
|
readu TVAR from VOCVAR,"VOC" locked
|
||
|
LSLEEP -= 1
|
||
|
sleep 2
|
||
|
goto VERVOC1
|
||
|
end else
|
||
|
close VOCVAR
|
||
|
stop "Can't run ":CMDNAME:": Can't read from '":VPATH:"'"
|
||
|
end
|
||
|
write TVAR to VOCVAR,"VOC" else
|
||
|
close VOCVAR
|
||
|
stop "Can't run ":CMDNAME:": Can't write to '":VPATH:"'"
|
||
|
end
|
||
|
if ARGA = "INFO" then close VOCVAR ; goto DOINFO
|
||
|
|
||
|
* Prompt for filename if no command line arguments
|
||
|
if FILENM = "" then
|
||
|
print "Enter Filename to be converted (or <return> to Quit)? ":
|
||
|
input FILENM
|
||
|
if FILENM = "" then goto GOODBYE
|
||
|
end
|
||
|
|
||
|
* Get FILENM's VOC pointer, if possible, to support Q-pointer error message
|
||
|
readl RVAR from VOCVAR,FILENM locked
|
||
|
RVAR = ""
|
||
|
end else
|
||
|
RVAR = ""
|
||
|
end
|
||
|
VOCCODE = upcase(RVAR<1>[1,1])
|
||
|
if ARGA[1,7] # "RESTORE" then close VOCVAR ; *leave VOC open if RESTORE
|
||
|
|
||
|
* Open the file, get its STATUS
|
||
|
hush on ; *Suppress ugly message if can't open due to (eg) no SQL permission
|
||
|
open FILENM to FVAR then
|
||
|
hush off
|
||
|
status FSTAT from FVAR else
|
||
|
close FVAR
|
||
|
stop "Can't get STATUS of file '":FILENM:"'"
|
||
|
end
|
||
|
end else
|
||
|
OPSTAT = status()
|
||
|
hush off
|
||
|
print "Can't open file '":FILENM:"'. ":
|
||
|
if OPSTAT = -1 and VOCCODE = "Q" then OPSTAT = -987
|
||
|
gosub POPERR
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
|
||
|
* Verify that the data file is on the local system
|
||
|
FPATH = FSTAT<27,1,1>
|
||
|
IF OS.TYPE = "UNIX" THEN
|
||
|
if FPATH[1] = "/" then FPATH = FPATH[1,len(FPATH)-1]
|
||
|
END ELSE
|
||
|
if FPATH[1] = "/" OR FPATH[1] = "\" then FPATH = FPATH[1,len(FPATH)-1]
|
||
|
END
|
||
|
if fileinfo(FVAR,0) then
|
||
|
if fileinfo(FVAR,12) # "" then
|
||
|
close FVAR
|
||
|
stop "File '":FPATH:"' is not on local system"
|
||
|
end
|
||
|
end else
|
||
|
stop "File '":FPATH:"' is no longer open"
|
||
|
end
|
||
|
* Close the data file
|
||
|
close FVAR
|
||
|
|
||
|
* Open the dictionary, get its STATUS
|
||
|
open "DICT",FILENM to DVAR else
|
||
|
OPSTAT = status()
|
||
|
print "Can't open DICT '":FILENM:"'. ":
|
||
|
gosub POPERR
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
status DSTAT from DVAR else
|
||
|
close DVAR
|
||
|
stop "Can't get STATUS of DICT '":FILENM:"'"
|
||
|
end
|
||
|
|
||
|
* Verify that dictionary is on local system
|
||
|
DPATH = DSTAT<27,1,1>
|
||
|
IF OS.TYPE = "UNIX" THEN
|
||
|
if DPATH[1] = "/" then DPATH = DPATH[1,len(DPATH)-1]
|
||
|
END ELSE
|
||
|
if DPATH[1] = "/" OR DPATH[1] = "\" then DPATH = DPATH[1,len(DPATH)-1]
|
||
|
END
|
||
|
if fileinfo(DVAR,0) then
|
||
|
if fileinfo(DVAR,12) # "" then
|
||
|
close DVAR
|
||
|
stop "Dictionary '":DPATH:"' is not on local system"
|
||
|
end
|
||
|
end else
|
||
|
stop "Dictionary '":DPATH:"' is no longer open"
|
||
|
end
|
||
|
|
||
|
if DEBUG then
|
||
|
print "PATH is ":PATH
|
||
|
print "FPATH is ":FPATH
|
||
|
print "DPATH is ":DPATH
|
||
|
end
|
||
|
* At this point, the dictionary file is open (to DVAR)
|
||
|
|
||
|
if ARGA[1,7] # "RESTORE" then goto ANALYZE
|
||
|
|
||
|
* RESTORE and RESTOREDATA options
|
||
|
* At this point, the VOC file is open (to VOCVAR)
|
||
|
* Close the file's dictionary
|
||
|
close DVAR
|
||
|
* Check to verify that the file is an SQL table
|
||
|
if FSTAT<29,1,1> = 0 then
|
||
|
close VOCVAR
|
||
|
stop "Can't do RESTORE option because '":FILENM:"' is not a table"
|
||
|
end
|
||
|
* Check that <filename>_SQLDEF exists
|
||
|
DEFNM = change(FILENM," ","_"):"_SQLDEF"
|
||
|
readl SVAR from VOCVAR,DEFNM else
|
||
|
close VOCVAR
|
||
|
stop "Can't do RESTORE option because '":DEFNM:"' doesn't exist"
|
||
|
end
|
||
|
release VOCVAR,DEFNM
|
||
|
* See if <filename>_SQLSAVE exists (keep info in ZVAR)
|
||
|
SAVNM = change(FILENM," ","_"):"_SQLSAVE"
|
||
|
readl ZVAR from VOCVAR,SAVNM then
|
||
|
release VOCVAR,SAVNM
|
||
|
end else ZVAR = ""
|
||
|
* Close the VOC file
|
||
|
close VOCVAR
|
||
|
if ARGA[8,4] = "DATA" then
|
||
|
* Check that <filename>_SQLSAVE exists
|
||
|
if ZVAR = "" then
|
||
|
stop "Can't do RESTOREDATA option because '":SAVNM:"' doesn't exist"
|
||
|
end
|
||
|
* Open the SQLSAVE file to VVAR and its dict to DVVAR (locking both)
|
||
|
gosub OPENSAVE
|
||
|
if OPENERR then stop ; * no message needed, OPENSAVE already produced one
|
||
|
end
|
||
|
* Open the SQLDEF file to NVAR and its dict to DNVAR (locking both)
|
||
|
gosub OPENDEF
|
||
|
if OPENERR then
|
||
|
if ARGA[8,4] = "DATA" then
|
||
|
fileunlock VVAR ; close VVAR
|
||
|
fileunlock DVVAR ; close DVVAR
|
||
|
end
|
||
|
stop ; * no message needed, OPENDEF already produced one
|
||
|
end
|
||
|
|
||
|
* Issue the CREATE EXISTING TABLE...RESTORE command
|
||
|
CRXDEF = 'CREATE EXISTING TABLE "':change(FILENM,'"','""'):'" RESTORE;'
|
||
|
if LPTR then
|
||
|
execute CRXDEF capturing CRTABOUT setting TVAR
|
||
|
POUT = change(CRTABOUT,char(254),char(10),0,1)
|
||
|
if POUT[1] = char(10) then POUT = POUT[1,len(POUT)-1]
|
||
|
print POUT
|
||
|
end else
|
||
|
execute CRXDEF setting TVAR
|
||
|
end
|
||
|
* Check to see if CREATE EXISTING TABLE...RESTORE failed
|
||
|
if TVAR <> 0 then
|
||
|
fileunlock NVAR ; close NVAR
|
||
|
fileunlock DNVAR ; close DNVAR
|
||
|
if ARGA[8,4] = "DATA" then
|
||
|
fileunlock VVAR ; close VVAR
|
||
|
fileunlock DVVAR ; close DVVAR
|
||
|
end
|
||
|
if TVAR <> -1 then goto NORETURN
|
||
|
stop "Table could not be restored to a file."
|
||
|
end
|
||
|
* Restore the dictionary from the SQLDEF file
|
||
|
print " Restoring DICT '":FILENM:"' (using ":DEFNM:")"
|
||
|
open "DICT",FILENM to DVAR else
|
||
|
OPSTAT = status()
|
||
|
fileunlock NVAR ; close NVAR
|
||
|
fileunlock DNVAR ; close DNVAR
|
||
|
if ARGA[8,4] = "DATA" then
|
||
|
fileunlock VVAR ; close VVAR
|
||
|
fileunlock DVVAR ; close DVVAR
|
||
|
end
|
||
|
print "Can't open DICT '":FILENM:"'. ":
|
||
|
gosub POPERR
|
||
|
stop
|
||
|
end
|
||
|
* Copy the saved dictionary records from SQLDEF to DICT FILENM
|
||
|
clearfile DVAR
|
||
|
SELNX = 'SELECT ':DEFNM:' WITH NOT (@ID LIKE "SQL_..." AND F1 LIKE "X...")'
|
||
|
execute SELNX capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
NOMORE = 0
|
||
|
loop
|
||
|
until NOMORE
|
||
|
readnext ATID then
|
||
|
read DICTREC from NVAR,ATID else goto RDERR
|
||
|
write DICTREC to DVAR,ATID else goto WRERR
|
||
|
end else NOMORE = 1
|
||
|
repeat
|
||
|
fileunlock NVAR ; close NVAR
|
||
|
fileunlock DNVAR ; close DNVAR
|
||
|
close DVAR
|
||
|
* If RESTOREDATA option, restore the file's data from the SQLSAVE file
|
||
|
if ARGA[8,4] = "DATA" then
|
||
|
print " Restoring DATA '":FILENM:"' (using ":SAVNM:")"
|
||
|
open FILENM to FVAR else
|
||
|
OPSTAT = status()
|
||
|
print "Can't open file '":FILENM:"'. ":
|
||
|
gosub POPERR
|
||
|
stop
|
||
|
end
|
||
|
* Copy the saved data records from SQLSAVE to FILENM
|
||
|
clearfile FVAR
|
||
|
select VVAR
|
||
|
NOMORE = 0
|
||
|
loop
|
||
|
until NOMORE
|
||
|
readnext ATID then
|
||
|
read DATAREC from VVAR,ATID else goto RDERR
|
||
|
write DATAREC to FVAR,ATID else goto WRERR
|
||
|
end else NOMORE = 1
|
||
|
repeat
|
||
|
fileunlock VVAR ; close VVAR
|
||
|
fileunlock DVVAR ; close DVVAR
|
||
|
close FVAR
|
||
|
end
|
||
|
* Delete the SQLSAVE file if it exists
|
||
|
if ZVAR # "" then
|
||
|
print " Deleting file '":SAVNM:"'"
|
||
|
DELSAVE = "DELETE.FILE ":SAVNM
|
||
|
execute DELSAVE capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
end
|
||
|
print "File restored"
|
||
|
goto GOODBYE
|
||
|
|
||
|
* INFO option
|
||
|
DOINFO:
|
||
|
gosub SETUPINFO
|
||
|
CNTASD = CNTD:FILENM:CNTDD2
|
||
|
gosub GETCNT
|
||
|
if CNTCNT = "" then goto GOODBYE
|
||
|
if CNTCNT = 0 then goto DOCNTAS
|
||
|
SUMDDD = SUMDD:FILENM:SUMDD2
|
||
|
execute SUMDDD capturing TVAR
|
||
|
gosub PRINTOUT
|
||
|
DOCNTAS:
|
||
|
CNTASD = CNTD:FILENM:CNTAS2
|
||
|
gosub GETCNT
|
||
|
if CNTCNT = "" or CNTCNT = 0 then goto GOODBYE
|
||
|
SUMAAS = SUMAS:FILENM:SUMAS2
|
||
|
execute SUMAAS capturing TVAR
|
||
|
gosub PRINTOUT
|
||
|
goto GOODBYE
|
||
|
|
||
|
ANALYZE:
|
||
|
gosub GETDAT ; * get printable date/time in DATETIME
|
||
|
print "Analyzing '":FILENM:"' for conversion to SQL ":
|
||
|
if len(FILENM) < 18 then print space(18 - len(FILENM)):
|
||
|
print DATETIME
|
||
|
* Determine if filename is legal as a table name on this O/S
|
||
|
IDENT = FILENM ; IDTYPE = "TABLE" ; gosub CKLEGAL
|
||
|
if ILLEGAL then
|
||
|
close DVAR
|
||
|
print "File name '":FILENM:"' is not a legal table name: ":ILLEGAL
|
||
|
stop "Create VOC entry using a legal name and then rerun ":CMDNAME
|
||
|
end
|
||
|
* Lock the dictionary (it will remain locked for awhile)
|
||
|
LSLEEP = MAXSLEEP
|
||
|
ANAL1:
|
||
|
if LSLEEP <= 0 then
|
||
|
close DVAR
|
||
|
stop "Can't lock dictionary: ":DPATH
|
||
|
end
|
||
|
filelock DVAR locked
|
||
|
LSLEEP -= 1
|
||
|
sleep 2
|
||
|
goto ANAL1
|
||
|
end
|
||
|
|
||
|
* Verify that the file is not already an SQL table, and that its file Type
|
||
|
* is valid for being an SQL table
|
||
|
VERFVALID:
|
||
|
if FSTAT<29,1,1> <> 0 then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
stop "File '":FPATH:"' is already an SQL table"
|
||
|
end
|
||
|
FTYPE = FSTAT<21,1,1>
|
||
|
FMODSEP = ""
|
||
|
PMODSEP = "1,2"
|
||
|
begin case
|
||
|
case (FTYPE >= 2 and FTYPE <= 18)
|
||
|
FMODSEP = FSTAT<22,1,1>:" "
|
||
|
FMODSEP := FSTAT<23,1,1>
|
||
|
PMODSEP = FSTAT<22,1,1>:","
|
||
|
PMODSEP := FSTAT<23,1,1>
|
||
|
* case FTYPE = 25 ; * In rel 8.3.1 we don't support type 25 tables
|
||
|
case FTYPE = 30
|
||
|
case 1
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
stop "File '":FPATH:"' has invalid Type (":FTYPE:") for an SQL table"
|
||
|
end case
|
||
|
|
||
|
* Get file's F- or Q-record from VOC, save it (in RVAR)
|
||
|
GETFVOC:
|
||
|
* Open the VOC file to VOCVAR
|
||
|
openpath VPATH to VOCVAR else
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
stop "Can't run ":CMDNAME:": Can't open '":VPATH:"'"
|
||
|
end
|
||
|
LSLEEP = MAXSLEEP
|
||
|
GETFVOC1:
|
||
|
if LSLEEP <= 0 then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
close VOCVAR
|
||
|
stop "Can't run ":CMDNAME:": '":VPATH:"' is locked"
|
||
|
end
|
||
|
readl RVAR from VOCVAR,FILENM locked
|
||
|
LSLEEP -= 1
|
||
|
sleep 2
|
||
|
goto GETFVOC1
|
||
|
end else
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
close VOCVAR
|
||
|
stop "Can't run ":CMDNAME:": Can't read from '":VPATH:"'"
|
||
|
end
|
||
|
|
||
|
* Find out if <filename>_SQLDEF already exists (keep info in SVAR)
|
||
|
DEFNM = change(FILENM," ","_"):"_SQLDEF"
|
||
|
readl SVAR from VOCVAR,DEFNM else SVAR = ""
|
||
|
* Close the VOC file
|
||
|
close VOCVAR
|
||
|
|
||
|
* Verify that the file's VOC entry is either an F-record or a Q-record
|
||
|
VOCCODE = upcase(RVAR<1>[1,1])
|
||
|
if (VOCCODE # "F" and VOCCODE # "Q") then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
stop "VOC entry for '":FILENM:"' is neither an F-record nor a Q-record"
|
||
|
end
|
||
|
|
||
|
* Verify that the file (if it has an F-pointer) is not a multi-level file
|
||
|
if VOCCODE = "F" and upcase(RVAR<4>) matches 'M...' then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
stop "File '":FPATH:"' is a multi-level file (invalid for an SQL table)"
|
||
|
end
|
||
|
|
||
|
* Prepare to create and/or delete SQLDEF file
|
||
|
SQLDEF:
|
||
|
DELDEF = "DELETE.FILE ":DEFNM
|
||
|
if CREATE.SYNTAX = "PICK" then
|
||
|
CREDEF = "CREATE.FILE ":DEFNM:" 1 1,2,3"
|
||
|
end else
|
||
|
CREDEF = "CREATE.FILE ":DEFNM:" 3 1 2"
|
||
|
end
|
||
|
if SVAR = "" then goto GENDEF ; * goto GENDEF if SQLDEF file doesn't exist
|
||
|
* A SQLDEF file exists
|
||
|
gosub OPENDEF ; *open the SQLDEF file to NVAR, and its dict to DNVAR
|
||
|
if OPENERR then goto GENDEF ; * goto GENDEF if SQLDEF file doesn't exist
|
||
|
if NEWDEF then goto DELEX
|
||
|
if (TEST or CRTAB) then
|
||
|
print "Using existing file '":DEFNM:"'"
|
||
|
goto REPORT
|
||
|
end
|
||
|
print "File '":DEFNM:"' exists. Do you wish to overwrite? [N] ":
|
||
|
input TVAR
|
||
|
if not(upcase(TVAR) = "Y" or upcase(TVAR) = "YES") then goto REPORT
|
||
|
|
||
|
* Delete existing SQLDEF file
|
||
|
DELEX:
|
||
|
print "Generating file '":DEFNM:"' ..":
|
||
|
print ".":
|
||
|
clearfile NVAR
|
||
|
goto GENDEF1
|
||
|
|
||
|
* Generate new SQLDEF file
|
||
|
GENDEF:
|
||
|
print "Generating file '":DEFNM:"' ..":
|
||
|
execute CREDEF capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
print ".":
|
||
|
gosub OPENDEF ; *open the SQLDEF file to NVAR, and its dict to DNVAR
|
||
|
if OPENERR then goto GOODBYE
|
||
|
GENDEF1:
|
||
|
print ".":
|
||
|
* Create select list of original dictionary recid's into DICTLIST (used to
|
||
|
* check for duplicate dictionary entries when creating artificial columns -
|
||
|
* this is inefficient, but before changing it look at ALL references in code).
|
||
|
select DVAR
|
||
|
print ".":
|
||
|
readlist DICTLIST else
|
||
|
print ""
|
||
|
gosub UCAF
|
||
|
print "DICT '":FILENM:"' is empty. Can't convert file"
|
||
|
execute DELDEF capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
* Copy all records from DICT FILENM to the SQLDEF file (and determine largest
|
||
|
* field number for a stored field while doing so, to use when creating arrays)
|
||
|
select DICTLIST
|
||
|
print ".":
|
||
|
NOMORE = 0
|
||
|
loop
|
||
|
until NOMORE
|
||
|
readnext ATID then
|
||
|
read DICTREC from DVAR,ATID else goto RDERR
|
||
|
write DICTREC to NVAR,ATID else goto WRERR
|
||
|
CODETEMP = upcase(DICTREC<1>[1,2])
|
||
|
LOCTEMP = DICTREC<2>
|
||
|
CORRTEMP = DICTREC<8>
|
||
|
begin case
|
||
|
case (CODETEMP = "A" or CODETEMP = "A ") and num(LOCTEMP)
|
||
|
if CORRTEMP # "" then LOCTEMP = 0
|
||
|
case (CODETEMP = "S" or CODETEMP = "S ") and num(LOCTEMP)
|
||
|
if CORRTEMP # "" then LOCTEMP = 0
|
||
|
case CODETEMP[1,1] = "D"
|
||
|
case 1
|
||
|
LOCTEMP = 0
|
||
|
end case
|
||
|
if LOCTEMP > MAXC and LOCTEMP <= TOPMAX then
|
||
|
MAXC = LOCTEMP+0
|
||
|
MAXNEW = MAXC
|
||
|
end
|
||
|
end else NOMORE = 1
|
||
|
repeat
|
||
|
print ".":
|
||
|
|
||
|
* Put useful entries in SQLDEF's dictionary (F1-F6, @, and INFX)
|
||
|
LSLEEP = MAXSLEEP
|
||
|
WRDICT:
|
||
|
if LSLEEP <= 0 then
|
||
|
print "Can't read F1 from DICT ":DEFNM:" because it is locked"
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
fileunlock NVAR
|
||
|
close NVAR
|
||
|
fileunlock DNVAR
|
||
|
close DNVAR
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
readu TVAR from DNVAR,"F1" locked
|
||
|
LSLEEP -= 1
|
||
|
sleep 2
|
||
|
print ".":
|
||
|
goto WRDICT
|
||
|
end then
|
||
|
print "...":
|
||
|
goto MAKEDINFO
|
||
|
end else
|
||
|
for M = 1 to 6
|
||
|
FFF = "D":@FM:M:@FM:@FM:@FM:"2L":@FM:"S"
|
||
|
GGG = "F":M
|
||
|
write FFF to DNVAR,GGG else goto WRERR
|
||
|
next M
|
||
|
EEE = "D":@FM:2:@FM:@FM:@FM:"60T":@FM:"S"
|
||
|
write EEE to DNVAR,"F2" else goto WRERR
|
||
|
print ".":
|
||
|
HHH = "PH":@FM:"F1 F2 F3 F4 F5 F6" ; *??
|
||
|
write HHH to DNVAR,"@" else goto WRERR
|
||
|
print ".":
|
||
|
IDESC = "IF (@ID MATCH 'SQL_A...' OR @ID MATCH 'SQL_C...'"
|
||
|
IDESC := " OR @ID MATCH 'SQL_K...')"
|
||
|
IDESC := " THEN F2 ELSE F2:' ':F3:' ':F4:' ':F5:' ':F6"
|
||
|
TVAR = "I":@FM:IDESC:@FM:@FM:"SQL Table Information":@FM:"62T":@FM:"S"
|
||
|
write TVAR to DNVAR,"INFX" else goto WRERR
|
||
|
print ".":
|
||
|
CDICT = "CD ":DEFNM:" INFX"
|
||
|
execute CDICT capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
end
|
||
|
* Create WARNINGS, KEYNAMES, ATNAMES, and SELNAMES arrays
|
||
|
MAKEDINFO:
|
||
|
VOCOPEN = 0 ; * This flag is used in subroutine EXPANDPH to avoid
|
||
|
* repeatedly opening the VOC file once it has been opened
|
||
|
dim WARNINGS (MAXWARN)
|
||
|
mat WARNINGS = ""
|
||
|
WARNX = 1
|
||
|
* Create KEYNAMES array
|
||
|
TRYKEY:
|
||
|
NEEDATKEY = 0
|
||
|
KEYLAST = 0
|
||
|
SEPCHAR = ""
|
||
|
read DICTREC from NVAR,"@KEY" then
|
||
|
if upcase(DICTREC<1>[1,2]) # "PH" then goto TRYAT
|
||
|
end else
|
||
|
goto TRYAT
|
||
|
end
|
||
|
DICTTEMP = trim(DICTREC<2>)
|
||
|
PHNAME = "@KEY" ; gosub EXPANDPH
|
||
|
dim KEYNAMES (TOKCNT+1,2)
|
||
|
mat KEYNAMES = ""
|
||
|
MSGTEMP = ""
|
||
|
loop
|
||
|
while DICTTEMP # "" do
|
||
|
TOKTEMP = field(DICTTEMP," ",1)
|
||
|
DICTTEMP = field(DICTTEMP," ",2,999)
|
||
|
* Check for duplicated tokens in @KEY; only put one of them in KEYNAMES()
|
||
|
if KEYLAST > 0 then
|
||
|
for I = 1 to KEYLAST
|
||
|
until KEYNAMES(I) = TOKTEMP
|
||
|
next I
|
||
|
if I <= KEYLAST then
|
||
|
WARNINGS(WARNX) = "Duplicate token '":TOKTEMP
|
||
|
WARNINGS(WARNX) := "' in @KEY phrase is ignored"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
continue
|
||
|
end
|
||
|
end
|
||
|
read RECTEMP from NVAR,TOKTEMP then
|
||
|
CODETEMP = upcase(RECTEMP<1>[1,2])
|
||
|
LOCTEMP = RECTEMP<2>
|
||
|
CORRTEMP = RECTEMP<8>
|
||
|
KEYLAST += 1
|
||
|
KEYNAMES(KEYLAST,1) = TOKTEMP
|
||
|
begin case
|
||
|
case CODETEMP[1,1] = "I"
|
||
|
KEYNAMES(KEYLAST,2) = "I"
|
||
|
case (CODETEMP = "A" or CODETEMP = "A ") and num(LOCTEMP) and LOCTEMP # "" and CORRTEMP # ""
|
||
|
KEYNAMES(KEYLAST,2) = "A"
|
||
|
case (CODETEMP = "S" or CODETEMP = "S ") and num(LOCTEMP) and LOCTEMP # "" and CORRTEMP # ""
|
||
|
KEYNAMES(KEYLAST,2) = "S"
|
||
|
case 1
|
||
|
KEYLAST -= 1
|
||
|
if MSGTEMP # "" then
|
||
|
WARNINGS(WARNX) = "@KEY token '":MSGTEMP:"' is ignored:"
|
||
|
WARNINGS(WARNX) := ' must be I-type or Correlative'
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
end
|
||
|
MSGTEMP = TOKTEMP
|
||
|
end case
|
||
|
end else
|
||
|
WARNINGS(WARNX) = "@KEY token '":TOKTEMP:"' is ignored:"
|
||
|
WARNINGS(WARNX) := ' not in dictionary'
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
end
|
||
|
repeat
|
||
|
if TOKCNT > 1 and MSGTEMP # "" then
|
||
|
WARNINGS(WARNX) = "@KEY token '":MSGTEMP:"' is ignored:"
|
||
|
WARNINGS(WARNX) := ' must be I-type or Correlative'
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
end
|
||
|
* Put key-separator character (if any) in SEPCHAR
|
||
|
read DICTREC from NVAR,"@KEY_SEPARATOR" then
|
||
|
if upcase(DICTREC<1>[1,1]) = "X" then SEPCHAR = DICTREC<2>[1,1]
|
||
|
end
|
||
|
* Create ATNAMES array
|
||
|
TRYAT:
|
||
|
ATLAST = 0
|
||
|
SELLAST = 0
|
||
|
NEWATSEL = 0
|
||
|
USESELECT = 0
|
||
|
read DICTREC from NVAR,"@" else goto TRYSEL
|
||
|
if upcase(DICTREC<1>[1,2]) = "PH" then
|
||
|
DICTTEMP = DICTREC<2>
|
||
|
PHNAME = "@" ; gosub EXPANDPH
|
||
|
dim SELNAMES (TOKCNT+1,2)
|
||
|
mat SELNAMES = ""
|
||
|
gosub MKSELAT
|
||
|
dim ATNAMES (TOKCNT+1,2)
|
||
|
mat ATNAMES = mat SELNAMES
|
||
|
ATLAST = SELLAST
|
||
|
end
|
||
|
* Create SELNAMES array
|
||
|
TRYSEL:
|
||
|
read DICTREC from NVAR,"@SELECT" else goto GOTNOSEL
|
||
|
if upcase(DICTREC<1>[1,2]) = "PH" then
|
||
|
DICTTEMP = DICTREC<2>
|
||
|
PHNAME = "@SELECT" ; gosub EXPANDPH
|
||
|
dim SELNAMES (TOKCNT+1,2)
|
||
|
mat SELNAMES = ""
|
||
|
gosub MKSELAT
|
||
|
if SELLAST > 0 then goto BUILDSELD
|
||
|
end
|
||
|
GOTNOSEL:
|
||
|
if ATLAST > 0 then
|
||
|
NEWATSEL = 1
|
||
|
SELLAST = 0
|
||
|
dim SELNAMES (TOKCNT+2,2)
|
||
|
mat SELNAMES = ""
|
||
|
if IDSUP = 0 and KEYLAST <= 1 then DICTTEMP = "@ID" ; gosub MKSELAT
|
||
|
* At this point SELLAST is either 0 or 1
|
||
|
for I = 1 to ATLAST
|
||
|
SELNAMES(I+SELLAST,1) = ATNAMES(I,1)
|
||
|
SELNAMES(I+SELLAST,2) = ATNAMES(I,2)
|
||
|
next I
|
||
|
SELLAST += I-1
|
||
|
end
|
||
|
goto BUILDSELD
|
||
|
|
||
|
* This subroutine fills array SELNAMES (for eventual use either as ATNAMES
|
||
|
* or as SELNAMES) with an ordered list of tokens which represent D, I, A,
|
||
|
* or S dictionary entries (or the speciial token @ASSOC_ROW)
|
||
|
* Input: SELNAMES must be dim'd and mat'd
|
||
|
* DICTTEMP contains space-separated list of tokens in proper order
|
||
|
* Output: SELNAMES is now loaded
|
||
|
* SELLAST contains the number of rows in SELNAMES
|
||
|
MKSELAT:
|
||
|
SELLAST = 0
|
||
|
loop
|
||
|
while DICTTEMP # "" do
|
||
|
TOKTEMP = field(DICTTEMP," ",1)
|
||
|
DICTTEMP = field(DICTTEMP," ",2,999)
|
||
|
read RECTEMP from NVAR,TOKTEMP then
|
||
|
CODETEMP = upcase(RECTEMP<1>[1,2])
|
||
|
LOCTEMP = RECTEMP<2>
|
||
|
SELLAST += 1
|
||
|
SELNAMES(SELLAST,1) = TOKTEMP
|
||
|
begin case
|
||
|
case (CODETEMP = "A" or CODETEMP = "A ") and num(LOCTEMP) and LOCTEMP # ""
|
||
|
SELNAMES(SELLAST,2) = "A"
|
||
|
case (CODETEMP = "S" or CODETEMP = "S ") and num(LOCTEMP) and LOCTEMP # ""
|
||
|
SELNAMES(SELLAST,2) = "S"
|
||
|
case (CODETEMP[1,1] = "D" or CODETEMP[1,1] = "I")
|
||
|
SELNAMES(SELLAST,2) = CODETEMP[1,1]
|
||
|
case 1
|
||
|
SELLAST -= 1
|
||
|
end case
|
||
|
end else
|
||
|
if TOKTEMP = "@ASSOC_ROW" then
|
||
|
SELLAST += 1
|
||
|
SELNAMES(SELLAST,1) = TOKTEMP
|
||
|
SELNAMES(SELLAST,2) = "Y"
|
||
|
end
|
||
|
end
|
||
|
repeat
|
||
|
return ; *from MKSELAT
|
||
|
|
||
|
* Build select list of all valid D, A, and S records in dictionary
|
||
|
* (note that A or S is invalid if 2nd character of F1 is non-blank)
|
||
|
BUILDSELD:
|
||
|
print ".":
|
||
|
SELD = 'SELECT ':DEFNM:' WITH F1 LIKE "D..." '
|
||
|
SELD := 'OR F1 LIKE "A ..." OR F1 = "A" '
|
||
|
SELD := 'OR F1 LIKE "S ..." OR F1 = "S"'
|
||
|
execute SELD capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
print ".."
|
||
|
|
||
|
* Define array COLUMNS of field names
|
||
|
MAKEARRAY:
|
||
|
dim COLUMNS (MAXC+1,52) ;* First element is the code (D, A, or S) followed
|
||
|
* by a multi-digit preference code (CODEX)
|
||
|
* 2nd element is number of definitions of this field
|
||
|
* 3rd element is field-name of this field
|
||
|
* 4th, 5th,..elements are synonym-names
|
||
|
mat COLUMNS = 0
|
||
|
COLLAST = 0 ; * Field number of last column to be created
|
||
|
COLCNT = 0 ; * Number of columns to be created (not counting field 0)
|
||
|
COLTOT = 0 ; * Number of fields defined (incl. AMC > MAXC, not incl. 0)
|
||
|
|
||
|
* Define array ASSOCS of associations
|
||
|
dim ASSOCS (50,MAXC+3) ;* First element is association-name
|
||
|
* Elements 2 - MAXC+1 show which columns (1-MAXC) are
|
||
|
* in this association; K means it is a key column
|
||
|
* and X means that it is a non-key column
|
||
|
* If element MAXC+2 is "X" then this assoc overlaps
|
||
|
* a lower-numbered assoc definition
|
||
|
* Element MAXC+3 contains information from this
|
||
|
* association's @ASSOC_KEY X-record (if any):
|
||
|
* S = Stable key
|
||
|
* U = Unstable key
|
||
|
* K = Key'd association
|
||
|
* N = This association has no @ASSOC_KEY record
|
||
|
mat ASSOCS = 0
|
||
|
ASSOCLAST = 0 ; * Number of the last association
|
||
|
|
||
|
* Define array PHRASES for PHrases whose names are also in ASSOC field of dict
|
||
|
dim PHRASES (50,MAXC+3) ; *First element is phrase-name (= assoc-name)
|
||
|
* Elements 2 - MAXC+1 show which fields (1-MAXC) are in
|
||
|
* this phrase; "1" means it is the first token in
|
||
|
* the PHrase, "2" means it is the second token in
|
||
|
* the PHrase, etc.
|
||
|
* If element MAXC+2 is "X" then the PHrase includes a
|
||
|
* token that is not a data field name in this file
|
||
|
* (it might be an I-descriptor)
|
||
|
* Element MAXC+3 is the number of data-field tokens
|
||
|
* in the PHrase
|
||
|
mat PHRASES = 0
|
||
|
|
||
|
* Define array NEWDICT of new (valid SQL) dictionary names to be created
|
||
|
dim NEWDICT (MAXNEW) ; * Each element is a new name
|
||
|
mat NEWDICT = 0
|
||
|
NEWLAST = 0 ; * Number of the last new dictionary name at any time
|
||
|
|
||
|
* Initialize ATSELX, LEGALX, etc, which are "importance weightings" used in
|
||
|
* deciding which dictionary definition (when there are several for the same
|
||
|
* field) should be chosen as the basis for the column definition.
|
||
|
* Each importance weighting must be different (9 = highest, 1 = lowest).
|
||
|
***** NOTE: these importance weightings must all be different ***************
|
||
|
ATSELX = 9 ; * ATSELCODEX = 1 if fieldname is in user-supplied @SELECT phrase
|
||
|
LEGALX = 8 ; * LEGALCODEX = 1 if fieldname is a legal identifier on this O/S
|
||
|
MVAX = 7 ; * MVACODEX tells whether this field is in an association, etc:
|
||
|
* MVACODEX = 0 if MULTI and AMC = 0; otherwise:
|
||
|
* if dict entry has legal token in ASSOC field (or Pick equiv),
|
||
|
* then = 9 if MULTI, in ASSOC phrase, is KEY in @ASSOC_KEY.assoc
|
||
|
* = 8 if MULTI and in ASSOC phrase
|
||
|
* = 4 if MULTI and is KEY in @ASSOC_KEY.assoc
|
||
|
* = 3 if MULTI
|
||
|
* = 1 if SINGLE (in which case ASSOC field is ignored)
|
||
|
* if dict entry doesn't have legal ASSOC token (or Pick equiv),
|
||
|
* then = 7 if MULTI and exists @ASSOC_KEY.namv for this field
|
||
|
* = 6 if MULTI
|
||
|
* = 5 if SINGLE
|
||
|
TYPEX = 6 ; * TYPECODEX = 4 if dict datatype is DATE/TIME and CONV agrees
|
||
|
* = 4 if dict datatype is DEC/NUMERIC and CONV agrees
|
||
|
* = 3 if datatype is DATE/TIME/DEC/NUM and CONV = ""
|
||
|
* = 2 if valid dict datatype (not DATE/TIME/DEC/NUM)
|
||
|
* = 1 if dict datatype is DATE/TIME and CONV disagrees
|
||
|
* = 1 if datatype is DEC/NUMERIC and CONV disagrees
|
||
|
* = 0 if dict doesn't contain a valid SQL datatype
|
||
|
ATKEYX = 5 ; * ATKEYCODEX = 1 if fieldname is in multi-token @KEY phrase
|
||
|
ATX = 4 ; * ATCODEX = 1 if fieldname is in @ phrase
|
||
|
NUMX = 3 ; * NUMCODEX = 1 if fieldname is NOT a valid SQL numeric literal
|
||
|
ATIDX = 2 ; * ATIDCODEX = 1 if fieldname is NOT "@ID"
|
||
|
DASX = 1 ; * DASCODEX = 3 for D-type, 2 for A-type, 1 for S-type
|
||
|
* Initialize CODEX, used to rate different possible column-definitions.
|
||
|
**** NOTE: CODEX must contain at least as many 0's as the largest "importance
|
||
|
* weighting" number above (currently 5/20/97 this is 9 = ATSELX)
|
||
|
CODEX = "000000000"
|
||
|
|
||
|
* Build the COLUMNS array
|
||
|
MAKE1:
|
||
|
readnext ATID else goto MAKEEND
|
||
|
if ATID[1,4] = '@Ak.' then goto MAKE1
|
||
|
read TVAR from NVAR,ATID else goto RDERR
|
||
|
CODE = upcase(TVAR<1>[1,1])
|
||
|
*Reject A or S item with Correlative, since this is equivalent to I-type
|
||
|
*Also reject A or S with empty LOC
|
||
|
if (CODE = 'A' or CODE = 'S') then
|
||
|
if len(trim(TVAR<8>)) > 0 then goto MAKE1
|
||
|
if len(trim(TVAR<2>)) = 0 then goto MAKE1
|
||
|
end
|
||
|
LOC = trim(TVAR<2>)
|
||
|
if (LOC = '' or LOC = 0) then LOC = '0'
|
||
|
if len(LOC) > 1 then LOC = trim(LOC,"0","L")
|
||
|
if not(num(LOC)) then
|
||
|
WARNINGS(WARNX) = 'Dict entry "':ATID:'" is ignored:'
|
||
|
WARNINGS(WARNX) := " non-numeric LOC field '":LOC:"'"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
goto MAKE1
|
||
|
end
|
||
|
if LOC > MAXC then
|
||
|
WARNINGS(WARNX) = 'Dict entry "':ATID:'" is ignored:'
|
||
|
WARNINGS(WARNX) := ' its LOC field (':LOC:') is too large'
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
COLTOT += 1
|
||
|
goto MAKE1
|
||
|
end
|
||
|
AMC = LOC
|
||
|
if LOC = 0 then LOC = MAXC+1 else
|
||
|
if COLUMNS(LOC,2) = 0 then
|
||
|
COLCNT += 1
|
||
|
COLTOT += 1
|
||
|
end
|
||
|
if LOC > COLLAST then COLLAST = LOC
|
||
|
end
|
||
|
if COLUMNS(LOC,2) = 50 then
|
||
|
if AMC = 0 then LOC = '0'
|
||
|
if len(LOC) > 1 then LOC = trim(LOC,"0","L")
|
||
|
WARNINGS(WARNX) = 'Dict entry "':ATID:'" is ignored:'
|
||
|
WARNINGS(WARNX) := ' >50 definitions for location ':LOC
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
goto MAKE1
|
||
|
end
|
||
|
COLUMNS(LOC,2) += 1
|
||
|
if CODE = "D" then
|
||
|
SQLTYPE = TVAR<8>
|
||
|
CONV = TVAR<3>
|
||
|
ASSOCID = TVAR<7>
|
||
|
MULTI = upcase(TVAR<6>[1,1])
|
||
|
end else
|
||
|
SQLTYPE = TVAR<6>
|
||
|
CONV = TVAR<7>
|
||
|
PICKA = TVAR<4>
|
||
|
if len(PICKA) > 2 and (PICKA[1,2] = "C;" or PICKA[1,2] = "D;") then
|
||
|
ASSOCID = "@DC":LOC
|
||
|
if AMC <> 0 then MULTI = "M" else MULTI = "S"
|
||
|
end else
|
||
|
ASSOCID = ""
|
||
|
if TVAR<5> = "M" and AMC <> 0 then MULTI = "M" else MULTI = "S"
|
||
|
end
|
||
|
end
|
||
|
if len(SQLTYPE) = 0 then TYPECODEX = "0" else
|
||
|
SQLX = SQLTYPE
|
||
|
gosub CONVST
|
||
|
if CSTERR then
|
||
|
if seq(SQLX[1,1]) <= 31 and seq(SQLX[1,1]) >= 0 then
|
||
|
SQLX = "(hex 00-1F)"
|
||
|
end else
|
||
|
SQLX = "'":SQLX:"'"
|
||
|
end
|
||
|
WARNINGS(WARNX) = 'In dict entry "':ATID:'",'
|
||
|
WARNINGS(WARNX) := " illegal DATATYPE ":SQLX:" is ignored"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
TYPECODEX = "0"
|
||
|
end else
|
||
|
* See if CONV is consistent with DATATYPE in dictionary
|
||
|
begin case
|
||
|
case SQLTYPE = "DATE"
|
||
|
begin case
|
||
|
case CONV[1,1] = "D"
|
||
|
TYPECODEX = "4"
|
||
|
case CONV = ""
|
||
|
TYPECODEX = "3"
|
||
|
case 1
|
||
|
WARNINGS(WARNX) = 'In field "':ATID:'",'
|
||
|
WARNINGS(WARNX) := " CONV '":CONV:"' disagrees"
|
||
|
WARNINGS(WARNX) := " with DATATYPE '":SQLX:"'"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
TYPECODEX = "1"
|
||
|
end case
|
||
|
case SQLTYPE = "TIME"
|
||
|
begin case
|
||
|
case CONV[1,2] = "MT"
|
||
|
TYPECODEX = "4"
|
||
|
case CONV = ""
|
||
|
TYPECODEX = "3"
|
||
|
case 1
|
||
|
WARNINGS(WARNX) = 'In field "':ATID:'",'
|
||
|
WARNINGS(WARNX) := " CONV '":CONV:"' disagrees"
|
||
|
WARNINGS(WARNX) := " with DATATYPE '":SQLX:"'"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
TYPECODEX = "1"
|
||
|
end case
|
||
|
case SQLTYPE[1,3] = "DEC" or SQLTYPE[1,7] = "NUMERIC"
|
||
|
SCALET = "0"
|
||
|
if count(SQLTYPE,',') = 1 then SCALET = SQLTYPE[2][1,1]
|
||
|
begin case
|
||
|
case CONV[1,2] = "MD" or CONV[1,2] = "ML" or CONV[1,2] = "MR"
|
||
|
gosub GETSCALE
|
||
|
if SCALEX = SCALET then
|
||
|
TYPECODEX = "4"
|
||
|
end else
|
||
|
WARNINGS(WARNX) = 'In field "':ATID:'",'
|
||
|
WARNINGS(WARNX) := " CONV '":CONV:"' disagrees"
|
||
|
WARNINGS(WARNX) := " with DATATYPE '":SQLX:"'"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
TYPECODEX = "1"
|
||
|
end
|
||
|
case CONV = ""
|
||
|
TYPECODEX = "3"
|
||
|
case 1
|
||
|
WARNINGS(WARNX) = 'In field "':ATID:'",'
|
||
|
WARNINGS(WARNX) := " CONV '":CONV:"' disagrees"
|
||
|
WARNINGS(WARNX) := " with DATATYPE '":SQLX:"'"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
TYPECODEX = "1"
|
||
|
end case
|
||
|
case 1
|
||
|
TYPECODEX = "2"
|
||
|
end case
|
||
|
end
|
||
|
end
|
||
|
if ASSOCID # "" then
|
||
|
IDENT = ASSOCID ; IDTYPE = "ASSOC" ; gosub CKLEGAL
|
||
|
if ILLEGAL then
|
||
|
WARNINGS(WARNX) = 'ASSOC field of dict entry "':ATID:'" is'
|
||
|
WARNINGS(WARNX) := ' ignored: ':ILLEGAL
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
goto IGNOREF7
|
||
|
end else
|
||
|
if MULTI = "M" then
|
||
|
gosub CKASSOCPH ; * find out if this ASSOCID has an assoc phrase
|
||
|
* and if so whether ATID is in it
|
||
|
gosub CKASSOCKEY1 ; * find out if this ASSOCID has an @ASSOC_KEY
|
||
|
* X-record and if so whether ATID is in it
|
||
|
if INASSOCKEY then
|
||
|
if INASSOCPH then MVACODEX = "9" else MVACODEX = "4"
|
||
|
end else
|
||
|
if INASSOCPH then MVACODEX = "8" else MVACODEX = "3"
|
||
|
end
|
||
|
end else
|
||
|
MVACODEX = "1"
|
||
|
end
|
||
|
end
|
||
|
end else
|
||
|
IGNOREF7:
|
||
|
if MULTI = "M" then
|
||
|
gosub CKASSOCKEY2 ; * find out if this ATID has an @ASSOC_KEY record
|
||
|
if ISASSOCKEY then
|
||
|
MVACODEX = "7"
|
||
|
end else
|
||
|
MVACODEX = "6"
|
||
|
end
|
||
|
end else
|
||
|
MVACODEX = "5"
|
||
|
end
|
||
|
end
|
||
|
if AMC = 0 and MULTI = "M" then
|
||
|
MVACODEX = "0"
|
||
|
end
|
||
|
goto SELKEYCODES
|
||
|
CKASSOCPH:
|
||
|
INASSOCPH = 0
|
||
|
read APH from NVAR,ASSOCID then
|
||
|
if upcase(APH<1>[1,2]) = "PH" then
|
||
|
DICTTEMP = trim(APH<2>)
|
||
|
PHNAME = ASSOCID ; gosub EXPANDPH
|
||
|
if VOCOPEN then close VOCVAR ; VOCOPEN = 0
|
||
|
loop ; until DICTTEMP = "" do
|
||
|
if ATID = field(DICTTEMP," ",1,1) then INASSOCPH = 1
|
||
|
until INASSOCPH
|
||
|
DICTTEMP = field(DICTTEMP," ",2,999)
|
||
|
repeat
|
||
|
end
|
||
|
end
|
||
|
return ; *from CKASSOCPH
|
||
|
CKASSOCKEY1:
|
||
|
INASSOCKEY = 0
|
||
|
read KEYX from NVAR,"@ASSOC_KEY.":ASSOCID then
|
||
|
if upcase(KEYX<1>[1,1]) = "X" then
|
||
|
KEYX2 = trim(KEYX<2>)
|
||
|
if field(KEYX2," ",1,1) = "KEY" then
|
||
|
KEYX2 = field(KEYX2," ",2,99)
|
||
|
loop ; until KEYX2 = "" do
|
||
|
if ATID = field(KEYX2," ",1,1) then INASSOCKEY = 1
|
||
|
until INASSOCKEY
|
||
|
KEYX2 = field(KEYX2," ",2,99)
|
||
|
repeat
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return ; *from CKASSOCKEY1
|
||
|
CKASSOCKEY2:
|
||
|
ISASSOCKEY = 0
|
||
|
read KEYX from NVAR,"@ASSOC_KEY.":ATID then
|
||
|
if upcase(KEYX<1>[1,1]) = "X" then ISASSOCKEY = 1
|
||
|
end
|
||
|
return ; *from CKASSOCKEY2
|
||
|
SELKEYCODES:
|
||
|
* See if column name is in user-specified @SELECT phrase
|
||
|
gosub CKSEL
|
||
|
if INSEL then ATSELCODEX = "1" else ATSELCODEX = "0"
|
||
|
* See if column name is in multi-token @KEY phrase
|
||
|
gosub CKKEY
|
||
|
if INKEY then ATKEYCODEX = "1" else ATKEYCODEX = "0"
|
||
|
* See if column name is in @ phrase
|
||
|
gosub CKAT
|
||
|
if INAT then ATCODEX = "1" else ATCODEX = "0"
|
||
|
* See if column name is a legal SQL identifier
|
||
|
IDENT = ATID ; IDTYPE = "COLUMN" ; gosub CKLEGAL
|
||
|
if ILLEGAL then
|
||
|
WARNINGS(WARNX) = 'Field name "':ATID:'" is illegal'
|
||
|
WARNINGS(WARNX) := ' as a column name: ':ILLEGAL
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
LEGALCODEX = "0"
|
||
|
end else
|
||
|
LEGALCODEX = "1"
|
||
|
end
|
||
|
* See if column name is "@ID"
|
||
|
if ATID = "@ID" then ATIDCODEX = "0" else ATIDCODEX = "1"
|
||
|
begin case
|
||
|
case CODE = 'D'
|
||
|
DASCODEX = "3"
|
||
|
case CODE = 'A'
|
||
|
DASCODEX = "2"
|
||
|
case CODE = 'S'
|
||
|
DASCODEX = "1"
|
||
|
case 1
|
||
|
DASCODEX = "0"
|
||
|
end case
|
||
|
* See if the column name is a valid SQL numeric literal
|
||
|
if num(ATID) then NUMCODEX = "0" else NUMCODEX = "1"
|
||
|
|
||
|
* Assemble the column-definition preference code (CODEX)
|
||
|
CODEX[10-NUMX,1] = NUMCODEX
|
||
|
CODEX[10-TYPEX,1] = TYPECODEX
|
||
|
CODEX[10-MVAX,1] = MVACODEX
|
||
|
CODEX[10-ATSELX,1] = ATSELCODEX
|
||
|
CODEX[10-ATKEYX,1] = ATKEYCODEX
|
||
|
CODEX[10-LEGALX,1] = LEGALCODEX
|
||
|
CODEX[10-ATIDX,1] = ATIDCODEX
|
||
|
CODEX[10-ATX,1] = ATCODEX
|
||
|
CODEX[10-DASX,1] = DASCODEX
|
||
|
* Make appropriate adjustments to current entry in COLUMNS() ARRAY
|
||
|
I = COLUMNS(LOC,2)+2
|
||
|
if CODEX <= COLUMNS(LOC,1)[2,9] then
|
||
|
* The current column-definition is less "preferred" than previous one
|
||
|
COLUMNS(LOC,I) = ATID
|
||
|
end else
|
||
|
* The current column-definition is more "preferred" than previous one
|
||
|
if COLUMNS(LOC,2) > 1 then COLUMNS(LOC,I) = COLUMNS(LOC,3)
|
||
|
COLUMNS(LOC,1) = CODE:CODEX
|
||
|
COLUMNS(LOC,3) = ATID
|
||
|
end
|
||
|
goto MAKE1
|
||
|
|
||
|
MAKEEND:
|
||
|
******* debug messages *******************
|
||
|
if DEBUG then
|
||
|
print "COLUMNS() 0 through 5:"
|
||
|
print " ":
|
||
|
for N = 1 to 9
|
||
|
print COLUMNS(MAXC+1,N):", ":
|
||
|
next N
|
||
|
print COLUMNS(MAXC+1,10)
|
||
|
for M = 1 to 5
|
||
|
print " ":
|
||
|
for N = 1 to 9
|
||
|
print COLUMNS(M,N):", ":
|
||
|
next N
|
||
|
print COLUMNS(M,10)
|
||
|
next M
|
||
|
print "SELNAMES() 1 through ":SELLAST:":"
|
||
|
for M = 1 to SELLAST
|
||
|
print " ":
|
||
|
print SELNAMES(M,1):", '":SELNAMES(M,2):"'"
|
||
|
next M
|
||
|
print "KEYNAMES() 1 through ":KEYLAST:":"
|
||
|
for M = 1 to KEYLAST
|
||
|
print " ":
|
||
|
print KEYNAMES(M,1):", '":KEYNAMES(M,2):"'"
|
||
|
next M
|
||
|
end
|
||
|
******* debug messages *******************
|
||
|
|
||
|
* Create primary key definition
|
||
|
MAKEPK:
|
||
|
if KEYLAST > 1 then
|
||
|
for I = 1 to KEYLAST
|
||
|
LOC = (-1) * I
|
||
|
ATID = KEYNAMES(I,1)
|
||
|
if KEYNAMES(I,2) = "I" then CODE = "D" else CODE = KEYNAMES(I,2)
|
||
|
read ATREC from NVAR,ATID else BERX = 6 ; goto BADERR
|
||
|
ALPHID = "A"
|
||
|
gosub CREATECOL
|
||
|
next I
|
||
|
goto MAKEOTHER
|
||
|
end
|
||
|
if COLUMNS(MAXC+1,2) <= 0 then
|
||
|
gosub UCAF
|
||
|
print "No record-id defined in DICT '":FILENM:"'. Can't convert file"
|
||
|
execute DELDEF capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
LOC = 0
|
||
|
CODE = COLUMNS(MAXC+1,1)[1,1]
|
||
|
ATID = COLUMNS(MAXC+1,3)
|
||
|
read ATREC from NVAR,ATID else goto RDERR ; *??what should I do?
|
||
|
gosub TRIMAT
|
||
|
if upcase(ATREC<1>[1,1]) <> CODE then BERX = 7 ; goto BADERR
|
||
|
if ATREC<2> <> LOC then BERX = 8 ; goto BADERR
|
||
|
ALPHID = "A"
|
||
|
gosub CREATECOL
|
||
|
gosub WRCOL
|
||
|
|
||
|
* Create primary key synonyms
|
||
|
MAKEPKSYN:
|
||
|
if COLUMNS(MAXC+1,2) = 1 then goto MAKEOTHER
|
||
|
for I = 1 to COLUMNS(MAXC+1,2)-1
|
||
|
LOC = 0
|
||
|
ATID = COLUMNS(MAXC+1,3+I)
|
||
|
read ATREC from NVAR,ATID else goto RDERR ; *??what should I do?
|
||
|
gosub TRIMAT
|
||
|
if ATREC<2> <> LOC then BERX = 9 ; goto BADERR
|
||
|
BASEID = COLUMNS(MAXC+1,3)
|
||
|
CODE = upcase(ATREC<1>[1,1])
|
||
|
if ALPHID = "Z" then ALPHID = "a" else
|
||
|
ALPHID = char(seq(ALPHID)+1)
|
||
|
end
|
||
|
gosub CREATECOL
|
||
|
next I
|
||
|
|
||
|
* Create remaining column definitions and synonyms
|
||
|
MAKEOTHER:
|
||
|
for K = 1 to COLLAST
|
||
|
LOC = K
|
||
|
if COLUMNS(LOC,2) <= 0 then gosub CREATENONE ; continue
|
||
|
CODE = COLUMNS(LOC,1)[1,1]
|
||
|
ATID = COLUMNS(LOC,3)
|
||
|
read ATREC from NVAR,ATID else goto RDERR ; *??what should I do?
|
||
|
gosub TRIMAT
|
||
|
if upcase(ATREC<1>[1,1]) <> CODE then BERX = 1 ; goto BADERR
|
||
|
if ATREC<2> <> LOC then BERX = 2 ; goto BADERR
|
||
|
ALPHID = "A"
|
||
|
gosub CREATECOL
|
||
|
gosub WRCOL
|
||
|
if COLUMNS(LOC,2) = 1 then continue
|
||
|
for J = 1 to COLUMNS(LOC,2)-1
|
||
|
ATID = COLUMNS(LOC,3+J)
|
||
|
read ATREC from NVAR,ATID else goto RDERR ; *??what should I do?
|
||
|
gosub TRIMAT
|
||
|
if ATREC<2> <> LOC then BERX = 3 ; goto BADERR
|
||
|
BASEID = COLUMNS(LOC,3)
|
||
|
CODE = ATREC<1>[1,1]
|
||
|
if ALPHID = "Z" then ALPHID = "a" else
|
||
|
ALPHID = char(seq(ALPHID)+1)
|
||
|
end
|
||
|
gosub CREATECOL
|
||
|
next J
|
||
|
next K
|
||
|
|
||
|
if ASSOCLAST = 0 then goto WRINFO
|
||
|
******* debug messages *******************
|
||
|
if DEBUG then
|
||
|
print "ASSOCS() 1 through 5; elements 1-9 and ":MAXC+2:":"
|
||
|
for M = 1 to 5
|
||
|
print " ":
|
||
|
for N = 1 to 9
|
||
|
print ASSOCS(M,N):", ":
|
||
|
next N
|
||
|
print "..., ":ASSOCS(M,MAXC+2)
|
||
|
next M
|
||
|
print "PHRASES() 1 through 5; elements 1-9, ":MAXC+2:", ":MAXC+3:":"
|
||
|
for M = 1 to 5
|
||
|
print " ":
|
||
|
for N = 1 to 9
|
||
|
print PHRASES(M,N):", ":
|
||
|
next N
|
||
|
print "..., ":PHRASES(M,MAXC+2):", ":PHRASES(M,MAXC+3)
|
||
|
next M
|
||
|
end
|
||
|
******* debug messages *******************
|
||
|
|
||
|
* Create associations
|
||
|
MAKEASSOCS:
|
||
|
if ASSOCLAST = 0 then goto WRINFO
|
||
|
for K = 1 to ASSOCLAST
|
||
|
ALPHID = char(seq("A")-1)
|
||
|
AIX = K
|
||
|
ASSOCID = ASSOCS(K,1)
|
||
|
gosub CREATEASSOC
|
||
|
next K
|
||
|
******* debug messages *******************
|
||
|
if DEBUG then
|
||
|
print "ASSOCS() 1 through 5; elements 1-9 and ":MAXC+2:":"
|
||
|
for M = 1 to 5
|
||
|
print " ":
|
||
|
for N = 1 to 9
|
||
|
print ASSOCS(M,N):", ":
|
||
|
next N
|
||
|
print "..., ":ASSOCS(M,MAXC+2)
|
||
|
next M
|
||
|
end
|
||
|
******* debug messages *******************
|
||
|
|
||
|
WRINFO:
|
||
|
* Create and write the SQL_SELECT record in SQLDEF file
|
||
|
TVAR = "X":@FM
|
||
|
if SELLAST > 0 then
|
||
|
* Create SQL_SELECT record if there was an @SELECT or @ phrase in dict
|
||
|
if NEWATSEL then USESELECT = 1
|
||
|
for I = 1 to SELLAST
|
||
|
AMCAGAIN = 0
|
||
|
* First see if this @SELECT token is in @KEY
|
||
|
if KEYLAST > 1 then
|
||
|
for J = 1 to KEYLAST
|
||
|
until KEYNAMES(J,1) = SELNAMES(I)
|
||
|
next J
|
||
|
if J <= KEYLAST then
|
||
|
if KEYNAMES(J,2) = "FOUND" then
|
||
|
* This can only happen if @SELECT contains the same
|
||
|
* keypart name more than once
|
||
|
USESELECT = 1
|
||
|
AMCAGAIN = 1
|
||
|
end else
|
||
|
KEYNAMES(J,2) = "FOUND"
|
||
|
end
|
||
|
end
|
||
|
L = COLLAST
|
||
|
end else
|
||
|
L = MAXC+1
|
||
|
end
|
||
|
* Now see if this @SELECT token is a stored-column definition
|
||
|
loop
|
||
|
until L = 0
|
||
|
KNUM = COLUMNS(L,2)
|
||
|
for J = 3 to 2+KNUM
|
||
|
until COLUMNS(L,J) = SELNAMES(I)
|
||
|
next J
|
||
|
if J > 2+KNUM then
|
||
|
if L = MAXC+1 then L = COLLAST else L -= 1
|
||
|
continue
|
||
|
end
|
||
|
if COLUMNS(L,1) = "FOUND" then
|
||
|
* This can only happen if @SELECT contains more than one
|
||
|
* field name defining the same field position (AMC)
|
||
|
USESELECT = 1
|
||
|
AMCAGAIN = 1
|
||
|
end else
|
||
|
COLUMNS(L,1) = "FOUND"
|
||
|
if J <> 3 then
|
||
|
SELNAMES(I) = COLUMNS(L,3)
|
||
|
USESELECT = 1
|
||
|
end
|
||
|
end
|
||
|
until 1
|
||
|
repeat
|
||
|
if not(AMCAGAIN) then TVAR := SELNAMES(I):" "
|
||
|
next I
|
||
|
if TVAR[1] = " " then TVAR = TVAR[1,len(TVAR)-1]
|
||
|
write TVAR to NVAR,"SQL_SELECT" else goto WRERR
|
||
|
end else
|
||
|
* Create SQL_SELECT record if there was no @SELECT or @ phrase in dict
|
||
|
if KEYLAST > 1 then
|
||
|
for L = 1 to KEYLAST
|
||
|
TVAR := KEYNAMES(L,1):" "
|
||
|
next L
|
||
|
end else
|
||
|
TVAR := COLUMNS(MAXC+1,3):" "
|
||
|
end
|
||
|
for L = 1 to COLLAST
|
||
|
if COLUMNS(L,2) > 0 then TVAR := COLUMNS(L,3):" "
|
||
|
next L
|
||
|
if TVAR[1] = " " then TVAR = TVAR[1,len(TVAR)-1]
|
||
|
write TVAR to NVAR,"SQL_SELECT" else goto WRERR
|
||
|
USESELECT = 1
|
||
|
end
|
||
|
* Create and write the SQL_INFO record
|
||
|
gosub GETDAT ; * get current date/time in DATETIME
|
||
|
DSTAMP = "(SQLDEF was generated ":DATETIME:")"
|
||
|
TVAR = "X":@FM:FILENM:@FM:COLLAST:@FM:ASSOCLAST:@FM:CURREVCODE:@FM:DSTAMP
|
||
|
TVAR := @FM:SELLAST:@FM:KEYLAST:@FM:SEPCHAR:@FM:USESELECT:@FM:NEEDATKEY
|
||
|
write TVAR to NVAR,"SQL_INFO" else goto WRERR
|
||
|
* Create and write the SQL_VOC record
|
||
|
TVAR = "X":@FM:FPATH:@FM:DPATH:@FM:VOCCODE
|
||
|
if RVAR<5> <> "" then
|
||
|
if RVAR<6> <> "" then TVAR := @FM:RVAR<5>:@FM:RVAR<6> else
|
||
|
TVAR := @FM:RVAR<5>
|
||
|
end
|
||
|
end else
|
||
|
if RVAR<6> <> "" then TVAR := @FM:@FM:RVAR<6>
|
||
|
end
|
||
|
write TVAR to NVAR,"SQL_VOC" else goto WRERR
|
||
|
|
||
|
REPORT:
|
||
|
* The SQLDEF file (NVAR) and its dict (DNVAR) are open and filelocked
|
||
|
read INFREC from NVAR,"SQL_INFO" else goto RDERR
|
||
|
TABNM = INFREC<2>
|
||
|
COLLAST = INFREC<3>
|
||
|
ASSOCLAST = INFREC<4>
|
||
|
REVCODE = INFREC<5>
|
||
|
DSTAMP = INFREC<6>
|
||
|
SELLAST = INFREC<7>
|
||
|
KEYLAST = INFREC<8>
|
||
|
SEPCHAR = INFREC<9>
|
||
|
USESELECT = INFREC<10>
|
||
|
NEEDATKEY = INFREC<11>
|
||
|
|
||
|
* If SQLDEF file was created with incompatible rev of CONVERT.SQL, recreate it
|
||
|
if REVCODE # CURREVCODE then goto DELEX
|
||
|
|
||
|
* Print Col and Assoc definitions if DISP is set
|
||
|
* Build CREATE EXISTING TABLE in array CRX
|
||
|
PINFO:
|
||
|
dim CRX (MAXLINES+2)
|
||
|
mat CRX = ""
|
||
|
CRX(1) = 'CREATE EXISTING TABLE "':change(TABNM,'"','""'):'"'
|
||
|
CRNEX = 2
|
||
|
* The ith element of array CRXCOL contains the line number (in the CREATE
|
||
|
* EXISTING TABLE statement) where column i is defined; used by MKDEFASSOC
|
||
|
dim CRXCOL (COLLAST+5)
|
||
|
mat CRXCOL = 0
|
||
|
if DISP then
|
||
|
print 'Table name: "':TABNM:'"':space(3):
|
||
|
if len(TABNM) < 18 then print space(18 - len(TABNM)):
|
||
|
print DSTAMP
|
||
|
print "Columns:"
|
||
|
end
|
||
|
NN = "NOT NULL"
|
||
|
PK = "PRIMARY KEY"
|
||
|
COLFIRST = 0
|
||
|
* Handle multi-part primary key (case: KEYLAST > 1)
|
||
|
if KEYLAST > 1 then
|
||
|
PKCLAUSE = PK
|
||
|
if SEPCHAR # "" then PKCLAUSE := " '":SEPCHAR:"'"
|
||
|
PKCLAUSE := " ("
|
||
|
CRX(CRNEX) = "("
|
||
|
for I = 1 to KEYLAST
|
||
|
if I <= 9 then I2 = "0":I else I2 = I
|
||
|
read DEF1 from NVAR,"SQL_K":I2 else goto RDERR
|
||
|
NEEDQ = DEF1<2> ; gosub QFIRST
|
||
|
DEF12 = NEEDQ
|
||
|
if DISP then print " K":I2:" ":DEF12
|
||
|
PKCLAUSE := field(DEF12," ",1,1):", "
|
||
|
findstr " ":NN in DEF12 setting FMC then
|
||
|
CRX(CRNEX) := DEF12:","
|
||
|
end else
|
||
|
CRX(CRNEX) := field(DEF12," ",1,2)
|
||
|
if field(DEF12," ",2,1) = "DOUBLE" then
|
||
|
CRX(CRNEX) := " ":field(DEF12," ",3,1)
|
||
|
CRX(CRNEX) := " ":NN:" ":field(DEF12," ",4,99):","
|
||
|
end else
|
||
|
CRX(CRNEX) := " ":NN:" ":field(DEF12," ",3,99):","
|
||
|
end
|
||
|
end
|
||
|
CRNEX += 1
|
||
|
next I
|
||
|
PKCLAUSE = PKCLAUSE[1,len(PKCLAUSE)-2]
|
||
|
CRX(CRNEX) = PKCLAUSE:"),"
|
||
|
CRNEX += 1
|
||
|
COLFIRST = 1
|
||
|
end
|
||
|
LESS100 = 1
|
||
|
* Build sorted select list of column synonyms for columns 0 through 99,
|
||
|
* and read first record-id from the list into SQLID.
|
||
|
gosub BUILDSELCOL
|
||
|
PINFO4:
|
||
|
for I = COLFIRST to COLLAST
|
||
|
if I <= 9 then I2 = "0":I else I2 = I
|
||
|
read DEF1 from NVAR,"SQL_C":I2 else goto RDERR
|
||
|
NEEDQ = DEF1<2> ; gosub QFIRST
|
||
|
DEF12 = NEEDQ
|
||
|
if DISP then print " ":I2:" ":DEF12
|
||
|
* Handle single-part primary key
|
||
|
if I2 = "00" then
|
||
|
NNPK = " ":NN:" ":PK:" "
|
||
|
findstr PK in DEF12 setting FMC then
|
||
|
CRX(CRNEX) = "(":DEF12:","
|
||
|
end else
|
||
|
CRX(CRNEX) = "(":field(DEF12," ",1,2)
|
||
|
if field(DEF12," ",2,1) = "DOUBLE" then
|
||
|
CRX(CRNEX) := " ":field(DEF12," ",3,1)
|
||
|
CRX(CRNEX) := NNPK:field(DEF12," ",4,99):","
|
||
|
end else
|
||
|
CRX(CRNEX) := NNPK:field(DEF12," ",3,99):","
|
||
|
end
|
||
|
end
|
||
|
end else
|
||
|
CRX(CRNEX) = DEF12:","
|
||
|
end
|
||
|
PINFO3:
|
||
|
CRXCOL(I) = CRNEX
|
||
|
CRNEX += 1
|
||
|
BASEID = field(DEF1<2>," ",1,1)
|
||
|
* If column number is > 99 and a second list hasn't been built yet,
|
||
|
* build sorted select list of column synonyms for columns greater
|
||
|
* than 99 and read first record-id from the list into SQLID.
|
||
|
if I > 99 and LESS100 then
|
||
|
LESS100 = 0
|
||
|
clearselect
|
||
|
gosub BUILDSELCOL
|
||
|
end
|
||
|
PINFO1:
|
||
|
if CRNEX > MAXLINES then
|
||
|
print "Too many column/association definitions (>":MAXLINES:")"
|
||
|
gosub UCAF
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
loop
|
||
|
while SQLID # "NOMORE" do
|
||
|
if num(SQLID[8,1]) then
|
||
|
ALPHID = SQLID[9,1]
|
||
|
NUMID = SQLID[6,3]
|
||
|
end else
|
||
|
ALPHID = SQLID[8,1]
|
||
|
NUMID = SQLID[6,2]
|
||
|
end
|
||
|
while NUMID < I do
|
||
|
readnext SQLID else SQLID = "NOMORE"
|
||
|
repeat
|
||
|
if SQLID = "NOMORE" or NUMID > I then continue
|
||
|
read DEF2 from NVAR,SQLID else goto RDERR
|
||
|
readnext SQLID else SQLID = "NOMORE"
|
||
|
if upcase(DEF2<3>) = "D" then goto PINFO1
|
||
|
* Print out synonyms (but not the alphid which defines the preferred column)
|
||
|
if DISP then
|
||
|
if field(trim(DEF2<2>)," ",1,1) <> field(trim(DEF1<2>)," ",1,1) then
|
||
|
NEEDQ = DEF2<2> ; gosub QFIRST
|
||
|
print " ":I2:ALPHID:" ":NEEDQ
|
||
|
end
|
||
|
end
|
||
|
goto PINFO1
|
||
|
next I
|
||
|
clearselect
|
||
|
|
||
|
if ASSOCLAST = 0 then goto FINCRX
|
||
|
if DISP then print "Associations:"
|
||
|
for I = 1 to ASSOCLAST
|
||
|
if CRNEX > MAXLINES then
|
||
|
print "Too many column/association definitions (>":MAXLINES:")"
|
||
|
gosub UCAF
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
OLAP = 0
|
||
|
if I <= 9 then I2 = "0":I else I2 = I
|
||
|
read DEF1 from NVAR,"SQL_A":I2 else goto RDERR
|
||
|
if upcase(DEF1<3>) = "D" then continue
|
||
|
if upcase(DEF1<3>) = "O" then OLAP = 1 else
|
||
|
NEEDQ = DEF1<2> ; gosub QFIRST
|
||
|
ADEF = NEEDQ
|
||
|
if DISP then print " ":I2:" ":ADEF
|
||
|
gosub MKDEFASSOC
|
||
|
CRX(CRNEX) = "ASSOC ":COLDEF:"," ; CRNEX += 1
|
||
|
end
|
||
|
AC = "A" ; gosub BUILDSELAC
|
||
|
PINFO2:
|
||
|
readnext SQLID else continue
|
||
|
read DEF2 from NVAR,SQLID else goto RDERR
|
||
|
ALPHID = SQLID[8,1]
|
||
|
if (((DEF2<2> <> DEF1<2>) or OLAP) and DISP) then
|
||
|
print " ":I2:ALPHID:" ":
|
||
|
NEEDQ = DEF2<2> ; gosub QFIRST
|
||
|
print NEEDQ:
|
||
|
if OLAP then print space(6):"Overlapping association not used" else
|
||
|
print space(6):"PHrase differs from ASSOC"
|
||
|
end
|
||
|
end
|
||
|
goto PINFO2
|
||
|
next I
|
||
|
|
||
|
* Finish the CREATE EXISTING TABLE statement
|
||
|
* Replace trailing comma, on last column or assoc definition, by ");"
|
||
|
FINCRX:
|
||
|
CRL = CRX(CRNEX-1)
|
||
|
CRX(CRNEX-1) = CRL[1,len(CRL)-1]:");"
|
||
|
CRXDONE = 1
|
||
|
|
||
|
if WARNX > 1 then
|
||
|
NUMAST = 43 - len(FILENM)
|
||
|
if NUMAST < 4 then NUMAST = 4
|
||
|
NUMAST2 = int(NUMAST/2)
|
||
|
NUMAST1 = NUMAST2
|
||
|
if NUMAST2 <> NUMAST/2 then NUMAST1 += 1
|
||
|
BANNER = change(space(NUMAST1)," ","*"):' Inconsistencies found in DICT "'
|
||
|
BANNER := FILENM:'" ':change(space(NUMAST2)," ","*")
|
||
|
print BANNER
|
||
|
for I = 1 to WARNX-1
|
||
|
print " ":WARNINGS(I)
|
||
|
next I
|
||
|
print "****************************************************************":
|
||
|
print "*************"
|
||
|
end
|
||
|
|
||
|
if SHOW then gosub PCRX
|
||
|
if CRTAB then goto CRXTAB
|
||
|
if TEST then
|
||
|
if TESTEDIT then TEST = 0 else goto GOODBYE
|
||
|
end
|
||
|
|
||
|
* Allow user to edit the column definitions and association definitions
|
||
|
EDIT:
|
||
|
print "Enter C..., D..., U..., R..., ":
|
||
|
print "R, S, X, Q, or H for Help [R]: ":
|
||
|
if TESTEDIT then
|
||
|
TVAR = INPUTS(INCOUNT)
|
||
|
print TVAR
|
||
|
INCOUNT += 1
|
||
|
end else
|
||
|
input TVAR
|
||
|
end
|
||
|
EDIT1:
|
||
|
* Reset SHOW and DISP in case they were changed by previous "S" typein
|
||
|
SHOW = 0
|
||
|
DISP = 1
|
||
|
begin case
|
||
|
* case upcase(TVAR) = "V" **??
|
||
|
* gosub MAKEVFLAG
|
||
|
* goto EDIT
|
||
|
case TVAR = "" or upcase(TVAR) = "R"
|
||
|
goto REPORT
|
||
|
case upcase(TVAR[1,1]) = "R"
|
||
|
EDCOM = TVAR
|
||
|
gosub DISPCA
|
||
|
if EDERR then gosub PEDERR
|
||
|
goto EDIT
|
||
|
case upcase(TVAR) = "S"
|
||
|
if CRXDONE then
|
||
|
gosub PCRX
|
||
|
goto EDIT
|
||
|
end else
|
||
|
SHOW = 1
|
||
|
DISP = 0
|
||
|
goto REPORT
|
||
|
end
|
||
|
case upcase(TVAR) = "X"
|
||
|
CRTAB = 1
|
||
|
SAVE = 0
|
||
|
DISP = 0
|
||
|
goto REPORT
|
||
|
case upcase(TVAR) = "X.SAVEDATA"
|
||
|
CRTAB = 1
|
||
|
SAVE = 1
|
||
|
DISP = 0
|
||
|
goto REPORT
|
||
|
case upcase(TVAR) = "Q"
|
||
|
gosub UCAF
|
||
|
goto GOODBYE
|
||
|
case upcase(TVAR) = "H"
|
||
|
goto HELP
|
||
|
case upcase(TVAR) = "M"
|
||
|
goto MOREHELP
|
||
|
case upcase(TVAR[1,1]) = "C"
|
||
|
CRXDONE = 0
|
||
|
EDCOM = TVAR
|
||
|
gosub CHANGECA
|
||
|
if EDERR then gosub PEDERR
|
||
|
goto EDIT
|
||
|
case upcase(TVAR[1,1]) = "D"
|
||
|
CRXDONE = 0
|
||
|
EDCOM = TVAR
|
||
|
gosub DELETECA
|
||
|
if EDERR then gosub PEDERR
|
||
|
goto EDIT
|
||
|
case upcase(TVAR[1,1]) = "U"
|
||
|
CRXDONE = 0
|
||
|
EDCOM = TVAR
|
||
|
gosub USECA
|
||
|
if EDERR then gosub PEDERR
|
||
|
goto EDIT
|
||
|
case 1
|
||
|
print " Invalid response" ; goto EDIT
|
||
|
end case
|
||
|
|
||
|
HELP:
|
||
|
*print "You may now change column or association definitions:"
|
||
|
*print " Enter V to Validate all current definitions"
|
||
|
print " Enter C... to Change a column or association"
|
||
|
print " Enter D... to Delete an association"
|
||
|
print " Enter U... to Use another column or association"
|
||
|
print " Enter R... to Redisplay a column or association"
|
||
|
print " Enter M for More help on the above options"
|
||
|
print " Enter R to Redisplay all current definitions (default)"
|
||
|
print " Enter S to Show the CREATE EXISTING TABLE statement"
|
||
|
print " Enter X to eXecute the CREATE EXISTING TABLE statement"
|
||
|
print " Enter X.SAVEDATA to save the file's data and do the X option"
|
||
|
print " Enter Q to Quit"
|
||
|
print " Enter H for Help"
|
||
|
print "What is your choice? [R] ": ; input TVAR
|
||
|
goto EDIT1
|
||
|
|
||
|
MOREHELP:
|
||
|
print " CCn/xx/yy[/G] changes column n, replacing string 'xx' by 'yy'"
|
||
|
print " CKn/xx/yy[/G] changes key-part n, replacing string 'xx' by 'yy'"
|
||
|
print " CAn/xx/yy[/G] changes association n, replacing string 'xx' by 'yy'"
|
||
|
print " CCn T type changes datatype of column n to 'type'"
|
||
|
print " CKn T type changes datatype of key-part n to 'type'"
|
||
|
print " DAn deletes association n"
|
||
|
print " UCna uses synonym 'a' as preferred definition of column n"
|
||
|
print " UAna uses synonym 'a' as definition of association n"
|
||
|
print " RCn redisplays column n with all of its synonyms"
|
||
|
print " RKn redisplays key-part n"
|
||
|
print " RAn redisplays association n"
|
||
|
goto EDIT
|
||
|
|
||
|
* Prepare to Build and Execute the CREATE EXISTING TABLE statement
|
||
|
CRXTAB:
|
||
|
* Delete @KEY phrase from dictionary if it needs to be changed
|
||
|
if NEEDATKEY then
|
||
|
read TVAR from DVAR,"@KEY" then delete DVAR,"@KEY"
|
||
|
end
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
* If SAVEDATA option, create <filename>_SQLSAVE and copy data into it
|
||
|
if SAVE then
|
||
|
openpath VPATH to VOCVAR else
|
||
|
gosub UCAF
|
||
|
stop CMDNAME:" aborted. Can't open '":VPATH:"'"
|
||
|
end
|
||
|
SAVNM = change(FILENM," ","_"):"_SQLSAVE"
|
||
|
* Check if <filename>_SQLSAVE exists; if so, don't create it
|
||
|
LSLEEP = MAXSLEEP
|
||
|
CRXTAB1:
|
||
|
if LSLEEP <= 0 then
|
||
|
close VOCVAR
|
||
|
gosub UCAF
|
||
|
stop CMDNAME:" aborted. '":VPATH:"' is locked"
|
||
|
end
|
||
|
readl TVAR from VOCVAR,SAVNM locked
|
||
|
LSLEEP -= 1
|
||
|
sleep 2
|
||
|
goto CRXTAB1
|
||
|
end then
|
||
|
release VOCVAR,SAVNM
|
||
|
CLSAVE = "CLEAR.FILE ":SAVNM
|
||
|
print "Generating file '":SAVNM:"'"
|
||
|
execute CLSAVE capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
goto CRXTAB2
|
||
|
end else release VOCVAR,SAVNM
|
||
|
if CREATE.SYNTAX = "PICK" then
|
||
|
CRSAVE = "CREATE.FILE ":SAVNM:" 1 ":PMODSEP:",":FTYPE
|
||
|
end else
|
||
|
CRSAVE = "CREATE.FILE ":SAVNM:" ":FTYPE
|
||
|
if FMODSEP <> "" then CRSAVE := " ":FMODSEP
|
||
|
end
|
||
|
print "Generating file '":SAVNM:"'"
|
||
|
execute CRSAVE capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
CRXTAB2:
|
||
|
CPSAVE = "COPYI FROM ":FILENM:" TO ":SAVNM:" ALL"
|
||
|
execute CPSAVE capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
close VOCVAR ; *close the VOC file
|
||
|
end
|
||
|
print "Preparing to create table ......"
|
||
|
|
||
|
* Now build the CREATE EXISTING TABLE statement
|
||
|
CRXDEF = CRX(1):" "
|
||
|
for I = 2 to CRNEX-1
|
||
|
CRXDEF := CRX(I):" "
|
||
|
next I
|
||
|
CRXDEF = CRXDEF[1,len(CRXDEF)-1]
|
||
|
TVAR = 9
|
||
|
|
||
|
* Now execute the CREATE EXISTING TABLE statement
|
||
|
* If outputting to printer, then capture-and-print output because otherwise
|
||
|
* sdml writes its output to error-out which goes to screen
|
||
|
if LPTR then
|
||
|
execute CRXDEF capturing CRTABOUT setting TVAR
|
||
|
print change(CRTABOUT,char(254),char(10),0,1)
|
||
|
end else
|
||
|
execute CRXDEF setting TVAR
|
||
|
end
|
||
|
|
||
|
* Check to see if CREATE EXISTING TABLE failed
|
||
|
if TVAR <> 0 then
|
||
|
if TVAR <> -1 then goto NORETURN
|
||
|
print "Table could not be created."
|
||
|
* Silently issue a CREATE EXISTING TABLE...RESTORE command since the
|
||
|
* table may be partially created in some error situations
|
||
|
CRXDEF = 'CREATE EXISTING TABLE "':change(FILENM,'"','""'):'" RESTORE;'
|
||
|
execute CRXDEF capturing CRTABOUT setting TVAR
|
||
|
POUT = change(CRTABOUT,char(254),char(10),0,1)
|
||
|
if POUT[1] = char(10) then POUT = POUT[1,len(POUT)-1]
|
||
|
if DEBUG then print POUT
|
||
|
* Silently restore the dictionary from the SQLDEF file since it might
|
||
|
* be partially modified in some error situations
|
||
|
open "DICT",FILENM to DVAR else goto CRXTAB5
|
||
|
* Copy the saved dictionary records from SQLDEF to DICT FILENM
|
||
|
clearfile DVAR
|
||
|
SELNX = 'SELECT ':DEFNM:' WITH NOT (@ID LIKE "SQL_..." AND F1 LIKE "X...")'
|
||
|
execute SELNX capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
NOMORE = 0
|
||
|
loop
|
||
|
until NOMORE
|
||
|
readnext ATID then
|
||
|
read DICTREC from NVAR,ATID else goto RDERR
|
||
|
write DICTREC to DVAR,ATID else goto WRERR
|
||
|
end else NOMORE = 1
|
||
|
repeat
|
||
|
close DVAR
|
||
|
goto CRXTAB5
|
||
|
end else
|
||
|
* Write @SELECT record (unless no change needed) to the table's dictionary
|
||
|
if USESELECT = "1" then
|
||
|
read TVAR from NVAR,"SQL_SELECT" then
|
||
|
open "DICT",FILENM to DVAR else
|
||
|
OPSTAT = status()
|
||
|
print "Can't open DICT '":FILENM:"'. ":
|
||
|
gosub POPERR
|
||
|
goto CRXTAB5
|
||
|
end
|
||
|
write "PH":@FM:TVAR<2> to DVAR,"@SELECT" else goto WRERR
|
||
|
close DVAR
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
CRXTAB5:
|
||
|
fileunlock NVAR
|
||
|
close NVAR
|
||
|
fileunlock DNVAR
|
||
|
close DNVAR
|
||
|
|
||
|
GOODBYE:
|
||
|
* I think next is unnecessary because 1005 seems to get reset anyway but...
|
||
|
if SYS1005 = 0 then assign 0 to system(1005)
|
||
|
stop
|
||
|
|
||
|
|
||
|
|
||
|
*****************
|
||
|
* Subroutines *
|
||
|
*****************
|
||
|
|
||
|
* Subroutine to put current date/time in printable format (into DATETIME)
|
||
|
GETDAT:
|
||
|
DATETIME = oconv(date(),'D'):" ":oconv(time(),'MT')
|
||
|
return ; *from GETDAT
|
||
|
|
||
|
|
||
|
* Subroutine to rewrite SQL_INFO record with new date/time stamp
|
||
|
NEWDSTAMP:
|
||
|
read INFREC from NVAR,"SQL_INFO" else goto RDERR
|
||
|
gosub GETDAT
|
||
|
INFREC<6> = " (SQLDEF last updated ":DATETIME:")"
|
||
|
write INFREC to NVAR,"SQL_INFO" else goto WRERR
|
||
|
return ; *from NEWDSTAMP
|
||
|
|
||
|
|
||
|
* TRIMAT subroutine
|
||
|
*4/29/97 All this routine does is fix up field 2.
|
||
|
*4/29/97 It doesn't trim other fields any more.
|
||
|
*NONO Trim all fields in ATREC and eliminate completely blank fields.NONONONO
|
||
|
* For ATREC<2>, the location field, change empty or multiple 0's to a single 0.
|
||
|
* Also remove leading zeros from location field.
|
||
|
TRIMAT:
|
||
|
* for T = 1 to 10
|
||
|
* ATREC<T> = trim(ATREC<T>)
|
||
|
* if ATREC<T> = " " then ATREC<T> = ""
|
||
|
* next T
|
||
|
if (ATREC<2> = '' or ATREC<2> = 0) then ATREC<2> = '0'
|
||
|
if len(ATREC<2>) > 1 then ATREC<2> = trim(ATREC<2>,"0","L")
|
||
|
return ; *from TRIMAT
|
||
|
|
||
|
|
||
|
* Setup for the "INFO" argument
|
||
|
SETUPINFO:
|
||
|
CNTD = "COUNT DICT "
|
||
|
CNTAS2 = ' WITH CODE = "A" OR CODE LIKE "A ..."'
|
||
|
CNTAS2 := ' OR CODE = "S" OR CODE LIKE "S ..."'
|
||
|
CNTDD2 = ' WITH CODE LIKE "D..."'
|
||
|
SUMDD = "SELECT BREAK.ON LOC FMT '2R' COL.HDG 'LOC',"
|
||
|
SUMDD := " @ID FMT '18L' COL.HDG 'FIELD NAME',"
|
||
|
SUMDD := " SM FMT '2L' COL.HDG 'SM',"
|
||
|
SUMDD := " FORMAT FMT '4L' COL.HDG 'FMT',"
|
||
|
SUMDD := " CONV FMT '6L' COL.HDG 'CONV',"
|
||
|
SUMDD := " NAME FMT '24L' COL.HDG 'DISPLAY NAME',"
|
||
|
SUMDD := " SQLTYPE FMT '16L' COL.HDG 'SQLTYPE'"
|
||
|
SUMDD := " FROM DICT "
|
||
|
SUMDD2 = " WHERE CODE LIKE 'D%' ORDER BY LOC HEADING "
|
||
|
SUMDD2 := '"Type D entries from DICT '
|
||
|
SUMDD2 := "'F18' 'T'"
|
||
|
SUMDD2 := '" COUNT.SUP;'
|
||
|
SUMAS = "SELECT BREAK.ON F2 FMT '2R' COL.HDG 'LOC',"
|
||
|
SUMAS := " @ID FMT '18L' COL.HDG 'FIELD NAME',"
|
||
|
SUMAS := " F9 FMT '2L' COL.HDG 'TY',"
|
||
|
SUMAS := " F10 FMT '4L' COL.HDG 'WID',"
|
||
|
SUMAS := " F7 FMT '6L' COL.HDG 'CONV',"
|
||
|
SUMAS := " F3 FMT '24L' COL.HDG 'DISPLAY NAME',"
|
||
|
SUMAS := " F1 FMT '2L' COL.HDG 'AS',"
|
||
|
SUMAS := " F8 FMT '9L' COL.HDG 'CORR',"
|
||
|
SUMAS := " F6 FMT '3L' COL.HDG 'SQL'"
|
||
|
SUMAS := " FROM DICT "
|
||
|
SUMAS2 = " WHERE F1 = 'A' OR F1 LIKE 'A %' OR F1 = 'S'"
|
||
|
SUMAS2 := " OR F1 LIKE 'S %' ORDER BY LOC HEADING "
|
||
|
SUMAS2 := '"Type A and S entries from DICT '
|
||
|
SUMAS2 := "'F18' 'T'"
|
||
|
SUMAS2 := '" COUNT.SUP;'
|
||
|
return ; *from SETUPINFO
|
||
|
|
||
|
|
||
|
* Print dictionary info (part of INFO option)
|
||
|
* This subr removes ** and *** lines, press-any-key lines, clear-screen lines,
|
||
|
* superfluous blank lines (really?), and heading lines except the first
|
||
|
* Input is TVAR (captured output of a SELECT statement from DICT...)
|
||
|
PRINTOUT:
|
||
|
LOOPP = 0
|
||
|
HDG = 0
|
||
|
BLINE = 0
|
||
|
loop
|
||
|
LOOPP += 1
|
||
|
while TVAR <> "" and LOOPP <= 999 do
|
||
|
TVARF = field(TVAR,@FM,1,1)
|
||
|
if seq(TVARF[1,1]) = 27 then
|
||
|
LOOPN = 0
|
||
|
loop
|
||
|
LOOPN += 1
|
||
|
while TVARF <> "" and TVARF[1,4] <> "Type" and LOOPN <= 99 do TVARF = TVARF[2,len(TVARF)-1]
|
||
|
repeat
|
||
|
end
|
||
|
* Next takes care of INFO flavor BREAK.ON (line consists of **)
|
||
|
if TVARF = "**" then del TVAR<1> ; continue
|
||
|
* Next takes care of PICK flavor BREAK.ON (*** line followed by blank line)
|
||
|
if TVARF = "***" then
|
||
|
del TVAR<1>
|
||
|
if trim(TVAR<1>) = "" then del TVAR<1>
|
||
|
continue
|
||
|
end
|
||
|
if TVARF[1,5] = "Press" then del TVAR<1> ; continue
|
||
|
if TVARF[1,4] = "Type" and HDG = 2 then del TVAR<1> ; continue
|
||
|
if TVARF[1,5] = "LOC F" and HDG = 2 then del TVAR<1> ; continue
|
||
|
print TVARF
|
||
|
begin case
|
||
|
case HDG = 0
|
||
|
if TVARF[1,4] <> "Type" and TVARF <> "" then
|
||
|
print "Error in dictionary list output"
|
||
|
return
|
||
|
end else
|
||
|
if TVARF <> "" then HDG = 1
|
||
|
end
|
||
|
case HDG = 1
|
||
|
if TVARF[1,5] <> "LOC F" and TVARF <> "" then
|
||
|
print "Error in dictionary list output"
|
||
|
return
|
||
|
end else
|
||
|
if TVARF <> "" then HDG = 2
|
||
|
end
|
||
|
end case
|
||
|
if TVARF = "" then BLINE = 1 else BLINE = 0
|
||
|
del TVAR<1>
|
||
|
repeat
|
||
|
return ; *from PRINTOUT
|
||
|
|
||
|
|
||
|
* Get count of number of dictionary items of type D, or of type A or S
|
||
|
* Input is CNTASD (which is the COUNT statement ready to execute)
|
||
|
* Output is CNTCNT:
|
||
|
* "" means we can't COUNT this dictionary and message has been issued
|
||
|
* else CNTCNT contains the requested COUNT (which, of course, may be 0)
|
||
|
GETCNT:
|
||
|
execute CNTASD capturing TVAR
|
||
|
if TVAR[1,1] = @FM then TVAR = trim(TVAR[2,29]) else TVAR = trim(TVAR[1,29])
|
||
|
if count(TVAR,"records counted") <> 1 then
|
||
|
CNTCNT = ""
|
||
|
print "Can't count DICT '":FILENM:"'"
|
||
|
return
|
||
|
end
|
||
|
CNTCNT = field(TVAR[1,29]," ",1,1)
|
||
|
if not(num(CNTCNT)) then
|
||
|
CNTCNT = ""
|
||
|
print "Can't count DICT '":FILENM:"'"
|
||
|
return
|
||
|
end
|
||
|
return ; *from GETCNT
|
||
|
|
||
|
|
||
|
* Build select list to get all definitions of the nth column or association
|
||
|
* (except the "planned" definition"
|
||
|
* Input is: I2 (2-or-more-digit column number)
|
||
|
* AC ("C" for column, "A" for association)
|
||
|
BUILDSELAC:
|
||
|
SELAC = 'SELECT ':DEFNM:' BY @ID WITH @ID LIKE "'
|
||
|
SELAC := "'SQL_":AC:I2:"'1A...":'"'
|
||
|
execute SELAC capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
return ; *from BUILDSELAC
|
||
|
|
||
|
|
||
|
* Build sorted select list of all column synonyms (either columns in the
|
||
|
* range 00 - 99 or columns with numbers greater than 99, depending on the
|
||
|
* value of input parameter LESS100). Then read the first record-id into
|
||
|
* SQLID, setting SQLID = "NOMORE" there are no records.
|
||
|
BUILDSELCOL:
|
||
|
SELAC = 'SELECT ':DEFNM:' BY @ID WITH @ID LIKE "'
|
||
|
if LESS100 then
|
||
|
SELAC := "'SQL_C'2N1A...":'"'
|
||
|
end else
|
||
|
SELAC := "'SQL_C'3N1A...":'"'
|
||
|
end
|
||
|
execute SELAC capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
readnext SQLID else SQLID = "NOMORE"
|
||
|
return ; *from BUILDSELCOL
|
||
|
|
||
|
|
||
|
* Check if a given column name is in user-specified @SELECT phrase
|
||
|
* by consulting the SELNAMES() array.
|
||
|
* Input: ATID is column name to be checked
|
||
|
* Output: INSEL = 1 if there is a user-specified @SELECT and ATID is in it
|
||
|
* INSEL = 0 otherwise
|
||
|
CKSEL:
|
||
|
INSEL = 0
|
||
|
if NEWATSEL or (SELLAST = 0) then return
|
||
|
for I = 1 to SELLAST
|
||
|
until SELNAMES(I,1) = ATID
|
||
|
next I
|
||
|
if I <= SELLAST then INSEL = 1
|
||
|
return ; *from CKSEL
|
||
|
|
||
|
|
||
|
* Check if a given column name is in @KEY phrase (if @KEY contains more than
|
||
|
* one token) by consulting the KEYNAMES() array.
|
||
|
* Input: ATID is column name to be checked
|
||
|
* Output: INKEY = 1 if there is a multi-token @KEY phrase and ATID is in it
|
||
|
* INKEY = 0 otherwise
|
||
|
CKKEY:
|
||
|
INKEY = 0
|
||
|
if KEYLAST <= 1 then return
|
||
|
for I = 1 to KEYLAST
|
||
|
until KEYNAMES(I,1) = ATID
|
||
|
next I
|
||
|
if I <= KEYLAST then INKEY = 1
|
||
|
return ; *from CKKEY
|
||
|
|
||
|
|
||
|
* Check if a given column name is in @ phrase by consulting ATNAMES() array.
|
||
|
* Input: ATID is column name to be checked
|
||
|
* Output: INAT = 1 if there is an @ phrase and ATID is in it
|
||
|
* INAT = 0 otherwise
|
||
|
CKAT:
|
||
|
INAT = 0
|
||
|
if ATLAST <= 0 then return
|
||
|
for I = 1 to ATLAST
|
||
|
until ATNAMES(I,1) = ATID
|
||
|
next I
|
||
|
if I <= ATLAST then INAT = 1
|
||
|
return ; *from CKAT
|
||
|
|
||
|
|
||
|
* Create column definition from D, A, or S type dictionary record
|
||
|
* Builds COLDEF consisting of <column name>
|
||
|
* <datatype>
|
||
|
* then optionally <multival>
|
||
|
* <colhdg>
|
||
|
* <fmt>
|
||
|
* <conv>
|
||
|
*
|
||
|
* Inputs are: ATID (record-id)
|
||
|
* ATREC (record-contents)
|
||
|
* CODE (D, A, or S)
|
||
|
* LOC (field location)
|
||
|
* Note: LOC < 0 means this is a key-part of a multi-part key;
|
||
|
* in this case, if the field is an I-descriptor, the
|
||
|
* value of CODE will be D
|
||
|
CREATECOL:
|
||
|
if LOC < 0 then
|
||
|
LOC2 = LOC * (-1)
|
||
|
if LOC2 <= 9 then LOC2 = "0":LOC2
|
||
|
end else
|
||
|
if LOC <= 9 then LOC2 = "0":LOC else LOC2 = LOC
|
||
|
end
|
||
|
|
||
|
* Get SQLTYPE
|
||
|
if CODE = 'D' then SQLTYPE = ATREC<8> else SQLTYPE = ATREC<6>
|
||
|
|
||
|
* Get single/multi (MULTI), and get assoc info (ASSOC)
|
||
|
MULTI = 'S'
|
||
|
ASSOC = ""
|
||
|
if CODE = 'D' then
|
||
|
if ATREC<6>[1,1] = 'M' then MULTI = 'M'
|
||
|
ASSOC = ATREC<7>
|
||
|
if LOC <= 0 then
|
||
|
if MULTI = 'M' then
|
||
|
WX = 'Dict entry "':ATID:'" defines'
|
||
|
if LOC < 0 then WX := " a Key-part" else WX := " field 0"
|
||
|
WARNINGS(WARNX) = WX:" so its SM = 'M' is illegal"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
MULTI = 'S'
|
||
|
ASSOC = ""
|
||
|
end else
|
||
|
if ASSOC # "" then
|
||
|
WX = 'Dict entry "':ATID:'" defines'
|
||
|
if LOC < 0 then WX := " a Key-part" else WX := " field 0"
|
||
|
WARNINGS(WARNX) = WX:" so its ASSOC '":ASSOC:"' is ignored"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
ASSOC = ""
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end else
|
||
|
AR4 = ATREC<4>
|
||
|
if ATREC<5> = 'M' then MULTI = 'M'
|
||
|
if len(AR4) > 2 and (AR4[1,2] = "D;" or AR4[1,2] = "C;") then
|
||
|
MULTI = 'M'
|
||
|
ASSOC = AR4
|
||
|
end
|
||
|
if LOC <= 0 then
|
||
|
if ASSOC # "" then
|
||
|
WX = 'Dict entry "':ATID:'" defines'
|
||
|
if LOC < 0 then WX := " a Key-part" else WX := " field 0"
|
||
|
WARNINGS(WARNX) = WX:" so its ASSOC '":ASSOC:"' is illegal"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
MULTI = 'S'
|
||
|
ASSOC = ""
|
||
|
end else
|
||
|
if MULTI = 'M' then
|
||
|
WX = 'Dict entry "':ATID:'" defines'
|
||
|
if LOC < 0 then WX := " a Key-part" else WX := " field 0"
|
||
|
WARNINGS(WARNX) = WX:" so its SM = 'M' is illegal"
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
MULTI = 'S'
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if ASSOC[1,2] = "D;" then ASSOC = ""
|
||
|
end
|
||
|
|
||
|
* Get COLHDG
|
||
|
if CODE = 'D' then COLHDG = ATREC<4> else COLHDG = ATREC<3>
|
||
|
|
||
|
* Get CONV
|
||
|
if CODE = 'D' then CONV = ATREC<3> else CONV = ATREC<7>
|
||
|
|
||
|
* Get FMT
|
||
|
if CODE = 'D' then FMT = ATREC<5> else
|
||
|
FMT = ATREC<10>
|
||
|
if ATREC<9> = "" then
|
||
|
if FMT <> "" then FMT := 'L'
|
||
|
end else
|
||
|
FMT := ATREC<9>
|
||
|
end
|
||
|
end
|
||
|
gosub ANALFMT
|
||
|
|
||
|
* At this point: SQLTYPE (from dict, may be empty)
|
||
|
* MULTI (M or S or PK)
|
||
|
* COLHDG (from dict, may be empty)
|
||
|
* CONV (from dict, may be empty)
|
||
|
* FMT (empty, <just>, or <width><just>)
|
||
|
* FMTJ (justification)
|
||
|
* FMTW (width)
|
||
|
* ASSOC (from dict, empty if A/S 'D;...')
|
||
|
|
||
|
* Add info to ASSOCS array if relevant
|
||
|
if ASSOC <> "" and MULTI = "M" then
|
||
|
if CODE = 'D' then ASSOCID = ASSOC else ASSOCID = "@DC":LOC
|
||
|
for I = 1 to ASSOCLAST
|
||
|
until ASSOCS(I,1) = ASSOCID
|
||
|
next I
|
||
|
begin case
|
||
|
case I > 50
|
||
|
WARNINGS(WARNX) = "ASSOC '":ASSOC:"' in dict entry"
|
||
|
WARNINGS(WARNX) := ' "':ATID:'" is ignored: >50 associations'
|
||
|
if WARNX < MAXWARN then WARNX += 1
|
||
|
goto CONVSQLTYPE
|
||
|
case I > ASSOCLAST
|
||
|
if ASSOCS(I,LOC+1) = "0" then ASSOCS(I,LOC+1) = "X"
|
||
|
ASSOCLAST += 1
|
||
|
ASSOCS(I,1) = ASSOCID
|
||
|
ASSOCS(I,MAXC+3) = "N"
|
||
|
read KEYX from NVAR,"@ASSOC_KEY.":ASSOCID then
|
||
|
KEYX2 = trim(KEYX<2>)
|
||
|
begin case
|
||
|
case upcase(KEYX<1>[1,1]) # "X"
|
||
|
case KEYX2 = "STABLE"
|
||
|
ASSOCS(I,MAXC+3) = "S"
|
||
|
case KEYX2 = "UNSTABLE"
|
||
|
ASSOCS(I,MAXC+3) = "U"
|
||
|
case field(KEYX2," ",1,1) = "KEY"
|
||
|
KEYX2 = field(KEYX2," ",2,999)
|
||
|
FOUND = 0
|
||
|
loop ; until KEYX2 = "" do
|
||
|
COLID = field(KEYX2," ",1,1)
|
||
|
for L = 1 to COLLAST
|
||
|
KNUM = COLUMNS(L,2)
|
||
|
for J = 3 to 2+KNUM
|
||
|
until COLUMNS(L,J) = COLID
|
||
|
next J
|
||
|
if J > 2+KNUM then continue
|
||
|
FOUND = 1
|
||
|
ASSOCS(I,L+1) = "K"
|
||
|
until 1
|
||
|
next L
|
||
|
KEYX2 = field(KEYX2," ",2,999)
|
||
|
repeat
|
||
|
if FOUND then ASSOCS(I,MAXC+3) = "K"
|
||
|
end case
|
||
|
end
|
||
|
read APH from NVAR,ASSOCID then
|
||
|
if upcase(APH<1>[1,2]) = "PH" then
|
||
|
PHRASES(I,1) = ASSOCID
|
||
|
PHORD = 1
|
||
|
DICTTEMP = APH<2>
|
||
|
PHNAME = ASSOCID ; gosub EXPANDPH
|
||
|
if VOCOPEN then close VOCVAR ; VOCOPEN = 0
|
||
|
loop ; until DICTTEMP = "" do
|
||
|
COLID = field(DICTTEMP," ",1,1)
|
||
|
* (5/20/97) At this point I was tempted to verify that COLID actually exists
|
||
|
* in the dictionary (else give error message), but then I realized that (a)
|
||
|
* keywords such as HEADING may legitimately appear in an association phrase,
|
||
|
* and furthermore (b) subroutine EXPANDPH has already purged any such tokens
|
||
|
* from DICTTEMP.
|
||
|
for L = 1 to COLLAST
|
||
|
KNUM = COLUMNS(L,2)
|
||
|
for J = 3 to 2+KNUM
|
||
|
until COLUMNS(L,J) = COLID
|
||
|
next J
|
||
|
if J > 2+KNUM then continue
|
||
|
PHRASES(I,1+L) = PHORD
|
||
|
PHORD += 1
|
||
|
until 1
|
||
|
next L
|
||
|
DICTTEMP = field(DICTTEMP," ",2,999)
|
||
|
repeat
|
||
|
PHRASES(I,MAXC+3) = PHORD-1
|
||
|
end
|
||
|
end
|
||
|
case 1
|
||
|
if ASSOCS(I,LOC+1) = "0" then ASSOCS(I,LOC+1) = "X"
|
||
|
end case
|
||
|
if CODE # 'D' then
|
||
|
ASC2 = trim(ASSOC[3,99])
|
||
|
for J = 1 to 98
|
||
|
NEXTNUM = trim(field(ASC2,";",1))
|
||
|
NEXTNUM = trim(NEXTNUM,"0","L")
|
||
|
until NEXTNUM = ""
|
||
|
ASC2 = field(ASC2,";",2,50)
|
||
|
if not(num(NEXTNUM)) then continue
|
||
|
if NEXTNUM > MAXC then continue
|
||
|
ASSOCS(I,NEXTNUM+1) = "X"
|
||
|
next J
|
||
|
end
|
||
|
end
|
||
|
|
||
|
* Convert SQLTYPE to correct format if it is present in dictionary
|
||
|
CONVSQLTYPE:
|
||
|
if len(SQLTYPE) > 0 then
|
||
|
gosub CONVST
|
||
|
if not(CSTERR) then goto MKDEFCOL
|
||
|
end
|
||
|
|
||
|
* Determine data type if SQLTYPE not present
|
||
|
* (note that there is code below to handle cases where FMTW = "" even though
|
||
|
* I don't think that can happen)
|
||
|
MAKETYPE:
|
||
|
if CONV = "" then
|
||
|
begin case
|
||
|
case FMT = ""
|
||
|
SQLTYPE = "VARCHAR"
|
||
|
case FMTJ = 'R'
|
||
|
SQLTYPE = "INT"
|
||
|
case FMTJ[1,1] = 'Q'
|
||
|
SQLTYPE = "REAL"
|
||
|
* If we reach this point, the Justification is L, T, or U
|
||
|
case 1
|
||
|
SQLTYPE = "VARCHAR"
|
||
|
if FMTW # "" then
|
||
|
if FMTW > 254 and FMTW < 65536 then SQLTYPE := "(":FMTW:")"
|
||
|
end
|
||
|
end case
|
||
|
end else
|
||
|
CONV12 = upcase(CONV[1,2])
|
||
|
begin case
|
||
|
case CONV12 = 'MD' or CONV12 = 'ML' or CONV12 = 'MR'
|
||
|
gosub GETSCALE
|
||
|
if SCALEX = "0" then SQLTYPE = "INT" else
|
||
|
SQLTYPE = "DEC(9,":SCALEX:")" ; * Should prec always be 9??
|
||
|
end
|
||
|
case CONV12 = 'MB' or CONV12 = 'MO' or CONV12 = 'MX' or CONV12 = 'NR'
|
||
|
SQLTYPE = "INT"
|
||
|
case CONV12[1,1] = 'Q'
|
||
|
SQLTYPE = "REAL"
|
||
|
case CONV12[1,1] = 'D'
|
||
|
SQLTYPE = "DATE"
|
||
|
case CONV12 = 'MT'
|
||
|
SQLTYPE = "TIME"
|
||
|
case 1 ; * There is a CONV but it isn't one of the above
|
||
|
SQLTYPE = "VARCHAR"
|
||
|
if FMTW # "" then
|
||
|
if FMTW > 254 and FMTW < 65536 then SQLTYPE := "(":FMTW:")"
|
||
|
end
|
||
|
end case
|
||
|
end
|
||
|
if FMTW = "" then FMT = ""
|
||
|
|
||
|
* Build column definition string
|
||
|
MKDEFCOL:
|
||
|
COLDEF = SQLTYPE
|
||
|
if MULTI[1,1] = 'M' then COLDEF := " MULTIVALUED"
|
||
|
if COLHDG # "" then
|
||
|
USTRING = COLHDG ; gosub QSTRING
|
||
|
COLDEF := " COL.HDG ":USTRING
|
||
|
end
|
||
|
if FMT # "" then
|
||
|
USTRING = FMT ; gosub QSTRING
|
||
|
COLDEF := " FMT ":USTRING
|
||
|
end
|
||
|
if CONV # "" then
|
||
|
begin case
|
||
|
* If type is DATE or TIME and CONV disagrees, don't emit CONV
|
||
|
case SQLTYPE = "DATE" and CONV[1,1] # "D"
|
||
|
case SQLTYPE = "TIME" and CONV[1,2] # "MT"
|
||
|
case 1
|
||
|
USTRING = CONV ; gosub QSTRING
|
||
|
COLDEF := " CONV ":USTRING
|
||
|
end case
|
||
|
end
|
||
|
|
||
|
COLDEF = ATID:" ":COLDEF
|
||
|
|
||
|
TVAR = "X":@FM:COLDEF
|
||
|
if LOC < 0 then
|
||
|
AVAR = "SQL_K":LOC2 ; * key-part of multi-part key
|
||
|
end else
|
||
|
AVAR = "SQL_C":LOC2:ALPHID:"_":ATID
|
||
|
end
|
||
|
* Write synonym (alphid) definition, or key-part definition, to SQLDEF file
|
||
|
write TVAR to NVAR,AVAR else goto WRERR
|
||
|
|
||
|
return ; *from CREATECOL
|
||
|
|
||
|
|
||
|
* Write preferred column definition (except for key-part) to SQLDEF file
|
||
|
WRCOL:
|
||
|
AVAR = "SQL_C":LOC2
|
||
|
if LOC = 0 then
|
||
|
TVAR<3> = COLUMNS(MAXC+1,2)
|
||
|
end else
|
||
|
TVAR<3> = COLUMNS(LOC,2)
|
||
|
end
|
||
|
write TVAR to NVAR,AVAR else goto WRERR
|
||
|
return ; *from WRCOL
|
||
|
|
||
|
|
||
|
* Check SQLTYPE (from dictionary) for validity and convert it to proper format
|
||
|
* for CREATE TABLE. If invalid, set CSTERR = 1 and set SQLTYPE = "".
|
||
|
CONVST:
|
||
|
CSTERR = 0
|
||
|
SQLTYPE = change(SQLTYPE,"INTEGER","INT",1,1)
|
||
|
SQLTYPE = change(SQLTYPE,"DECIMAL","DEC",1,1)
|
||
|
SQLTYPE = change(SQLTYPE,"CHARACTER","CHAR",1,1)
|
||
|
NCOMMA = count(SQLTYPE,",")
|
||
|
begin case
|
||
|
case SQLTYPE = "INT"
|
||
|
case SQLTYPE = "SMALLINT"
|
||
|
case SQLTYPE = "REAL"
|
||
|
case SQLTYPE = "DATE"
|
||
|
case SQLTYPE = "TIME"
|
||
|
case SQLTYPE = "DOUBLE"
|
||
|
SQLTYPE = "DOUBLE PRECISION"
|
||
|
* Next 4 cases allow for commas in some SQLTYPEs
|
||
|
case SQLTYPE[1,3] = "DEC"
|
||
|
STYPE = "DEC"
|
||
|
if (NCOMMA = 1 or NCOMMA = 2) then goto CST1
|
||
|
SQLTYPE = STYPE
|
||
|
case SQLTYPE[1,7] = "NUMERIC"
|
||
|
STYPE = "NUMERIC"
|
||
|
if (NCOMMA = 1 or NCOMMA = 2) then goto CST1
|
||
|
SQLTYPE = STYPE
|
||
|
case SQLTYPE[1,5] = "FLOAT"
|
||
|
STYPE = "FLOAT"
|
||
|
if NCOMMA = 1 then goto CST1
|
||
|
SQLTYPE = STYPE
|
||
|
case SQLTYPE[1,4] = "CHAR" or SQLTYPE[1,7] = "VARCHAR"
|
||
|
if NCOMMA = 1 then
|
||
|
CSTNUM = field(SQLTYPE,',',2,99)
|
||
|
begin case
|
||
|
case not(num(CSTNUM))
|
||
|
SQLTYPE = "VARCHAR"
|
||
|
case CSTNUM < 1 or CSTNUM > 65535
|
||
|
SQLTYPE = "VARCHAR"
|
||
|
case CSTNUM < 255 and SQLTYPE[1,4] = "CHAR"
|
||
|
SQLTYPE = "CHAR(":CSTNUM:")"
|
||
|
case 1
|
||
|
SQLTYPE = "VARCHAR(":CSTNUM:")"
|
||
|
end case
|
||
|
end else
|
||
|
if SQLTYPE[1,4] = "CHAR" then
|
||
|
SQLTYPE = "CHAR"
|
||
|
end else
|
||
|
SQLTYPE = "VARCHAR"
|
||
|
end
|
||
|
end
|
||
|
case 1
|
||
|
SQLTYPE = ""
|
||
|
CSTERR = 1
|
||
|
return ; *from CONVST (SQLTYPE is bad)
|
||
|
end case
|
||
|
return ; *from CONVST (no error)
|
||
|
CST1:
|
||
|
NUM1 = field(SQLTYPE,',',2)
|
||
|
NUM2 = field(SQLTYPE,',',3)
|
||
|
if not(num(NUM1)) or NUM1 = "" then SQLTYPE = STYPE ; return
|
||
|
if NUM1 < 1 or NUM1 <> int(NUM1) then SQLTYPE = STYPE ; return
|
||
|
SQLTYPE = STYPE:"(":NUM1+0
|
||
|
if NCOMMA = 1 then SQLTYPE := ")" ; return
|
||
|
if not(num(NUM2)) or NUM2 = "" then SQLTYPE := ")" ; return
|
||
|
if NUM2 < 0 or NUM2 <> int(NUM2) then SQLTYPE := ")" ; return
|
||
|
if NUM2 > NUM1 then NUM2 = NUM1
|
||
|
if NUM2 > 9 then NUM2 = 9
|
||
|
SQLTYPE := ",":NUM2+0:")"
|
||
|
return ; *from CONVST (no error)
|
||
|
|
||
|
|
||
|
* Get the scale factor from MD, ML, or MR conversion
|
||
|
* Input: CONV contains the conversion code (already known to be MD, ML, or MR)
|
||
|
* Output: SCALEX contains the scale as a single digit in the range 0-9
|
||
|
GETSCALE:
|
||
|
if not(num(CONV[3,1])) or CONV[3,1] = "" then SCALEX = "0" ; return
|
||
|
if not(num(CONV[4,1])) or CONV[4,1] = "" then
|
||
|
SCALEX = CONV[3,1]
|
||
|
end else
|
||
|
SCALEX = CONV[4,1]
|
||
|
end
|
||
|
return ; *from GETSCALE
|
||
|
|
||
|
|
||
|
* Create column definition for field which has no dictionary definition
|
||
|
* Builds COLDEF consisting of <generated column name>
|
||
|
* <datatype>
|
||
|
* <multival>
|
||
|
*
|
||
|
* Inputs are: LOC (field location)
|
||
|
*
|
||
|
CREATENONE:
|
||
|
if LOC <= 9 then LOC2 = "0":LOC else LOC2 = LOC
|
||
|
LEGAL = "SQL_C":LOC2
|
||
|
gosub CKDUPDICT
|
||
|
* if ISDUP then VALCODE = 1 else
|
||
|
if not(ISDUP) then
|
||
|
NEWLAST += 1
|
||
|
if NEWLAST > MAXNEW then
|
||
|
print "Too many (>":MAXNEW:") newly created dictionary names"
|
||
|
goto GOODBYE
|
||
|
end
|
||
|
NEWDICT(NEWLAST) = LEGAL
|
||
|
end
|
||
|
COLDEF = LEGAL
|
||
|
COLDEF := " VARCHAR MULTIVALUED"
|
||
|
TVAR = "X":@FM:COLDEF:@FM:"1"
|
||
|
AVAR = "SQL_C":LOC2
|
||
|
write TVAR to NVAR,AVAR else goto WRERR
|
||
|
return ; *from CREATENONE
|
||
|
|
||
|
|
||
|
* Create association definition from an entry in ASSOCS
|
||
|
* Builds COLDEF consisting of <assoc name>
|
||
|
* (<colnumber> KEY,
|
||
|
* ... <colnumber>)
|
||
|
*
|
||
|
* Inputs are AIX (index into ASSOCS array)
|
||
|
* ASSOCID (association name)
|
||
|
*
|
||
|
CREATEASSOC:
|
||
|
if AIX <= 9 then AIX2 = "0":AIX else AIX2 = AIX
|
||
|
COLDEF = "("
|
||
|
if PHRASES(AIX,1) = ASSOCID then PHZ = 1 else PHZ = 0
|
||
|
OLAPERR = 0
|
||
|
PHZERR = 0
|
||
|
FIRST = 1
|
||
|
* Check consistency of this association's definition with:
|
||
|
* (a) the column-makeup of lower-numbered associations (ie, overlap), and
|
||
|
* (b) a PHrase having same name (if one exists)
|
||
|
* Set OLAPERR if there is an overlap; set PHZERR if PHrase exists and differs
|
||
|
for J = 1 to MAXC
|
||
|
if J <=9 then J2 = "0":J else J2 = J
|
||
|
if ASSOCS(AIX,J+1) = 0 then
|
||
|
if PHZ and PHRASES(AIX,J+1) <> 0 then PHZERR = 1
|
||
|
continue
|
||
|
end
|
||
|
if ASSOCS(AIX,J+1) = "K" then
|
||
|
if AIX > 1 then
|
||
|
for M = 1 to AIX-1
|
||
|
if (ASSOCS(M,J+1) # 0 and ASSOCS(M,MAXC+2) = 0) then OLAPERR = 1
|
||
|
next M
|
||
|
end
|
||
|
if PHZ and PHRASES(AIX,J+1) = 0 then PHZERR = 1
|
||
|
if FIRST then FIRST = 0 else COLDEF := ", "
|
||
|
COLDEF := J2:" KEY"
|
||
|
continue
|
||
|
end
|
||
|
if ASSOCS(AIX,J+1) = "X" then
|
||
|
if AIX > 1 then
|
||
|
for M = 1 to AIX-1
|
||
|
if (ASSOCS(M,J+1) # 0 and ASSOCS(M,MAXC+2) = 0) then OLAPERR = 1
|
||
|
next M
|
||
|
end
|
||
|
if PHZ and PHRASES(AIX,J+1) = 0 then PHZERR = 1
|
||
|
if FIRST then FIRST = 0 else COLDEF := ", "
|
||
|
COLDEF := J2
|
||
|
continue
|
||
|
end
|
||
|
next J
|
||
|
COLDEF := ")"
|
||
|
|
||
|
if PHRASES(AIX,MAXC+2) <> 0 then PHZERR = 1
|
||
|
* If this association overlaps another association, mark it in ASSOCS()
|
||
|
if OLAPERR then ASSOCS(AIX,MAXC+2) = "X"
|
||
|
|
||
|
* If there is a PHrase for this association, and it agrees with the assoc
|
||
|
* inferred from data field defs in dictionary, then order the columns in
|
||
|
* the chosen association definition according to the PHrase ordering
|
||
|
if (PHZ and not(PHZERR)) then gosub REORDER
|
||
|
if ASSOCS(AIX,MAXC+3) = "S" then COLDEF = "INSERT PRESERVING ":COLDEF
|
||
|
if ASSOCS(AIX,MAXC+3) = "U" then COLDEF = "INSERT LAST ":COLDEF
|
||
|
|
||
|
CHODEF = ASSOCID:" ":COLDEF
|
||
|
|
||
|
* If this association overlaps another association, mark it as overlapping
|
||
|
* Now write out the chosen association definition to <filename>_SQLDEF
|
||
|
*
|
||
|
TVAR = "X":@FM:CHODEF
|
||
|
if OLAPERR then TVAR := @FM:"O"
|
||
|
AVAR = "SQL_A":AIX2
|
||
|
write TVAR to NVAR,AVAR else goto WRERR
|
||
|
|
||
|
* If there is a PHrase for this association, then order the columns in the
|
||
|
* "association-synonym" according to the PHrase ordering
|
||
|
if PHZ then gosub REORDER
|
||
|
if ASSOCS(AIX,MAXC+3) = "S" then COLDEF = "INSERT PRESERVING ":COLDEF
|
||
|
if ASSOCS(AIX,MAXC+3) = "U" then COLDEF = "INSERT LAST ":COLDEF
|
||
|
|
||
|
COLDEF = ASSOCID:" ":COLDEF
|
||
|
|
||
|
* Now write the "assoc-synonym" definition to <filename>_SQLDEF
|
||
|
TVAR = "X":@FM:COLDEF
|
||
|
if ALPHID = "Z" then ALPHID = "a" else
|
||
|
ALPHID = char(seq(ALPHID)+1)
|
||
|
end
|
||
|
AVAR = "SQL_A":AIX2:ALPHID:"_":ASSOCID
|
||
|
write TVAR to NVAR,AVAR else goto WRERR
|
||
|
|
||
|
return ; *from CREATEASSOC
|
||
|
|
||
|
* Subroutine to reorder assoc according to PHrase ordering
|
||
|
REORDER:
|
||
|
COLDEF = "("
|
||
|
FIRST = 1
|
||
|
if PHRASES(AIX,MAXC+3) = 0 then goto REORD1
|
||
|
for I = 1 to PHRASES(AIX,MAXC+3)
|
||
|
for J = 1 to MAXC
|
||
|
if PHRASES(AIX,J+1) = I then
|
||
|
if J <= 9 then J2 = "0":J else J2 = J
|
||
|
if FIRST then FIRST = 0 else COLDEF := ", "
|
||
|
COLDEF := J2
|
||
|
if ASSOCS(AIX,J+1) = "K" then
|
||
|
COLDEF := " KEY"
|
||
|
end
|
||
|
goto REORD2
|
||
|
end
|
||
|
next J
|
||
|
REORD2:
|
||
|
next I
|
||
|
REORD1:
|
||
|
if PHRASES(AIX,MAXC+2) <> 0 then
|
||
|
if not(FIRST) then COLDEF := ", "
|
||
|
COLDEF := "??"
|
||
|
end
|
||
|
COLDEF := ")"
|
||
|
return ; *from REORDER
|
||
|
|
||
|
* Create association definition in CREATE TABLE format
|
||
|
* Also update column def(s) to add NOT NULL for association KEY(s)
|
||
|
* (if not already there)
|
||
|
* Also update column def(s) to add MULTIVALUED (if not already there)
|
||
|
* Output is COLDEF
|
||
|
* Inputs are: ADEF association definition with column numbers not names
|
||
|
* CRXCOL() array of positions in CRXTAB statemt of chosen columns
|
||
|
MKDEFASSOC:
|
||
|
COLDEF = field(ADEF,"(",1,1):"("
|
||
|
ADEF = trim(field(ADEF,"(",2,99))
|
||
|
if ADEF[1] = ")" then ADEF = trim(ADEF[1,len(ADEF)-1])
|
||
|
if ADEF[3] = ",??" then ADEF = trim(ADEF[1,len(ADEF)-3])
|
||
|
if ADEF[4] = ", ??" then ADEF = trim(ADEF[1,len(ADEF)-4])
|
||
|
FIRST = 1
|
||
|
loop ; until dcount(ADEF,",") < 1 do
|
||
|
CHGCOLM = 0
|
||
|
if FIRST then FIRST = 0 else COLDEF := ", "
|
||
|
A2 = field(ADEF," ",1,1) ; if A2[1] = "," then A2 = A2[1,len(A2)-1]
|
||
|
read DEF3 from NVAR,"SQL_C":A2 else goto MKDEFA1
|
||
|
if DEF3<2>[1,1] # '"' then
|
||
|
DEF321 = '"':change(field(DEF3<2>," ",1,1),'"','""'):'"'
|
||
|
end else DEF321 = field(DEF3<2>," ",1,1)
|
||
|
COLDEF := DEF321
|
||
|
DEF32 = DEF321:" ":field(DEF3<2>," ",2,1)
|
||
|
if field(DEF32," ",2,1) = "DOUBLE" then
|
||
|
DEF32 := " ":field(DEF3<2>," ",3,1)
|
||
|
DBL = 1
|
||
|
end else DBL = 0
|
||
|
MULTIV = " MULTIVALUED"
|
||
|
findstr MULTIV in DEF3<2> setting FMC else
|
||
|
DEF32 := MULTIV
|
||
|
CHGCOLM = 1
|
||
|
end
|
||
|
if field(ADEF," ",2,1)[1,3] = "KEY" then
|
||
|
COLDEF := " KEY"
|
||
|
findstr " ":NN in DEF3<2> setting FMC else
|
||
|
DEF32 := " ":NN
|
||
|
CHGCOLM = 1
|
||
|
end
|
||
|
end
|
||
|
MKDEFA1:
|
||
|
if CHGCOLM then
|
||
|
if DBL then
|
||
|
DEF32 := " ":field(DEF3<2>," ",4,99):","
|
||
|
end else
|
||
|
DEF32 := " ":field(DEF3<2>," ",3,99):","
|
||
|
end
|
||
|
CRX(CRXCOL(trim(A2,"0","L"))) = DEF32
|
||
|
end
|
||
|
ADEF = trim(field(ADEF,",",2,99))
|
||
|
repeat
|
||
|
COLDEF := ")"
|
||
|
return ; *from MKDEFASSOC
|
||
|
|
||
|
|
||
|
* Analyze FMT and generate FMTJ (justification) and FMTW (width)
|
||
|
ANALFMT:
|
||
|
if FMT # "" then
|
||
|
* put width in FMTW
|
||
|
TVAR = FMT
|
||
|
FMTW = ""
|
||
|
loop
|
||
|
until TVAR = "" or not(num(TVAR[1,1])) do
|
||
|
FMTW := TVAR[1,1]
|
||
|
TVAR = TVAR[2,len(TVAR)-1]
|
||
|
repeat
|
||
|
* If FMT doesn't specify 'width', try to get width from 'mask'
|
||
|
if FMTW = "" then
|
||
|
FMTW = '10'
|
||
|
FMT9 = fmt(change(space(254)," ","9"),FMT)
|
||
|
FMTSTAT = status()
|
||
|
if FMTSTAT = 0 then
|
||
|
if len(FMT9) < 255 then FMTW = len(FMT9)
|
||
|
end
|
||
|
end
|
||
|
if FMTW = 0 then
|
||
|
FMTW = '0'
|
||
|
end else
|
||
|
FMTW = trim(FMTW,'0','L')
|
||
|
end
|
||
|
if TVAR = "" then TVAR = "T"
|
||
|
if TVAR[1,1] = "'" then
|
||
|
* bypass quoted fill character
|
||
|
TVAR = field(TVAR,"'",3,99)
|
||
|
if len(TVAR) = 0 then TVAR = "T" ; *syntax error in FMT
|
||
|
goto FMT1
|
||
|
end
|
||
|
T1 = TVAR[1,1]
|
||
|
* bypass unquoted fill character
|
||
|
if not(T1 = 'L' or T1 = 'R' or T1 = 'T' or T1 = 'U' or T1 = 'Q') then
|
||
|
TVAR = TVAR[2,len(TVAR)-1]
|
||
|
if len(TVAR) = 0 then TVAR = "T" ; *syntax error in FMT
|
||
|
end
|
||
|
FMT1:
|
||
|
T1 = TVAR[1,1]
|
||
|
if (T1 = 'L' or T1 = 'R' or T1 = 'T' or T1 = 'U' or T1 = 'Q') then
|
||
|
FMTJ = T1
|
||
|
TVAR = TVAR[2,len(TVAR)-1]
|
||
|
if len(TVAR) > 0 then
|
||
|
if TVAR[1,1] = 'L' then FMTJ = 'QL'
|
||
|
if TVAR[1,1] = 'R' then FMTJ = 'QR'
|
||
|
end
|
||
|
end else
|
||
|
if len(TVAR) > 1 then FMTJ = TVAR[2,1] else FMTJ = 'T'
|
||
|
end
|
||
|
end else
|
||
|
FMTJ = 'T'
|
||
|
FMTW = '10'
|
||
|
end
|
||
|
return ; *from ANALFMT
|
||
|
|
||
|
|
||
|
* Put appropriate quotes (single or double) around a character string
|
||
|
* Input: USTRING contains string to be quoted
|
||
|
* Output: USTRING has been appropriately quoted
|
||
|
QSTRING:
|
||
|
DQUOTE = "'"
|
||
|
if count(USTRING,"'") = 0 then goto QSEND
|
||
|
if count(USTRING,'"') = 0 then DQUOTE = '"' ; goto QSEND
|
||
|
USTRING = change (USTRING,"'",'"',0,1)
|
||
|
QSEND:
|
||
|
USTRING = DQUOTE:USTRING:DQUOTE
|
||
|
return ; *from QSTRING
|
||
|
|
||
|
|
||
|
* Enclose the first token of a string within double-quote-marks, and
|
||
|
* replace every double-quote in that token by a pair of double-quotes,
|
||
|
* if this hasn't already been done.
|
||
|
* Input: The string to be modified is in NEEDQ
|
||
|
* Output: The modified string is put back into NEEDQ
|
||
|
QFIRST:
|
||
|
if NEEDQ[1,1] # '"' then
|
||
|
NQ1 = '"':change(field(NEEDQ," ",1),'"','""'):'"'
|
||
|
NEEDQ = fieldstore(NEEDQ," ",1,-1,NQ1)
|
||
|
end
|
||
|
return ; *from QFIRST
|
||
|
|
||
|
|
||
|
* In a phrase, recursively replace PH-tokens by their contents until no
|
||
|
* PH-tokens remain. This subroutine is called from MAKEDINFO to process
|
||
|
* @SELECT, @, and @KEY phrases. It is also called (from CKASSOCPH and
|
||
|
* from within CREATECOL) to process association phrases.
|
||
|
* NOTE: This subroutine deals with the fact that phrases may contain certain
|
||
|
* multi-token clauses such as BREAK.SUP "text" fieldname or BY.EXP fieldname.
|
||
|
* Input: DICTTEMP contains the original phrase
|
||
|
* NVAR is the file-variable for the SQLDEF file (containing dict recs)
|
||
|
* VOCOPEN is a flag indicating whether the VOC is currently open
|
||
|
* PHNAME is the name of the PHrase being expanded
|
||
|
* Output: At return, DICTTEMP contains only non-PH tokens
|
||
|
* TOKCNT contains the number of tokens (used later for array size)
|
||
|
* IDSUP = 1 if "ID.SUP" was encountered, else 0
|
||
|
* VOCOPEN will be set to 1 by this subroutine if it opens the VOC
|
||
|
EXPANDPH:
|
||
|
IDSUP = 0
|
||
|
loop
|
||
|
PHF = 0
|
||
|
DICTT2 = ""
|
||
|
TOKCNT = 0
|
||
|
DICTTEMP = trim(DICTTEMP)
|
||
|
loop
|
||
|
while DICTTEMP # "" do
|
||
|
* Skip quoted string, including embedded spaces, if it is the next "token"
|
||
|
if DICTTEMP[1,1] = '"' and count(DICTTEMP,'"') > 1 then
|
||
|
DICTTEMP = trim(field(DICTTEMP,'"',3,999))
|
||
|
end else
|
||
|
if DICTTEMP[1,1] = "'" and count(DICTTEMP,"'") > 1 then
|
||
|
DICTTEMP = trim(field(DICTTEMP,"'",3,999))
|
||
|
end
|
||
|
end
|
||
|
TOKTEMP = field(DICTTEMP," ",1)
|
||
|
DICTTEMP = field(DICTTEMP," ",2,999)
|
||
|
* Skip token if same as original phrase name (GTAR 23597)
|
||
|
if TOKTEMP = PHNAME then continue
|
||
|
read RECTEMP from NVAR,TOKTEMP then
|
||
|
if upcase(RECTEMP<1>[1,2]) = 'PH' then
|
||
|
DICTT2 := " ":trim(RECTEMP<2>)
|
||
|
PHF = 1
|
||
|
end else
|
||
|
DICTT2 := " ":TOKTEMP
|
||
|
TOKCNT += 1
|
||
|
end
|
||
|
end else
|
||
|
if TOKTEMP = "ID.SUP" then IDSUP = 1 ; goto EXP2
|
||
|
if TOKTEMP = "@ASSOC_ROW" then
|
||
|
DICTT2 := " ":TOKTEMP
|
||
|
TOKCNT += 1
|
||
|
goto EXP2
|
||
|
end
|
||
|
if not(VOCOPEN) then
|
||
|
openpath VPATH to VOCVAR else
|
||
|
gosub UCAF
|
||
|
stop "Can't open '":VPATH:"'"
|
||
|
end
|
||
|
VOCOPEN = 1
|
||
|
end
|
||
|
LSLEEP = MAXSLEEP
|
||
|
EXP1:
|
||
|
if LSLEEP <= 0 then
|
||
|
close VOCVAR
|
||
|
gosub UCAF
|
||
|
stop "Record '":TOKTEMP:"' in '":VPATH:"' is locked"
|
||
|
end
|
||
|
readl KVAR from VOCVAR,TOKTEMP locked
|
||
|
LSLEEP -= 1
|
||
|
sleep 2
|
||
|
goto EXP1
|
||
|
end then
|
||
|
release VOCVAR,TOKTEMP
|
||
|
if upcase(KVAR<1>[1,1]) = "K" then
|
||
|
KV2 = KVAR<2>
|
||
|
begin case
|
||
|
case KV2 = 12 or KV2 = 13 ; * BY or BY.DSND
|
||
|
case KV2 = 48 or KV2 = 49 ; * BY.EXP or BY.EXP.DSND
|
||
|
case KV2 = 303 ; * ASSOC.WITH
|
||
|
case KV2 = 42 ; * BREAK.SUP
|
||
|
* Skip quoted string (if present) after BREAK.SUP
|
||
|
if DICTTEMP[1,1] = '"' and count(DICTTEMP,'"') > 1 then
|
||
|
DICTTEMP = trim(field(DICTTEMP,'"',3,999))
|
||
|
end else
|
||
|
if DICTTEMP[1,1] = "'" and count(DICTTEMP,"'") > 1 then
|
||
|
DICTTEMP = trim(field(DICTTEMP,"'",3,999))
|
||
|
end
|
||
|
end
|
||
|
case KV2 = 18 ; * ID.SUP other spellings
|
||
|
IDSUP = 1
|
||
|
goto EXP2
|
||
|
case 1
|
||
|
goto EXP2
|
||
|
end case
|
||
|
* Skip fieldname after BY, BY.DSND, BY.EXP, BY.EXP.DSND,
|
||
|
* ASSOC.WITH, and BREAK.SUP (and their alternate spellings)
|
||
|
DICTTEMP = field(DICTTEMP," ",2,999)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
EXP2:
|
||
|
repeat
|
||
|
DICTTEMP = trim(DICTT2)
|
||
|
while PHF
|
||
|
repeat
|
||
|
return ; *from EXPANDPH
|
||
|
|
||
|
|
||
|
* Subroutine to show the planned CREATE EXISTING TABLE statement
|
||
|
PCRX:
|
||
|
if CRNEX < 3 then
|
||
|
print " CREATE EXISTING TABLE statement too short"
|
||
|
return
|
||
|
end
|
||
|
print CRX(1)
|
||
|
print " ":CRX(2)
|
||
|
if CRNEX = 3 then return
|
||
|
for I = 3 to CRNEX-1
|
||
|
print " ":CRX(I)
|
||
|
next I
|
||
|
return ; *from PCRX
|
||
|
|
||
|
|
||
|
* Check for legal SQL quoted identifier
|
||
|
* Input: IDENT is the identifier to be checked
|
||
|
* IDTYPE is the type of identifier (TABLE, COLUMN, or ASSOC)
|
||
|
* Output: if ILLEGAL = 0 then the identifier is OK
|
||
|
* else ILLEGAL contains a reason why the identifier is illegal (NULL,
|
||
|
* PERIOD, NTCHAR, MARKCHAR, CONTROL, SPACE, SLASHQ, or QUOTES)
|
||
|
CKLEGAL:
|
||
|
ILLEGAL = 0
|
||
|
if isnull(IDENT) then ILLEGAL = "it is SQL NULL" ; return
|
||
|
IDCH = IDENT[1,1]
|
||
|
if len(IDENT) = 1 and IDCH = @NULL.STR then
|
||
|
ILLEGAL = "it is SQL NULL"
|
||
|
return
|
||
|
end
|
||
|
if OS.TYPE = "UNIX" and IDCH = "." and IDTYPE = "TABLE" then
|
||
|
ILLEGAL = "starts with Period"
|
||
|
return
|
||
|
end
|
||
|
IDQ = ""
|
||
|
NTCHAR = "contains NT-restricted character"
|
||
|
SDQUOT = "contains both single and double Quotes"
|
||
|
MKCHAR = "contains Mark character"
|
||
|
loop ; until IDENT = "" do
|
||
|
IDCH = IDENT[1,1]
|
||
|
if OS.TYPE # "UNIX" then
|
||
|
if IDCH = '"' or IDCH = '%' or IDCH = '*' then ILLEGAL = NTCHAR
|
||
|
if IDCH = '\' or IDCH = ':' or IDCH = '<' then ILLEGAL = NTCHAR
|
||
|
if IDCH = '>' then ILLEGAL = NTCHAR
|
||
|
end
|
||
|
if seq(IDCH) >= 251 and seq(IDCH) <= 255 then ILLEGAL = MKCHAR
|
||
|
if seq(IDCH) >= 0 and seq(IDCH) <= 31 then ILLEGAL = "contains Hex 00-1F"
|
||
|
if IDCH = ' ' and IDTYPE = "COLUMN" then ILLEGAL = "contains Space"
|
||
|
if IDCH = '/' and IDTYPE = "TABLE" then ILLEGAL = "contains Slash"
|
||
|
if IDCH = '?' and IDTYPE = "TABLE" then ILLEGAL = "contains Question mark"
|
||
|
if IDCH = '"' and IDTYPE = "TABLE" then
|
||
|
if IDQ = "SINGLE" then ILLEGAL = SDQUOT
|
||
|
end else
|
||
|
IDQ = "DOUBLE"
|
||
|
end
|
||
|
if IDCH = "'" and IDTYPE = "TABLE" then
|
||
|
if IDQ = "DOUBLE" then ILLEGAL = SDQUOT
|
||
|
end else
|
||
|
IDQ = "SINGLE"
|
||
|
end
|
||
|
if ILLEGAL then return
|
||
|
IDENT = IDENT[2,len(IDENT)-1]
|
||
|
repeat
|
||
|
return ; *from CKLEGAL
|
||
|
|
||
|
* Check for duplicate entry in dictionary; generate non-dup name if necessary
|
||
|
* Input string is LEGAL (new name to be checked for uniqueness in dictionary)
|
||
|
* Output is ISDUP: if 0, then LEGAL is OK (possibly after some modification)
|
||
|
* if 1, then we can't find a valid name after many attempts
|
||
|
* and LEGAL is set to "NO_NAME"
|
||
|
CKDUPDICT:
|
||
|
gosub CDUP0
|
||
|
if ISDUP then
|
||
|
gosub CRNEW
|
||
|
if LEGAL = "" then
|
||
|
ISDUP = 1
|
||
|
LEGAL = "NO_NAME"
|
||
|
end else
|
||
|
ISDUP = 0
|
||
|
end
|
||
|
end
|
||
|
return ; *from CKDUPDICT
|
||
|
|
||
|
* Check for duplicate entry in dictionary
|
||
|
* Input string is LEGAL (new name to be checked for uniqueness in dictionary)
|
||
|
* Output is ISDUP: if 0, then LEGAL is OK
|
||
|
* if 1, then LEGAL is a duplicate
|
||
|
CDUP0:
|
||
|
ISDUP = 0
|
||
|
select DICTLIST
|
||
|
CDUP1:
|
||
|
readnext DICTID else goto CDUP2
|
||
|
if DICTID = LEGAL then goto CDUP9 else goto CDUP1
|
||
|
CDUP2:
|
||
|
if NEWLAST = 0 then return ; * LEGAL is OK, it doesn't duplicate anything
|
||
|
for N = 1 to NEWLAST
|
||
|
if NEWDICT(N) = LEGAL then goto CDUP9
|
||
|
next N
|
||
|
return ; * LEGAL is OK, it doesn't duplicate anything
|
||
|
CDUP9:
|
||
|
ISDUP = 1
|
||
|
return ; *from CDUP0 with ISDUP set to 1
|
||
|
|
||
|
* Create new unique legal name for dictionary entry
|
||
|
* Input = LEGAL (previously failed attempted new name)
|
||
|
* Output = LEGAL: if non-empty, contains new unique legal name
|
||
|
* if empty then we can't find a good name after many tries
|
||
|
CRNEW:
|
||
|
SAVLEG = LEGAL
|
||
|
if len(SAVLEG) > 16 then SAVLEG = SAVLEG[1,16]
|
||
|
for M = 1 to 9
|
||
|
LEGAL = SAVLEG:"_":char(48+M)
|
||
|
gosub CDUP0
|
||
|
if not(ISDUP) then return ; * LEGAL is now OK, ending with _1 to _9
|
||
|
next M
|
||
|
for M = 1 to 26
|
||
|
LEGAL = SAVLEG:"_":char(64+M)
|
||
|
gosub CDUP0
|
||
|
if not(ISDUP) then return ; * LEGAL is now OK, ending with _A to _Z
|
||
|
next M
|
||
|
for M = 1 to 26
|
||
|
LEGAL = SAVLEG:"_":char(96+M)
|
||
|
gosub CDUP0
|
||
|
if not(ISDUP) then return ; * LEGAL is now OK, ending with -a to -z
|
||
|
next M
|
||
|
LEGAL = ""
|
||
|
return ; *from CRNEW - can't find good name after many tries
|
||
|
|
||
|
|
||
|
* Subroutines to let user change a column or association definition
|
||
|
* Input is EDCOM (the typed-in edit command)
|
||
|
* Output is EDERR: 0 if no error
|
||
|
* 1 if command is wrong length
|
||
|
* 2 if column number is non-numeric
|
||
|
* 3 if synonym-identifier is non-alphabetic
|
||
|
* 4 if column has been deleted
|
||
|
* 5 if illegal command
|
||
|
* 6 if column-synonym is non-existent
|
||
|
* 7 if column number too large
|
||
|
* 8 if association number too large
|
||
|
* 9 if column not found (shouldn't happen)
|
||
|
* 10 if command not implemented yet
|
||
|
* 11 if key-part number is < 1
|
||
|
* 12 if assoc number is non-numeric
|
||
|
* 13 if no datatype was specified
|
||
|
* 14 if assoc has been deleted (or is overlapping)
|
||
|
* 16 if assoc-synonym is non-existent
|
||
|
* 17 if key-part number too large
|
||
|
* 18 if invalid datatype is entered
|
||
|
* 19 if assoc not found (shouldn't happen)
|
||
|
* 20 if datatype CHAR[ACTER] VARYING is entered
|
||
|
CHANGECA:
|
||
|
EDERR = 0
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
if upcase(EDCOM[1,1]) = "A" then goto CHANGEA
|
||
|
* Change column or key-part
|
||
|
if upcase(EDCOM[1,1]) = "K" then
|
||
|
EK = 1
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
end else
|
||
|
EK = 0
|
||
|
if upcase(EDCOM[1,1]) = "C" then EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
end
|
||
|
gosub GETNUM
|
||
|
if NUMERR and EK then EDERR = 11 ; return
|
||
|
if NUMERR then EDERR = 2 ; return
|
||
|
if EC2[1,1] = " " then EC2 = EC2[2,len(EC2)-1]
|
||
|
if len(EC2) < 2 and upcase(EC2) # "T" then EDERR = 1 ; return
|
||
|
EC3 = EC2[2,len(EC2)-1]
|
||
|
EC2 = EC2[1,1]
|
||
|
if EK then
|
||
|
if I2 > KEYLAST then EDERR = 17 ; return
|
||
|
if I2 < 1 then EDERR = 11 ; return
|
||
|
CREC = "SQL_K":I2
|
||
|
end else
|
||
|
if I2 > COLLAST then EDERR = 7 ; return
|
||
|
if I2 < 1 and KEYLAST > 1 then EDERR = 15 ; return
|
||
|
CREC = "SQL_C":I2
|
||
|
end
|
||
|
read CDEF from NVAR,CREC else EDERR = 9 ; return
|
||
|
DEF = CDEF<2>
|
||
|
begin case
|
||
|
case EC2 = "/" or EC2 = "\"
|
||
|
if upcase(field(EC3,EC2,3)[1,1]) = "G" and field(EC3,EC2,1) # ""
|
||
|
then NUMREP = -1
|
||
|
else NUMREP = 1
|
||
|
DEF = ereplace(DEF,field(EC3,EC2,1),field(EC3,EC2,2),NUMREP)
|
||
|
OLDNAME = field(CDEF<2>," ",1)
|
||
|
NEWNAME = field(DEF," ",1)
|
||
|
gosub REWRITESEL
|
||
|
CDEF<2> = DEF
|
||
|
write CDEF to NVAR,CREC else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
NEEDQ = DEF ; gosub QFIRST
|
||
|
if EK then print " K":I2:" ":NEEDQ else print " ":I2:" ":NEEDQ
|
||
|
case upcase(EC2) = "N"
|
||
|
if upcase(EC3[1,3]) = "AME" then EC3 = EC3[4,len(EC3)-3]
|
||
|
NEWNAME = trim(EC3)
|
||
|
DEF = fieldstore(DEF," ",1,-1,NEWNAME)
|
||
|
OLDNAME = field(CDEF<2>," ",1)
|
||
|
gosub REWRITESEL
|
||
|
CDEF<2> = DEF
|
||
|
write CDEF to NVAR,CREC else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
NEEDQ = DEF ; gosub QFIRST
|
||
|
if EK then print " K":I2:" ":NEEDQ else print " ":I2:" ":NEEDQ
|
||
|
case upcase(EC2) = "T"
|
||
|
if upcase(EC3[1,3]) = "YPE" then EC3 = EC3[4,len(EC3)-3]
|
||
|
NEWTYPE = upcase(trim(EC3))
|
||
|
TYPE2 = ""
|
||
|
TYPEFLAG = 0
|
||
|
begin case
|
||
|
case NEWTYPE = ""
|
||
|
EDERR = 13 ; return
|
||
|
case NEWTYPE = "DOUBLE PRECISION" or NEWTYPE = "REAL"
|
||
|
case NEWTYPE = "SMALLINT" or NEWTYPE = "INT" or NEWTYPE = "INTEGER"
|
||
|
case NEWTYPE = "DEC" or NEWTYPE = "DECIMAL" or NEWTYPE = "FLOAT"
|
||
|
case NEWTYPE = "NUMERIC" or NEWTYPE = "DATE" or NEWTYPE = "TIME"
|
||
|
case NEWTYPE = "CHAR" or NEWTYPE = "CHARACTER" or NEWTYPE = "VARCHAR"
|
||
|
case NEWTYPE[1,12] = "CHAR VARYING"
|
||
|
EDERR = 20 ; return
|
||
|
case NEWTYPE[1,17] = "CHARACTER VARYING"
|
||
|
EDERR = 20 ; return
|
||
|
case NEWTYPE[1,5] = "FLOAT"
|
||
|
TYPE2 = change(NEWTYPE[6,len(NEWTYPE)-5]," ","")
|
||
|
NEWTYPE = "FLOAT"
|
||
|
case NEWTYPE[1,7] = "VARCHAR"
|
||
|
TYPE2 = change(NEWTYPE[8,len(NEWTYPE)-7]," ","")
|
||
|
NEWTYPE = "VARCHAR"
|
||
|
case NEWTYPE[1,9] = "CHARACTER"
|
||
|
TYPE2 = change(NEWTYPE[10,len(NEWTYPE)-9]," ","")
|
||
|
NEWTYPE = "CHARACTER"
|
||
|
case NEWTYPE[1,4] = "CHAR"
|
||
|
TYPE2 = change(NEWTYPE[5,len(NEWTYPE)-4]," ","")
|
||
|
NEWTYPE = "CHAR"
|
||
|
case NEWTYPE[1,7] = "NUMERIC"
|
||
|
TYPE2 = change(NEWTYPE[8,len(NEWTYPE)-7]," ","")
|
||
|
NEWTYPE = "NUMERIC"
|
||
|
TYPEFLAG = 1
|
||
|
case NEWTYPE[1,7] = "DECIMAL"
|
||
|
TYPE2 = change(NEWTYPE[8,len(NEWTYPE)-7]," ","")
|
||
|
NEWTYPE = "DECIMAL"
|
||
|
TYPEFLAG = 1
|
||
|
case NEWTYPE[1,3] = "DEC"
|
||
|
TYPE2 = change(NEWTYPE[4,len(NEWTYPE)-3]," ","")
|
||
|
NEWTYPE = "DEC"
|
||
|
TYPEFLAG = 1
|
||
|
case 1
|
||
|
EDERR = 18 ; return
|
||
|
end case
|
||
|
if TYPE2 # "" then
|
||
|
if TYPE2[1,1] # "(" or TYPE2[1] # ")" then EDERR = 18 ; return
|
||
|
begin case
|
||
|
case count(TYPE2,",") > 1
|
||
|
EDERR = 18 ; return
|
||
|
case count(TYPE2,",") = 1 ; * DEC or NUMERIC with precision & scale
|
||
|
if TYPEFLAG = 0 then EDERR = 18 ; return
|
||
|
SBNUM = field(TYPE2,",",1)[2,99]
|
||
|
gosub TESTNUM
|
||
|
if NOTNUM then EDERR = 18 ; return
|
||
|
if SBNUM < 1 then EDERR = 18 ; return
|
||
|
PRECNUM = SBNUM
|
||
|
SBNUM = field(TYPE2,",",2)
|
||
|
SBNUM = SBNUM[1,len(SBNUM)-1]
|
||
|
gosub TESTNUM
|
||
|
if NOTNUM then EDERR = 18 ; return
|
||
|
if SBNUM > 9 or SBNUM > PRECNUM then EDERR = 18 ; return
|
||
|
NEWTYPE = NEWTYPE:TYPE2
|
||
|
case 1 ; * DEC, NUMERIC, FLOAT with prec; CHAR, VARCHAR with size
|
||
|
SBNUM = TYPE2[2,len(TYPE2)-2]
|
||
|
gosub TESTNUM
|
||
|
if NOTNUM then EDERR = 18 ; return
|
||
|
if SBNUM < 1 then EDERR = 18 ; return
|
||
|
if NEWTYPE[1,1] = "C" and SBNUM > 254 then EDERR = 18 ; return
|
||
|
if NEWTYPE[1,1] = "V" and SBNUM > 65535 then EDERR = 18 ; return
|
||
|
NEWTYPE = NEWTYPE:TYPE2
|
||
|
end case
|
||
|
end
|
||
|
if field(DEF," ",3) = "PRECISION" then
|
||
|
DEF = fieldstore(DEF," ",2,-2,NEWTYPE)
|
||
|
end else
|
||
|
DEF = fieldstore(DEF," ",2,-1,NEWTYPE)
|
||
|
end
|
||
|
CDEF<2> = DEF
|
||
|
write CDEF to NVAR,CREC else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
NEEDQ = DEF ; gosub QFIRST
|
||
|
if EK then print " K":I2:" ":NEEDQ else print " ":I2:" ":NEEDQ
|
||
|
case upcase(EC2) = "V" ; * Future possibility to change SM (valuedness)
|
||
|
EDERR = 10
|
||
|
case upcase(EC2) = "F" ; * Future possibility to change FMT
|
||
|
EDERR = 10
|
||
|
case upcase(EC2) = "H" ; * Future possibility to change COL.HDG
|
||
|
EDERR = 10
|
||
|
case upcase(EC2) = "C" ; * Future possibility to change CONV
|
||
|
EDERR = 10
|
||
|
case 1
|
||
|
EDERR = 5
|
||
|
end case
|
||
|
return ; *from CHANGECA after changing a column definition
|
||
|
|
||
|
TESTNUM:
|
||
|
NOTNUM = 0
|
||
|
WKNUM = SBNUM
|
||
|
if WKNUM = "" then NOTNUM = 1 ; return
|
||
|
loop
|
||
|
until WKNUM = "" or NOTNUM
|
||
|
if not(num(WKNUM[1,1])) then NOTNUM = 1
|
||
|
WKNUM = WKNUM[2,99]
|
||
|
repeat
|
||
|
return ; *from TESTNUM
|
||
|
|
||
|
CHANGEA:
|
||
|
* Change association
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
gosub GETNUM
|
||
|
if NUMERR then EDERR = 12 ; return
|
||
|
if EC2[1,1] = " " then EC2 = EC2[2,len(EC2)-1]
|
||
|
if len(EC2) < 2 then EDERR = 1 ; return
|
||
|
EC3 = EC2[2,len(EC2)-1]
|
||
|
EC2 = EC2[1,1]
|
||
|
begin case
|
||
|
case EC2 = "/" or EC2 = "\"
|
||
|
if I2 > ASSOCLAST then EDERR = 8 ; return
|
||
|
if I2 < 1 then EDERR = 12 ; return
|
||
|
AREC = "SQL_A":I2
|
||
|
read ADEF from NVAR,AREC else EDERR = 19 ; return
|
||
|
if upcase(ADEF<3>) = "D" then EDERR = 14 ; return
|
||
|
DEF = ADEF<2>
|
||
|
if upcase(field(EC3,EC2,3)[1,1]) = "G" and field(EC3,EC2,1) # ""
|
||
|
then NUMREP = -1
|
||
|
else NUMREP = 1
|
||
|
DEF = ereplace(DEF,field(EC3,EC2,1),field(EC3,EC2,2),NUMREP)
|
||
|
ADEF<2> = DEF
|
||
|
write ADEF to NVAR,AREC else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
NEEDQ = DEF ; gosub QFIRST
|
||
|
print " ":I2:" ":NEEDQ
|
||
|
case upcase(EC2) = "N"
|
||
|
EDERR = 10
|
||
|
case upcase(EC2) = "K"
|
||
|
EDERR = 10
|
||
|
case 1
|
||
|
EDERR = 5
|
||
|
end case
|
||
|
return ; *from CHANGECA after changing an assoc definition
|
||
|
|
||
|
* Delete a column or association definition
|
||
|
DELETECA:
|
||
|
EDERR = 0
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
if upcase(EDCOM[1,1]) = "A" then goto DELETEA
|
||
|
* Delete column -- mark it "deleted" in field 3
|
||
|
******* The following line of code was added 5/22/97 ************************
|
||
|
EDERR = 5 ; return
|
||
|
******* The following code is no longer used (as of 5/22/97) but ************
|
||
|
******* I am reluctant to delete it just yet ******************************
|
||
|
if upcase(EDCOM[1,1]) = "C" then EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
gosub GETNUM
|
||
|
if NUMERR then EDERR = 2 ; return
|
||
|
if EC2[1,1] = " " then EC2 = EC2[2,len(EC2)-1]
|
||
|
if len(EC2) <> 1 then EDERR = 1 ; return
|
||
|
if I2 > COLLAST then EDERR = 7 ; return
|
||
|
if not(alpha(EC2)) then EDERR = 3 ; return
|
||
|
SELAC = 'SELECT ':DEFNM:' WITH @ID LIKE "SQL_C':I2
|
||
|
SELAC := "'":EC2:"'..."
|
||
|
SELAC := '"'
|
||
|
execute SELAC capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
readnext SQLID else EDERR = 6 ; return
|
||
|
read CDEF from NVAR,SQLID else EDERR = 9 ; return
|
||
|
if upcase(CDEF<3>) = "D" then EDERR = 4 ; return
|
||
|
CDEF<3> = "D"
|
||
|
write CDEF to NVAR,SQLID else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
print " Column-synonym deleted"
|
||
|
return ; *from DELETECA after deleting a column
|
||
|
******* The above code is no longer used (as of 5/22/97) but ****************
|
||
|
******* I am reluctant to delete it just yet ******************************
|
||
|
* Delete an association entirely -- mark it deleted in field 3
|
||
|
DELETEA:
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
gosub GETNUM
|
||
|
if NUMERR then EDERR = 12 ; return
|
||
|
if len(EC2) <> 0 then EDERR = 1 ; return
|
||
|
if I2 > ASSOCLAST then EDERR = 8 ; return
|
||
|
if I2 < 1 then EDERR = 12 ; return
|
||
|
AREC = "SQL_A":I2
|
||
|
read ADEF from NVAR,AREC else EDERR = 19 ; return
|
||
|
if upcase(ADEF<3>) = "D" then EDERR = 14 ; return
|
||
|
ADEF<3> = "D"
|
||
|
write ADEF to NVAR,AREC else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
print " Association deleted"
|
||
|
return ; *from DELETECA after deleting an association
|
||
|
|
||
|
* Use another column or association definition
|
||
|
USECA:
|
||
|
EDERR = 0
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
if upcase(EDCOM[1,1]) = "A" then goto USEA
|
||
|
* Use other definition for the "chosen" column
|
||
|
EK = 0 ; * this is set in case we call subroutine REWRITESEL
|
||
|
if upcase(EDCOM[1,1]) = "C" then EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
gosub GETNUM
|
||
|
if NUMERR then EDERR = 2 ; return
|
||
|
if EC2[1,1] = " " then EC2 = EC2[2,len(EC2)-1]
|
||
|
if len(EC2) <> 1 then EDERR = 1 ; return
|
||
|
if I2 > COLLAST then EDERR = 7 ; return
|
||
|
if not(alpha(EC2)) then EDERR = 3 ; return
|
||
|
SELAC = 'SELECT ':DEFNM:' WITH @ID LIKE "SQL_C':I2
|
||
|
SELAC := "'":EC2:"'..."
|
||
|
SELAC := '"'
|
||
|
execute SELAC capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
readnext SQLID else EDERR = 6 ; return
|
||
|
read CDEF from NVAR,SQLID else EDERR = 9 ; return
|
||
|
if upcase(CDEF<3>) = "D" then EDERR = 4 ; return
|
||
|
read TVAR from NVAR,"SQL_C":I2 else EDERR = 9 ; return
|
||
|
OLDNAME = field(TVAR<2>," ",1)
|
||
|
NEWNAME = field(CDEF<2>," ",1)
|
||
|
gosub REWRITESEL
|
||
|
write CDEF to NVAR,"SQL_C":I2 else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
NEEDQ = CDEF<2> ; gosub QFIRST
|
||
|
print " ":I2:" ":NEEDQ
|
||
|
return ; *from USECA after setting column definition to Use another def
|
||
|
USEA:
|
||
|
* Use other definition for the "chosen" association
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
gosub GETNUM
|
||
|
if NUMERR then EDERR = 12 ; return
|
||
|
if EC2[1,1] = " " then EC2 = EC2[2,len(EC2)-1]
|
||
|
if len(EC2) <> 1 then EDERR = 1 ; return
|
||
|
if I2 > ASSOCLAST then EDERR = 7 ; return
|
||
|
if I2 < 1 then EDERR = 12 ; return
|
||
|
if not(alpha(EC2)) then EDERR = 3 ; return
|
||
|
SELAC = 'SELECT ':DEFNM:' WITH @ID LIKE "SQL_A':I2
|
||
|
SELAC := "'":EC2:"'..."
|
||
|
SELAC := '"'
|
||
|
execute SELAC capturing TVAR
|
||
|
if DEBUG then print change(TVAR,char(254),char(10),0,1)
|
||
|
readnext SQLID else EDERR = 16 ; return
|
||
|
read ADEF from NVAR,SQLID else EDERR = 19 ; return
|
||
|
if upcase(ADEF<3>) = "D" then EDERR = 14 ; return
|
||
|
if upcase(ADEF<3>) = "O" then EDERR = 14 ; return
|
||
|
write ADEF to NVAR,"SQL_A":I2 else goto WRERR
|
||
|
gosub NEWDSTAMP
|
||
|
NEEDQ = ADEF<2> ; gosub QFIRST
|
||
|
print " ":I2:" ":NEEDQ
|
||
|
return ; *from USECA after setting assoc definition to Use another def
|
||
|
|
||
|
* Redisplay one column or association definition
|
||
|
DISPCA:
|
||
|
EDERR = 0
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
if upcase(EDCOM[1,1]) = "A" then goto DISPA
|
||
|
* Redisplay column or key-part def
|
||
|
if upcase(EDCOM[1,1]) = "K" then
|
||
|
EK = 1
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
end else
|
||
|
EK = 0
|
||
|
if upcase(EDCOM[1,1]) = "C" then EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
end
|
||
|
gosub GETNUM
|
||
|
if NUMERR and EK then EDERR = 11 ; return
|
||
|
if NUMERR then EDERR = 2 ; return
|
||
|
if len(EC2) <> 0 then EDERR = 1 ; return
|
||
|
if EK then
|
||
|
if I2 > KEYLAST then EDERR = 17 ; return
|
||
|
if I2 < 1 then EDERR = 11 ; return
|
||
|
print "Column K":I2:":"
|
||
|
read DEF1 from NVAR, "SQL_K":I2 else EDERR = 9 ; return
|
||
|
NEEDQ = DEF1<2> ; gosub QFIRST
|
||
|
print " K":I2:" ":NEEDQ
|
||
|
goto DISPENDC
|
||
|
end
|
||
|
if I2 > COLLAST then EDERR = 7 ; return
|
||
|
if I2 < 1 and KEYLAST > 1 then EDERR = 15 ; return
|
||
|
print "Column ":I2:":"
|
||
|
read DEF1 from NVAR, "SQL_C":I2 else EDERR = 9 ; return
|
||
|
NEEDQ = DEF1<2> ; gosub QFIRST
|
||
|
print " ":I2:" ":NEEDQ
|
||
|
AC = "C" ; gosub BUILDSELAC
|
||
|
DISPRNC:
|
||
|
readnext SQLID else goto DISPENDC
|
||
|
read DEF2 from NVAR, SQLID else EDERR = 9 ; return
|
||
|
if upcase(DEF2<3>) = "D" then goto DISPRNC
|
||
|
ALPHID = SQLID[8,1]
|
||
|
if num(ALPHID) then ALPHID = SQLID[9,1]
|
||
|
if field(trim(DEF2<2>)," ",1,1) <> field(trim(DEF1<2>)," ",1,1) then
|
||
|
NEEDQ = DEF2<2> ; gosub QFIRST
|
||
|
print " ":I2:ALPHID:" ":NEEDQ
|
||
|
end
|
||
|
goto DISPRNC
|
||
|
DISPENDC:
|
||
|
return ; *from DISPCA after Redisplaying a column
|
||
|
* Redisplay assoc def
|
||
|
DISPA:
|
||
|
EDCOM = EDCOM[2,len(EDCOM)-1]
|
||
|
gosub GETNUM
|
||
|
if NUMERR then EDERR = 12 ; return
|
||
|
if len(EC2) <> 0 then EDERR = 1 ; return
|
||
|
if I2 > ASSOCLAST then EDERR = 8 ; return
|
||
|
if I2 < 1 then EDERR = 12 ; return
|
||
|
read DEF1 from NVAR,"SQL_A":I2 else EDERR = 19
|
||
|
if upcase(DEF1<3>) = "D" then EDERR = 14 ; return
|
||
|
if upcase(DEF1<3>) = "O" then EDERR = 14 ; return
|
||
|
print "Association ":I2:":"
|
||
|
NEEDQ = DEF1<2> ; gosub QFIRST
|
||
|
print " ":I2:" ":NEEDQ
|
||
|
AC = "A" ; gosub BUILDSELAC
|
||
|
DISPRNA:
|
||
|
readnext SQLID else goto DISPENDA
|
||
|
read DEF2 from NVAR,SQLID else EDERR = 19 ; return
|
||
|
if upcase(DEF2<3>) = "O" then OLAP = 1 else OLAP = 0
|
||
|
ALPHID = SQLID[8,1]
|
||
|
if (DEF2<2> <> DEF1<2>) then
|
||
|
NEEDQ = DEF2<2> ; gosub QFIRST
|
||
|
print " ":I2:ALPHID:" ":NEEDQ:
|
||
|
if OLAP then print space(6):"Overlapping association not used" else
|
||
|
print space(6):"PHrase differs from ASSOC"
|
||
|
end
|
||
|
end
|
||
|
goto DISPRNA
|
||
|
DISPENDA:
|
||
|
return ; *from DISPCA after Redisplaying an association
|
||
|
|
||
|
|
||
|
* Rewrite the @SELECT phrase (SQL_SELECT record in SQLDEF file) if it exists,
|
||
|
* replacing (every occurence of) a specified old column name by a specified
|
||
|
* new column name if the new name is different. This subroutine is called from
|
||
|
* the following editing operations: Use-column-synonym, Change-column-def, and
|
||
|
* Change-column-name.
|
||
|
* Inputs: OLDNAME is previous name of the column being changed
|
||
|
* NEWNAME is the new name of the column being changed
|
||
|
* EK = 1 if the column is a Key-part, EK = 0 otherwise
|
||
|
* Outputs: The effect of this subroutine is to rewrite the SQL_SELECT record
|
||
|
* (if there is one) in the SQLDEF file. Also, variables USESELECT
|
||
|
* and NEEDATKEY are changed if appropriate, and if so the SQL_INFO
|
||
|
* record is rewritten with updated values for USESELECT and NEEDATKEY.
|
||
|
* Also, OLDNAME and NEWNAME may be modified: this subroutine unquotes
|
||
|
* OLDNAME and NEWNAME if they are delimited, since the @SELECT phrase
|
||
|
* must not contain delimited identifiers.
|
||
|
REWRITESEL:
|
||
|
if OLDNAME[1,1] = '"' and OLDNAME[1] = '"' and len(OLDNAME) > 2 then
|
||
|
OLDNAME = OLDNAME[2,len(OLDNAME)-2]
|
||
|
OLDNAME = change(OLDNAME,'""','"')
|
||
|
end
|
||
|
if NEWNAME[1,1] = '"' and NEWNAME[1] = '"' and len(NEWNAME) > 2 then
|
||
|
NEWNAME = NEWNAME[2,len(NEWNAME)-2]
|
||
|
NEWNAME = change(NEWNAME,'""','"')
|
||
|
end
|
||
|
if NEWNAME = OLDNAME then return ; *from REWRITESEL if name didn't change
|
||
|
read SELREC from NVAR,"SQL_SELECT" then
|
||
|
if upcase(SELREC<1>[1,1]) # "X" then return
|
||
|
TVAR = ""
|
||
|
SELREC = trim(SELREC)
|
||
|
loop
|
||
|
while SELREC # "" do
|
||
|
TOKTEMP = field(SELREC," ",1)
|
||
|
SELREC = field(SELREC," ",2,999)
|
||
|
if TOKTEMP = OLDNAME then
|
||
|
TVAR := " ":NEWNAME
|
||
|
end else
|
||
|
TVAR := " ":TOKTEMP
|
||
|
end
|
||
|
repeat
|
||
|
write trim(TVAR) to NVAR,"SQL_SELECT" else goto WRERR
|
||
|
* Rewrite SQL_INFO record if necessary to force new SQL_SELECT to be used
|
||
|
* and/or, if the name being changed is a Key-part name, to force @KEY
|
||
|
* to be re-generated in the dictionary by CREATE EXISTING TABLE; this
|
||
|
* is accomplished by setting USESELECT = "1" and/or NEEDATKEY = "1",
|
||
|
* respectively.
|
||
|
if USESELECT = "0" or (EK and NEEDATKEY = "0") then
|
||
|
read INFREC from NVAR,"SQL_INFO" then
|
||
|
if upcase(INFREC<1>[1,1]) # "X" then return
|
||
|
USESELECT = "1"; INFREC<10> = USESELECT
|
||
|
if EK then NEEDATKEY = "1" ; INFREC<11> = NEEDATKEY
|
||
|
write INFREC to NVAR,"SQL_INFO" else goto WRERR
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return ; *from REWRITESEL after rewriting SQL_SELECT (and perhaps SQL_INFO)
|
||
|
|
||
|
|
||
|
***** GETNUM Subroutine to find column, key-part, or association number in
|
||
|
* an Editing command (C, D, U, or R)
|
||
|
* Input: EDCOM contains string of which the first part should be a number
|
||
|
* Outputs: NUMERR = 0 if a valid non-negative integer was found; in this case,
|
||
|
* I2 will contain the actual integer as 2 or more digits in
|
||
|
* the form required for reading from SQLDEF (eg, 00, 01,
|
||
|
* ... 09, 10, ..., 99, 100, ...). Also,
|
||
|
* EC2 will contain the remainder of the original string after
|
||
|
* the numeric part was removed (EC2 may contain empty)
|
||
|
* NUMERR = 1 if no number was found at beginning of string
|
||
|
* NUMERR = 2 if a number was found but it's invalid (eg negative,
|
||
|
* fractional, or exponential)
|
||
|
GETNUM:
|
||
|
NUMERR = 0
|
||
|
EC2 = ""
|
||
|
I2 = EDCOM
|
||
|
loop
|
||
|
until num(I2) do
|
||
|
EC2 = I2[1]:EC2
|
||
|
I2 = I2[1,len(I2)-1]
|
||
|
repeat
|
||
|
if I2 = "" then NUMERR = 1 ; return
|
||
|
I2 = trim(I2," ","T")
|
||
|
if int(I2) <> I2 or I2 < 0 then NUMERR = 2 ; return
|
||
|
if count(I2,".") > 0 then NUMERR = 2 ; return
|
||
|
if count(I2,"+") > 0 then NUMERR = 2 ; return
|
||
|
if count(I2,"-") > 0 then NUMERR = 2 ; return
|
||
|
if count(I2,"E") > 0 then NUMERR = 2 ; return
|
||
|
if count(I2,"e") > 0 then NUMERR = 2 ; return
|
||
|
if I2 = 0 then I2 = "00" ; return
|
||
|
I2 = trim(I2,"0","L")
|
||
|
if I2 <= 9 then I2 = "0":I2
|
||
|
return ; *from GETNUM
|
||
|
|
||
|
|
||
|
* Subroutine to check validity of entire set of col and assoc definitions
|
||
|
MAKEVFLAG:
|
||
|
return ; **??temporary until this routine is implemented
|
||
|
|
||
|
* Open the SQLDEF file to NVAR, and its dict to DNVAR, then filelock both
|
||
|
OPENDEF:
|
||
|
open DEFNM to NVAR else
|
||
|
OPSTAT = status()
|
||
|
print "Can't open '":DEFNM:"'. ":
|
||
|
gosub POPERR
|
||
|
if ARGA[1,7] # "RESTORE" then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
end
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
|
||
|
filelock NVAR locked
|
||
|
print "Can't filelock ":DEFNM
|
||
|
if ARGA[1,7] # "RESTORE" then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
end
|
||
|
close NVAR
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
|
||
|
open "DICT",DEFNM to DNVAR else
|
||
|
OPSTAT = status()
|
||
|
print "Can't open 'DICT ":DEFNM:"'. ":
|
||
|
gosub POPERR
|
||
|
if ARGA[1,7] # "RESTORE" then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
end
|
||
|
fileunlock NVAR
|
||
|
close NVAR
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
|
||
|
filelock DNVAR locked
|
||
|
print "Can't filelock DICT ":DEFNM
|
||
|
if ARGA[1,7] # "RESTORE" then
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
end
|
||
|
fileunlock NVAR
|
||
|
close NVAR
|
||
|
close DNVAR
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
OPENERR = 0
|
||
|
return ; *from OPENDEF
|
||
|
|
||
|
* Open the SQLSAVE file to VVAR, and its dict to DVVAR, then filelock both
|
||
|
OPENSAVE:
|
||
|
open SAVNM to VVAR else
|
||
|
OPSTAT = status()
|
||
|
print "Can't open '":SAVNM:"'. ":
|
||
|
gosub POPERR
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
|
||
|
filelock VVAR locked
|
||
|
print "Can't filelock ":SAVNM
|
||
|
close VVAR
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
|
||
|
open "DICT",SAVNM to DVVAR else
|
||
|
OPSTAT = status()
|
||
|
print "Can't open 'DICT ":SAVNM:"'. ":
|
||
|
gosub POPERR
|
||
|
fileunlock VVAR
|
||
|
close VVAR
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
|
||
|
filelock DVVAR locked
|
||
|
print "Can't filelock DICT ":SAVNM
|
||
|
fileunlock VVAR
|
||
|
close VVAR
|
||
|
close DVVAR
|
||
|
OPENERR = 1
|
||
|
return
|
||
|
end
|
||
|
OPENERR = 0
|
||
|
return ; *from OPENSAVE
|
||
|
|
||
|
* Print message for error occurring during interactive editing
|
||
|
* input = EDERR, which contains error code
|
||
|
PEDERR:
|
||
|
print " Operation not done. ":
|
||
|
begin case
|
||
|
case EDERR = 1
|
||
|
print "Command is wrong length"
|
||
|
case EDERR = 2
|
||
|
print "Invalid or missing column number"
|
||
|
case EDERR = 3
|
||
|
print "Synonym-identifier must be alphabetic"
|
||
|
case EDERR = 4
|
||
|
print "Specified synonym has been deleted"
|
||
|
case EDERR = 5
|
||
|
print "Illegal command"
|
||
|
case EDERR = 6
|
||
|
print "Specified synonym doesn't exist"
|
||
|
case EDERR = 7
|
||
|
print "Specified column number too large"
|
||
|
case EDERR = 8
|
||
|
print "Specified association number too large"
|
||
|
case EDERR = 9
|
||
|
print "Column definition not found"
|
||
|
case EDERR = 10
|
||
|
print "Command not yet implemented"
|
||
|
case EDERR = 11
|
||
|
print "Invalid or missing key-part number"
|
||
|
case EDERR = 12
|
||
|
print "Invalid or missing association number"
|
||
|
case EDERR = 13
|
||
|
print "No datatype specified"
|
||
|
case EDERR = 14
|
||
|
print "Specified association has been deleted or is overlapping"
|
||
|
case EDERR = 15
|
||
|
print "Column 0 is invalid; specify a Key-part"
|
||
|
case EDERR = 16
|
||
|
print "Specified association doesn't exist"
|
||
|
case EDERR = 17
|
||
|
print "Specified key-part number too large"
|
||
|
case EDERR = 18
|
||
|
print "Invalid datatype"
|
||
|
case EDERR = 19
|
||
|
print "Association definition not found"
|
||
|
case EDERR = 20
|
||
|
print "Use VARCHAR, not CHAR VARYING"
|
||
|
case 1
|
||
|
print "Error code ":EDERR
|
||
|
end case
|
||
|
return ; *from PEDERR
|
||
|
|
||
|
* Print error message when unable to open file (partial msg is already printed)
|
||
|
* input = OPSTAT, which contains status()
|
||
|
POPERR:
|
||
|
begin case
|
||
|
case OPSTAT = -1
|
||
|
print "Filename not in VOC"
|
||
|
case OPSTAT = -2
|
||
|
print "Non-existent or inaccessible file"
|
||
|
case OPSTAT = -3
|
||
|
print "No UNIX permission"
|
||
|
case OPSTAT = -4
|
||
|
print "No UNIX permission or invalid file"
|
||
|
case OPSTAT = -5
|
||
|
print "UNIX read error"
|
||
|
case OPSTAT = -6
|
||
|
print "Can't lock file header"
|
||
|
case OPSTAT = -7
|
||
|
print "Bad file rev or byte-swap"
|
||
|
case OPSTAT = -8
|
||
|
print "Invalid part-file info"
|
||
|
case OPSTAT = -9
|
||
|
print "Invalid type 30 info"
|
||
|
case OPSTAT = -10
|
||
|
print "File marked inconsistent"
|
||
|
case OPSTAT = -11
|
||
|
print "SQL view"
|
||
|
case OPSTAT = -12
|
||
|
print "No SQL permission"
|
||
|
case OPSTAT = -13
|
||
|
print "Problem with index"
|
||
|
case OPSTAT = -14
|
||
|
print "Can't open NFS file"
|
||
|
case OPSTAT = -987
|
||
|
print "Bad Q-pointer"
|
||
|
case 1
|
||
|
print "Status = ":OPSTAT
|
||
|
end case
|
||
|
return ; *from POPERR
|
||
|
|
||
|
|
||
|
* Unlock and Close all files
|
||
|
UCAF:
|
||
|
fileunlock DVAR
|
||
|
close DVAR
|
||
|
fileunlock DNVAR
|
||
|
close DNVAR
|
||
|
fileunlock NVAR
|
||
|
close NVAR
|
||
|
return ; *from UCAF
|
||
|
|
||
|
DELERR:
|
||
|
stop "Can't delete record"
|
||
|
*??
|
||
|
|
||
|
WRERR:
|
||
|
stop "Can't write record"
|
||
|
* ??
|
||
|
|
||
|
RDERR:
|
||
|
stop "Can't read record"
|
||
|
* ??
|
||
|
|
||
|
* Allegedly, this BADERR error exit should never be invoked
|
||
|
BADERR:
|
||
|
stop "BADERR: code = ":BERX
|
||
|
* ??
|
||
|
|
||
|
SELERR:
|
||
|
stop "SELERR"
|
||
|
* ??
|
||
|
|
||
|
Getschinfo:
|
||
|
* This subroutine determines whether the current account is a schema, and
|
||
|
* if so it returns the schema name. This routine doesn't use @SCHEMA
|
||
|
* because, unfortunately, @SCHEMA doesn't get updated until a user logs
|
||
|
* out of uniVerse and back in again, so @SCHEMA isn't always reliable.
|
||
|
* inputs are PATH is the full pathname of the account
|
||
|
* outputs are SCH = schema name if this is a schema, else empty string
|
||
|
*
|
||
|
* Open UV_SCHEMA
|
||
|
hush on setting OLDHUSH
|
||
|
open "UV_SCHEMA" to FVAR else
|
||
|
hush OLDHUSH
|
||
|
OPSTAT = status()
|
||
|
print "Can't open UV_SCHEMA. ":
|
||
|
gosub POPERR
|
||
|
stop
|
||
|
end
|
||
|
hush OLDHUSH
|
||
|
|
||
|
* Lock UV_SCHEMA
|
||
|
LSLEEP = MAXSLEEP
|
||
|
Getsin1:
|
||
|
if LSLEEP <=0 then
|
||
|
close FVAR
|
||
|
stop "Can't lock UV_SCHEMA."
|
||
|
end
|
||
|
filelock FVAR locked
|
||
|
LSLEEP -= 1
|
||
|
sleep 2
|
||
|
goto Getsin1
|
||
|
end
|
||
|
|
||
|
* Search UV_SCHEMA for path of this account
|
||
|
select FVAR
|
||
|
SCH = ""
|
||
|
FOUND = 0
|
||
|
NOMORE = 0
|
||
|
loop
|
||
|
until (FOUND or NOMORE) do
|
||
|
readnext SCH then
|
||
|
read SCHREC from FVAR,SCH then
|
||
|
if IS.EQPATHS(trim(SCHREC<2>), PATH) then FOUND = 1
|
||
|
end else
|
||
|
fileunlock FVAR
|
||
|
close FVAR
|
||
|
stop "Can't read UV_SCHEMA."
|
||
|
end
|
||
|
end else NOMORE = 1
|
||
|
repeat
|
||
|
fileunlock FVAR
|
||
|
close FVAR
|
||
|
return ; * return from Getschinfo
|
||
|
|
||
|
NORETURN:
|
||
|
stop "Invalid system return code from sdml"
|
||
|
end
|