DROP PROCEDURE psp_database_version ?
DROP PROCEDURE psp_dbnames ?
DROP PROCEDURE psp_columns ?
DROP PROCEDURE psp_column_attributes ?
DROP PROCEDURE psp_column_rights ?
DROP PROCEDURE psp_fkeys ?
DROP PROCEDURE psp_groups ?
DROP PROCEDURE psp_help_sp ?
DROP PROCEDURE psp_help_trigger ?
DROP PROCEDURE psp_help_udf ?
DROP PROCEDURE psp_help_view ?
DROP PROCEDURE psp_indexes ?
DROP PROCEDURE psp_pkeys ?
DROP PROCEDURE psp_stored_procedures ?
DROP PROCEDURE psp_tables ?
DROP PROCEDURE psp_table_rights ?
DROP PROCEDURE psp_triggers ?
DROP PROCEDURE psp_udfs ?
DROP PROCEDURE psp_users ?
DROP PROCEDURE psp_views ?
DROP PROCEDURE psp_rename ?
DROP PROCEDURE psp_view_rights ?
DROP PROCEDURE psp_procedure_rights ?

DROP PROCEDURE psp_dumpTable ?
DROP PROCEDURE psp_dumpIndexOnTable ?
DROP PROCEDURE psp_dumpPKOnTable ?
DROP PROCEDURE psp_dumpPK2OnTable ?
DROP PROCEDURE psp_dumpFKOnTable ?
DROP PROCEDURE psp_dumpView ?
DROP PROCEDURE psp_dumpTables ?
DROP PROCEDURE psp_dumpViews ?
DROP PROCEDURE psp_dumpProcedures ?
DROP PROCEDURE psp_dumpTriggers ?
DROP PROCEDURE psp_dumpUsers ?
DROP PROCEDURE psp_dumpUserRights ?
DROP PROCEDURE psp_dumpTabColRights ?
DROP PROCEDURE psp_dumpTabColRights2 ?
DROP PROCEDURE psp_execDumpTables ?
DROP PROCEDURE psp_execDumpViews ?
DROP PROCEDURE psp_execDumpProcedures ?
DROP PROCEDURE psp_execDumpTriggers ?
DROP PROCEDURE psp_execDumpUsers ?
DROP PROCEDURE psp_execDumpUserRights ?
DROP PROCEDURE psp_execDumpTabColRights ?
DROP PROCEDURE psp_dumpDB ?
DROP PROCEDURE psp_execDumpDB ?
DROP PROCEDURE psp_verifyDumpColumnsEtc ?
DROP PROCEDURE psp_verifyDumpFKeys ?
DROP PROCEDURE psp_verifyDumpFKRules ?
DROP PROCEDURE psp_verifyDumpIndexes ?
DROP PROCEDURE psp_verifyDumpPKeys ?
DROP PROCEDURE psp_verifyDumpProcEtc ?
DROP PROCEDURE psp_verifyDumpTables ?
DROP PROCEDURE psp_verifyDumpColumnAttribs ?


/*
 * This stored procedure is used to determine the metadata version of the
 * given database. It is not exposed to the users and is only used by some of
 * the system stored procedures.
 */
 
CREATE PROCEDURE psp_database_version (
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
	out :version        INTEGER -- metadata version    
) WITH DEFAULT HANDLER    
AS
BEGIN

    -- Variable to hold the dynamic query    
    DECLARE :version_query  LONGVARCHAR;

	-- This variable stores the size of Xf$Name column.
    -- This size will be used to determine whether the database is using V1
    -- metadata or V2 metadata
	DECLARE :col_size           INTEGER;

	-- This variable stores X$Field table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :field_table_name    VARCHAR(30);

    -- If the database qualifier is null, then use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

    SET :field_table_name = '"' + :database_qual + '".X$Field';
    
    CREATE TABLE #vt (col_size INTEGER);
    SET :version_query = 'INSERT INTO #vt SELECT Xe$Size FROM ' 
                         + :field_table_name + ' WHERE Xe$Name = ''Xf$Name'' ';
    exec(:version_query);

    if (SQLSTATE = 'S1000') then
        SIGNAL 'S1000', 'Unable to open table: X$Field';
    end if;

	SELECT col_size INTO :col_size FROM #vt;

    if (:col_size = 20) then  
        -- Metadata version is V1
        SET :version = 1;        
    else 
        if (:col_size = 128) then  
            -- Metadata version is V2
            SET :version = 2;
        end if;                     
    end if;
END; -- End of stored procedure.

?
/*
 * This system procedure is used to return the list of db names on the server.
 */
CREATE PROCEDURE psp_dbnames
(   
    in :dbnameLocation   VARCHAR(1000),  -- dbnames.cfg location on the server; NULL is to drop x$dbnames in dictionary
    in :dbname           VARCHAR(21)     -- takes wild card for LIKE or NULL
)

RETURNS
(
    NAME VARCHAR(20)
);

BEGIN

    declare :dbnameId int = NULL;
    declare :sqlStmt VARCHAR(1000);
    declare :sqlStmt2 VARCHAR(800);

    if ( :dbnameLocation is NULL ) then

      drop table pervasivesysdb.x$dbnames in dictionary;

      select NULL;

    else

      select xf$id into :dbnameId from pervasivesysdb.x$file where xf$name = 'x$dbnames';

      if ( :dbnameId is NULL ) then

         set truenullcreate = off;

         set :sqlStmt = 'create table pervasivesysdb.x$dbnames in dictionary using ''';

         set :sqlStmt = :sqlStmt + :dbnameLocation + '/dbnames.cfg'' ';

         set :sqlStmt2 = '( 
            name                char(20) case,
            version             smallint,
            type                utinyint,
            integrityEnforced   utinyint,
            policy              utinyint,
            dbCodePage          usmallint,
            reserved            char(5),
            location            varchar(254) case,
            datalocations       varchar(511) case);';

        set :sqlStmt = :sqlStmt + :sqlStmt2;

        exec ( :sqlStmt );

        set truenullcreate = on;

        create unique index name_idx in dictionary on pervasivesysdb.x$dbnames ( name );

      end if;

      if ( :dbname is NOT null) then
    	select rtrim(name) from pervasivesysdb.x$dbnames where name like :dbname;
      else
        select rtrim(name) from pervasivesysdb.x$dbnames;
      end if;

    end if;

END;

?
/*
 * This system procedure is used to return the list of columns
 * and the corresponding column for the given table(s).
 */
 
CREATE PROCEDURE psp_columns (
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
	in :table_name      VARCHAR(255),             -- Name of the table
	in :column_name     VARCHAR(255)=NULL         -- Name of the column
)
RETURNS (
	TABLE_QUALIFIER     VARCHAR (20), 	-- Name of the database that contains the table
	TABLE_OWNER         VARCHAR (20), 	-- Owner of the table
	TABLE_NAME          VARCHAR (255),	-- Table Name
	COLUMN_NAME         VARCHAR(255),	-- Column name
	DATA_TYPE           SMALLINT,		-- Datatype of Column
	TYPE_NAME           VARCHAR (32),	-- Datatype Name
	PRECISION           INTEGER,		-- Precision
	LENGTH              INTEGER, 		-- Length
	SCALE               SMALLINT,		-- Scale
	RADIX               SMALLINT,		-- Radix
	NULLABLE            SMALLINT,		-- Nullable flag
	REMARKS             VARCHAR(255),	-- Remarks
	COLUMN_DEF          VARCHAR(255)        -- Column Defaults
)
as 
BEGIN 

    -- Check if the database qualifier specified is a blank string
    if (rtrim(:database_qual) = '') then
	SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
    end if;
    
    -- Check if the table_name is null
    if (:table_name is null) then
    	SIGNAL 'S1000', 'Table name cannot be null'; 
    end if;
    
    -- Check if the table_name specified is a blank string
    if (rtrim(:table_name) = '') then
	SIGNAL 'S1000', 'Table name cannot be a blank string'; 
    end if;

    -- Check if the table_name specified is a blank string
    if (rtrim(:column_name) = '') then
	SIGNAL 'S1000', 'Column name cannot be a blank string'; 
    end if;

    SET :database_qual = IFNULL(:database_qual, DATABASE());

    SET :column_name = IFNULL(:column_name,'%'); 

    SELECT "TABLE_QUALIFIER",
           "TABLE_OWNER",
           "TABLE_NAME",
           "COLUMN_NAME",
           "DATA_TYPE",
           "TYPE_NAME",
           "PRECISION",
           "LENGTH",
           "SCALE",
           "RADIX",
           "NULLABLE",
           "REMARKS",
           "COLUMN_DEF"
    FROM DBO.fSQLColumns ( :database_qual, :table_name, :column_name );

END; -- End of stored procedure.


?
/* Possible Column Attributes:
 * 1. DEFAULT 2. NULL 3. HEADING in Create View 4.Validation (Range check)
 * For the DEFAULT column attribute, one row is inserted into x$attrib. 
 * For rest of the three attributes, the PSQL Docs do not talk about it in detail.
 */
/**
 * This system procedure is used to return the list of
 * column attributes and the corresponding information 
 * in the current database.
 */
CREATE PROCEDURE psp_column_attributes
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :table_name      VARCHAR(255) = NULL, -- Name of the table
    in :column_name     VARCHAR(255) = NULL  -- Name of the column 
)

RETURNS
(
    TABLE_QUALIFIER     VARCHAR (20), -- Name of the database that contains the table
    TABLE_OWNER         VARCHAR (20),  -- owner of the table
    TABLE_NAME          VARCHAR(255),   -- Name of the table
    COLUMN_NAME         VARCHAR(255),   -- Name of the column
    ATTRIB_TYPE         CHAR(10),    -- Type of the column attribute
    ATTRIB_SIZE         USMALLINT,  -- Size of the column attribute
    ATTRIB_VALUE        LONGVARCHAR -- Value of the column attribute
);

BEGIN
    -- Variable to hold the dynamic query
    declare :stmt_psp_column_attributes LONGVARCHAR;
    
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

     -- Check if the table_name specified is a blank string
	if (rtrim(:table_name) = '') then
		SIGNAL 'S1000', 'Table name cannot be a blank string'; 
	end if;

    -- Check if the table_name specified is a blank string
	if (rtrim(:column_name) = '') then
		SIGNAL 'S1000', 'Column name cannot be a blank string'; 
	end if;

	SET :database_qual = IFNULL(:database_qual, DATABASE());

	/*	If the table name is not supplied or the value is null, 
	    match all the tables */	
	SET :table_name  = IFNULL(:table_name,'%');
	
	/*	If the column name is not supplied or the value is null 
	    null match all the columns */
	SET :column_name = IFNULL(:column_name,'%');

	/* Enumerate all the column attributes */
		
	set :stmt_psp_column_attributes = ' SELECT ''' + 
			  :database_qual + 
			 ''', null, ' + 
			 ' A.Xf$Name, ' + 
			 ' B.Xe$Name, ' + 
			 ' case when (ISNUMERIC(C.Xa$Type) = 1 and C.Xa$Type = 68) or (ASCII( C.Xa$Type ) = 68) ' + 
			 	' then ' + '''DEFAULT''' + 
				' else' + 
				' case when (ISNUMERIC(C.Xa$Type) = 1 and C.Xa$Type = 79) or (ASCII( C.Xa$Type ) = 79) ' + 
					' then ' + '''COLLATE''' + 
					' else' + ' case when (ASCII( C.Xa$Type ) = 76) ' + 
						' then ' + '''L''' + 
					' else' + ' case when (ASCII( C.Xa$Type ) = 77) ' + 
						' then ' + '''M''' + 
						' else NULL' + 
						' end' + 
					' end' + 
				' end' + 
			' end, ' + ' C.Xa$ASize, ' + ' C.Xa$Attrs ' + 
			' FROM  "' + 
			:database_qual + '".X$File A, "'  + 
			:database_qual + '".X$Field B, "' + 
			:database_qual + '".X$Attrib C ' +                  
			' WHERE A.Xf$Name LIKE ''' + :table_name + 
			''' AND A.Xf$Id = B.Xe$File AND ' + ' B.Xe$Name LIKE ''' + :column_name + 
			''' AND C.Xa$Id = B.Xe$Id; '

    exec (:stmt_psp_column_attributes);
END; -- End of stored procedure.

?
/**
 * This system procedure is used to return the list of 
 * column rights for a given table(s) from the database
 * specified.
 */
CREATE PROCEDURE psp_column_rights
(
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :table_name      VARCHAR(255) , -- Name of the table
    in :column_name     VARCHAR(255) = NULL, -- Name of the column
    in :user_name       VARCHAR(255) = USER() -- Name of the user
)

RETURNS
(
    TABLE_QUALIFIER     VARCHAR (20), -- Name of the database that contains the table
    TABLE_OWNER         VARCHAR (20),  -- owner of the table
    USER_NAME           VARCHAR(255), -- Name of the user
    TABLE_NAME          VARCHAR(255), -- Name of the table 
    COLUMN_NAME         VARCHAR(255), -- Name of the column
    RIGHTS              VARCHAR(12)  -- Column rights 
);

BEGIN
	-- This variable stores the dynamic query
	DECLARE :query              LONGVARCHAR;
    
	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :file_table_name    VARCHAR(30);
    DECLARE :rights_table_name  VARCHAR(30);
    DECLARE :field_table_name   VARCHAR(30);
    DECLARE :user_table_name    VARCHAR(30);

	-- This variable stores the metadata version.
	DECLARE :version    INTEGER;

    -- Check if the table name is null
	if (:table_name is null) then
		SIGNAL 'S1000', 'Table name cannot be null';
	end if;

    -- Check if the table name is a blank string
	if (rtrim(:table_name) = '') then
		SIGNAL 'S1000', 'Table name cannot be blank string';
	end if;		

    -- If the database qualifier is null, then use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;
    
    -- Check if the column name is a blank string
	if (rtrim(:column_name) = '') then
		SIGNAL 'S1000', 'Column name cannot be blank string';
	end if;		
    
    -- Check if the user name is a blank string
	if (rtrim(:user_name) = '') then
		SIGNAL 'S1000', 'User name cannot be a blank string';
	end if;
    
	/*	If column name is null or not supplied, 
	    match all the columns */
	SET :column_name = IFNULL(:column_name,'%');
	
	/*	If user name is null or not supplied, 
	    Set user_name to current user */
	SET :user_name = IFNULL(:user_name, USER());

    SET :field_table_name = '"' + :database_qual + '".X$Field C';
    SET :file_table_name = '"' + :database_qual + '".X$File A';
    SET :rights_table_name = '"' + :database_qual + '".X$Rights B';
    SET :user_table_name = '"' + :database_qual + '".X$User D';
	
    -- call procedure to find the metadata version and store it in a variable
	CALL psp_database_version(:database_qual, :version);

    if (:version = 1) then  
        -- Metadata version is V1

    /*  Enumerate the column rights */
                -- SELECT rights for column
    SET :query = 'SELECT ''' + :database_qual + ''', null,' +	   	
            	 ' D.Xu$Name, A.Xf$Name, C.Xe$Name, ''SELECT'' ' +
            	 ' FROM ' + :file_table_name + ',' + :rights_table_name +
                 ',' + :field_table_name + ',' + :user_table_name +
                 ' WHERE	A.Xf$Name LIKE ''' + :table_name + ''' AND ' +
        		 'D.Xu$Name LIKE ''' + :user_name + ''' AND C.Xe$Name ' +
                 'LIKE ''' + :column_name + ''' AND	' +
                 'b.xr$rights & 64 = 64 AND ' + 
        		 'B.Xr$Column <> 0 AND A.Xf$Id = B.Xr$Table AND ' +
        		 'D.Xu$Id = B.Xr$User and  B.Xr$Column = C.Xe$Id ' +
                 -- UPDATE rights for column
                 'UNION SELECT ''' + :database_qual + ''', null, ' +	   	
        		 'D.Xu$Name, A.Xf$Name, C.Xe$Name, ''UPDATE'' ' +
            	 'FROM ' + :file_table_name + ',' + :rights_table_name +
                 ',' + :field_table_name + ',' + :user_table_name + 
                 ' WHERE A.Xf$Name LIKE ''' + :table_name + 
                 ''' AND D.Xu$Name LIKE ''' +
                 :user_name + ''' AND C.Xe$Name LIKE ''' + :column_name  + 
                 ''' AND b.xr$rights & 130 = 130 AND ' +
        	  	 'B.Xr$Column <> 0 AND A.Xf$Id = B.Xr$Table AND ' +
                 'D.Xu$Id = B.Xr$User and B.Xr$Column = C.Xe$Id ' +
                 -- INSERT rights for column
                 'UNION SELECT ''' + :database_qual + ''', null, ' +
                 'D.Xu$Name, A.Xf$Name, C.Xe$Name, ''INSERT'' ' +
            	 'FROM ' + :file_table_name + ',' + :rights_table_name +
                 ',' + :field_table_name + ',' + :user_table_name +
                 ' WHERE 	A.Xf$Name LIKE ''' + :table_name + ''' AND ' +
              	 ' D.Xu$Name LIKE ''' + :user_name + ''' AND C.Xe$Name ' +
                 'LIKE ''' + :column_name + ''' AND	b.xr$rights & 132 = 132 ' +
                 'AND B.Xr$Column <> 0 AND A.Xf$Id = B.Xr$Table AND ' + 
                 'D.Xu$Id = B.Xr$User and B.Xr$Column = C.Xe$Id';
    else 
        if (:version = 2) then  
            -- Metadata version is V2
            /*  Enumerate the column rights */
                        -- SELECT rights for column
            SET :query = 'SELECT ''' + :database_qual + ''', null,' +	   	
                         ' D.Xu$Name, A.Xf$Name, C.Xe$Name, ''SELECT'' ' +
                         ' FROM ' + :file_table_name + ',' + :rights_table_name +
                         ',' + :field_table_name + ',' + :user_table_name +
                         ' WHERE	A.Xf$Name LIKE ''' + :table_name + ''' AND ' +
                         'D.Xu$Name LIKE ''' + :user_name + ''' AND C.Xe$Name ' +
                         'LIKE ''' + :column_name + ''' AND	' +
                         'b.xr$rights & 64 = 64 AND ' + 
                         'B.Xr$Column <> 0 AND A.Xf$Id = B.Xr$Object AND ' +
                         'D.Xu$Id = B.Xr$User and  B.Xr$Column = C.Xe$Id and B.Xr$Type = 1 ' +
                         -- UPDATE rights for column
                         'UNION SELECT ''' + :database_qual + ''', null, ' +	   	
                         'D.Xu$Name, A.Xf$Name, C.Xe$Name, ''UPDATE'' ' +
                         'FROM ' + :file_table_name + ',' + :rights_table_name +
                         ',' + :field_table_name + ',' + :user_table_name + 
                         ' WHERE A.Xf$Name LIKE ''' + :table_name + 
                         ''' AND D.Xu$Name LIKE ''' +
                         :user_name + ''' AND C.Xe$Name LIKE ''' + :column_name  + 
                         ''' AND b.xr$rights & 130 = 130 AND ' +
                         'B.Xr$Column <> 0 AND A.Xf$Id = B.Xr$Object AND ' +
                         'D.Xu$Id = B.Xr$User and B.Xr$Column = C.Xe$Id and B.Xr$Type = 1 ' +
                         -- INSERT rights for column
                         'UNION SELECT ''' + :database_qual + ''', null, ' +
                         'D.Xu$Name, A.Xf$Name, C.Xe$Name, ''INSERT'' ' +
                         'FROM ' + :file_table_name + ',' + :rights_table_name +
                         ',' + :field_table_name + ',' + :user_table_name +
                         ' WHERE 	A.Xf$Name LIKE ''' + :table_name + ''' AND ' +
                         ' D.Xu$Name LIKE ''' + :user_name + ''' AND C.Xe$Name ' +
                         'LIKE ''' + :column_name + ''' AND	b.xr$rights & 132 = 132 ' +
                         'AND B.Xr$Column <> 0 AND A.Xf$Id = B.Xr$Object AND ' + 
                         'D.Xu$Id = B.Xr$User and B.Xr$Column = C.Xe$Id and B.Xr$Type = 1';            
        end if;                     
    end if;
                     
    exec (:query);

END; -- End of stored procedure.


?
/* DEFERRABILITY ommited */
/*
 * This system procedure returns the foreign key information
 * for a given table(s).
 */
CREATE PROCEDURE psp_fkeys
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :pktable_name    VARCHAR(255), -- Name of the primary key table
    in :fktable_name    VARCHAR(255) = NULL  -- Name of the foreign key table
)

RETURNS
(
    PKTABLE_QUALIFIER   VARCHAR (20), -- Name of the database that contains the Primary key table
    PKTABLE_OWNER       VARCHAR (20),  -- owner of the primary key table
    PKTABLE_NAME        VARCHAR(255),  -- Name of the primary key table
    PKCOLUMN_NAME       VARCHAR(255),  -- Name of the primary key column
    KEY_SEQ             USMALLINT, -- Sequence of the keys
    FKTABLE_QUALIFIER   VARCHAR (20), -- Name of the database that contains the Foreign key table
    FKTABLE_OWNER       VARCHAR (20),  -- owner of the Foreign key  table
    FKTABLE_NAME        VARCHAR(255),  -- Name of the foriegn key table
    FKCOLUMN_NAME       VARCHAR(255),  -- Name of the foreign key column
    UPDATE_RULE         UTINYINT,  -- Update rule 
    DELETE_RULE         UTINYINT,  -- Delete rule
    PK_NAME             VARCHAR(255),  -- Name of the primary key
    FK_NAME             VARCHAR(255)   -- Name of the foreign key
);

BEGIN
    -- Variable to hold the dynamic query
    DECLARE :stmt_psp_fkeys LONGVARCHAR;

    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;
    
     -- Check if the table_name is null
    if (:pktable_name is null) then
    	SIGNAL 'S1000', 'Primary key table name cannot be null'; 
    end if;
    
     -- Check if the table_name specified is a blank string
	if (rtrim(:pktable_name) = '') then
		SIGNAL 'S1000', 'Primary key table name cannot be a blank string'; 
	end if;

     -- Check if the table_name specified is a blank string
	if (rtrim(:fktable_name) = '') then
		SIGNAL 'S1000', 'Foreign key table name cannot be a blank string'; 
	end if;

	SET :database_qual = IFNULL(:database_qual,DATABASE());

	/*	If foreign key table name is null or not supplied, 
	    match all the foreign key tables */
	SET :fktable_name = IFNULL(:fktable_name,'%');
	
	/* Enumerate all the foreign key information */
	
	SET :stmt_psp_fkeys = ' SELECT ''' +
							:database_qual +			/* PKTABLE_QUALIFIER */ 
							''', null, ' +				/* PKTABLE_OWNER */	
							' B.Xf$Name, ' +			/* PKTABLE_NAME */
							' F.Xe$Name, ' +			/* PKCOLUMN_NAME */
							' D.Xi$Part, ''' + 			/* KEY_SEQUENCE */
							:database_qual +			/* FKTABLE_QUALIFIER */ 
							''', null, ' +				/* FKTABLE_OWNER */	
							' C.Xf$Name, ' +			/* FKTABLE_NAME */
							' H.Xe$Name, ' +			/* FKCOLUMN_NAME */ 
		   					' A.Xr$UpdateRule, ' +		/* UPDATE_RULE */
							' A.Xr$DeleteRule,  ' +		/* DELETE_RULE */
							' E.Xe$Name, ' +			/* PK_NAME */
							' A.Xr$Name	' +				/* FK_NAME */
							' FROM "' + :database_qual + '".X$Relate A ,"' +
									   :database_qual + '".X$File B, "' + 
									   :database_qual + '".X$File C, "' + 
									   :database_qual + '".X$Index D, "' + 
									   :database_qual + '".X$Field E, "' +
									   :database_qual + '".X$Field F, "' +
									   :database_qual + '".X$Index G, "' +
									   :database_qual + '".X$Field H ' +
							' WHERE A.Xr$PId = B.Xf$Id AND ' +
    						' B.Xf$Name LIKE ''' + :pktable_name +
							''' AND A.Xr$FId = C.Xf$Id AND ' +
      						' C.Xf$Name LIKE ''' + :fktable_name +
							''' AND B.Xf$Id = D.Xi$File AND ' +
      						' A.Xr$Index = D.Xi$Number AND ' +
      						' D.Xi$File = E.Xe$File AND ' +
      						' D.Xi$Number = E.Xe$Offset AND ' +
      						' E.Xe$DataType = 227 AND ' +
      						' A.Xr$PId = F.Xe$File AND ' +
      						' D.Xi$Field = F.Xe$Id AND ' +
      						' A.Xr$Fid = G.Xi$File AND ' +
      						' A.Xr$FIndex = G.Xi$Number AND ' +
      						' D.Xi$Part = G.Xi$Part AND ' +
      						' G.Xi$File = H.Xe$File AND ' +
      						' G.Xi$Field = H.Xe$Id ' +
							' ORDER BY E.Xe$Name, A.Xr$Name; ' 
   
   exec (:stmt_psp_fkeys);
END; -- End of stored procedure.

?
/**
 * This system procedure is used to return the list
 * of groups and the corresponding information from the 
 * database specified.
 */
CREATE PROCEDURE psp_groups
(
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :group_name      VARCHAR(254) = NULL -- Name of the group
)
RETURNS
(
    DATABASE_QUALIFIER  VARCHAR(20), -- Name of the database
    GROUP_ID            USMALLINT, -- Group ID
    GROUP_NAME          VARCHAR(254)   -- Name of the group
);
BEGIN

    -- This variable stores the dynamic query
	DECLARE :query      LONGVARCHAR;
    
	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :table_name VARCHAR(30);

    -- If database qualifier is null, use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;

    -- Check if the database qualifier is a blank string.
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;    
    
    -- Check if the group name is a blank string.
    if (rtrim(:group_name) = '') then
        SIGNAL 'S1000', 'Please enter a valid group name.Group name cannot be a blank string';
    end if;

	/* If group name is null or not supplied, 
	   match all the groups */
		
	SET :group_name = IFNULL(:group_name,'%');
	
	-- Enumerate the group ids and the group names

    SET :table_name = '"' + :database_qual + '".X$User';

    SET :query = ' SELECT ''' + :database_qual + 
                 ''' ,A.Xu$Id, A.Xu$Name FROM ' + :table_name +
                 ' A WHERE A.Xu$Name LIKE ''' + :group_name + 
                 ''' AND  A.Xu$Flags & 64 = 64 ';     -- Check whether the name is a group name. 
    exec (:query);
	
END; -- End of stored procedure.


?
/**
 * This system procedure is used to return the 
 * definition text of the stored procedure from the 
 * database specified.
 */
create PROCEDURE psp_help_sp
(
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :procedure_name  VARCHAR(254) -- Name of the stored procedure
)

RETURNS
(
    DATABASE_QUALIFIER  VARCHAR(20), -- Name of the database
    SP_TEXT             LONGVARCHAR -- Definition text of the stored procedure.
);

BEGIN

	-- This variable stores the dynamic query
	DECLARE :query      LONGVARCHAR;
    
	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :table_name VARCHAR(30);

    -- This variable stores the metadata version
    DECLARE :version           INTEGER;
    
    -- If the database qualifier is null, then use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

    -- Check if the procedure name is null
	if (:procedure_name is null) then
		SIGNAL 'S1000', 'Procedure name cannot be null';
	end if;

    -- Check if the procedure name is a blank string
	if (rtrim(:procedure_name) = '') then
		SIGNAL 'S1000', 'Procedure name cannot be blank string';
	end if;		
	
    set :table_name = '"' + :database_qual + '".X$Proc' 
    	
    -- call procedure to find the metadata version and store it in a variable
    CALL psp_database_version(:database_qual, :version);

    if (:version = 1) then  
        -- Metadata version is V1
    
    set :query = 'SELECT ''' + :database_qual + ''', Xp$Misc FROM ' + 
                 :table_name + ' WHERE Xp$Name LIKE ''' + :procedure_name +
                 ''' and Xp$Flags <> 1 ORDER BY Xp$Id ';
    else 
        if (:version = 2) then  
   	    -- Metadata version is V2
	    
	    set :query = 'SELECT ''' + :database_qual + ''', Xp$Misc FROM ' + 
                 :table_name + ' WHERE Xp$Name LIKE ''' + :procedure_name +
                 ''' and Xp$Flags <> 1 ORDER BY Xp$Sequence ';

	end if;                     
    end if;

    exec (:query);
END; -- End of stored procedure.


?
/**
 * This system procedure is used to return the
 * definition text of the given trigger from the 
 * database specified.
 */
CREATE PROCEDURE psp_help_trigger
(
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :trigger_name    VARCHAR(254) -- Name of the trigger
)

RETURNS
(
    DATABASE_QUALIFIER  VARCHAR(20), -- Name of the database
    TRIGGER_TEXT        LONGVARCHAR -- Definiton text of the trigger
);

BEGIN

	-- This variable stores the dynamic query
	DECLARE :query      LONGVARCHAR;

	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :table_name VARCHAR(30);

    -- If the database qualifier is null, then use current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;
	
    -- Check if the trigger name is null
	if (:trigger_name is null) then
		SIGNAL 'S1000', 'Trigger name cannot be null';
	end if;

    -- Check if the trigger name is a blank string
	if (rtrim(:trigger_name) = '') then
		SIGNAL 'S1000', 'Trigger name cannot be blank string';
	end if;		
	
    set :table_name = '"' + :database_qual + '".X$Trigger' 

    set :query = 'SELECT ''' + :database_qual + ''', Xt$Misc FROM ' + 
                 :table_name + ' WHERE Xt$Name LIKE ''' + :trigger_name +
                 ''' ORDER BY Xt$Sequence ';

    exec (:query);		
		
END;


?
/**
 * This system procedure is used to return the 
 * definition text of the user defined function from
 * the database specified.
 */
CREATE PROCEDURE psp_help_udf
(
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :udf_name        VARCHAR(254) -- Name of the user defined function
)

RETURNS
(
    DATABASE_QUALIFIER  VARCHAR(20), -- Name of the database
    UDF_TEXT LONGVARCHAR -- Definiton text of the user defined function.
);

BEGIN

	-- This variable stores the dynamic query
	DECLARE :query      LONGVARCHAR;
	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :table_name VARCHAR(30);

    -- If the database qualifier specified is null, then use current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier is blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;
	
    -- Check if the udf name is null
	if (:udf_name is null) then
		SIGNAL 'S1000', 'User-defined function name cannot be null';
	end if;

    -- Check if the udf name is a blank string
	if (rtrim(:udf_name) = '') then
		SIGNAL 'S1000', 'User-defined function name cannot be blank string';
	end if;		
	
    set :table_name = '"' + :database_qual + '".X$Proc' 

    set :query = 'SELECT ''' + :database_qual + ''', Xp$Misc FROM ' + 
                 :table_name + ' WHERE Xp$Name LIKE ''' + :udf_name +
                 ''' and Xp$Flags = 1 ORDER BY Xp$Id ';

    exec (:query);	
END;
 -- End of the stored procedure.

?
/**
 * This system procedure is used to return the 
 * definition text of the view from the database specified.
 */
CREATE PROCEDURE psp_help_view
(
    in :database_qual   VARCHAR(20) = DATABASE(), -- Database name
    in :view_name       VARCHAR(254) -- Name of the view
)

RETURNS
(
    DATABASE_QUALIFIER  VARCHAR(20), -- Database name
    VIEW_TEXT LONGVARCHAR  -- Definition text of the view
);

BEGIN
	-- This variable stores the dynamic query
	DECLARE :query      LONGVARCHAR;
    
	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :table_name VARCHAR(30);

    -- This variable stores the metadata version
    DECLARE :version           INTEGER;

    -- If the database qualifier is null, use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

    -- Check if view name is null
	if (:view_name is null) then
		SIGNAL 'S1000', 'View name cannot be null';
	end if;
    
    -- Check if the view name is a blank string
	if (rtrim(:view_name) = '') then
		SIGNAL 'S1000', 'View name cannot be blank string';
	end if;		
	
    set :table_name = '"' + :database_qual + '".X$View' 
	
    -- call procedure to find the metadata version and store it in a variable
    CALL psp_database_version(:database_qual, :version);
   
    if (:version = 1) then  
        -- Metadata version is V1
	
    set :query = 'SELECT ''' + :database_qual + ''', Xv$Misc FROM ' + 
                 :table_name + ' WHERE Xv$Name LIKE ''' + :view_name +
                 ''' ORDER BY Xv$Id ';
    else 
        if (:version = 2) then  
   	    -- Metadata version is V2
	    
	    set :query = 'SELECT ''' + :database_qual + ''', Xv$Misc FROM ' + 
                 :table_name + ' WHERE Xv$Name LIKE ''' + :view_name +
                 ''' ORDER BY Xv$Sequence ';
		end if;                     
    end if;
    
    exec (:query);		
		
END; -- End of the stored procedure.


?
/* ALTERNATIVE APPROACHES  : 
 * 1. Multiple columns. (one implemented)
 * 2. Multiple rows using unions.
 * 3. Flag values as it is.
*/
/**
 * This system procedure is used to return the list  
 * of indexes defined in the current database.
 */
CREATE PROCEDURE psp_indexes
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :table_name      VARCHAR(255) = NULL -- Name of the table
)

RETURNS
(
    TABLE_QUALIFIER     VARCHAR (20),  -- Name of the database that contains the table
    TABLE_OWNER         VARCHAR (20),  -- owner of the table
    TABLE_NAME          VARCHAR(255),  -- Name of the table
    INDEX_NAME          VARCHAR(255),  -- Name of the index
    INDEX_TYPE          CHAR(20), 	    -- Type of the index - Primary/Foreign 
    COLUMN_NAME         VARCHAR(255),  -- Name of the column
    ORDINAL_POSITION    USMALLINT,  -- Ordinal position of the index
    DUPLICATES_ALLOWED  CHAR(3), -- Duplicate Index
    UPDATABLE           CHAR(3), -- Modifiable Index
    CASE_SENSITIVE      CHAR(3), -- Case sensitive Index
    ASC_DESC		    CHAR(1), -- Asc or Desc index
    NAMED_INDEX		    CHAR(3)  -- Named or UnNamed Index
);

BEGIN

  declare :pkName VARCHAR(255);

  -- Check if the database qualifier specified is a blank string
  if (rtrim(:database_qual) = '') then
    SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
  end if;

  -- Check if the table_name specified is a blank string
  if (rtrim(:table_name) = '') then
    SIGNAL 'S1000', 'Table name cannot be a blank string'; 
  end if;

  -- If database qualifier is null, use current database
  SET :database_qual = IFNULL(:database_qual, DATABASE());

  /* If table name is not supplied or null value
     match all */	
  SET :table_name = IFNULL(:table_name,'%');

  SELECT B."PK_NAME" INTO :pkName FROM dbo.fSQLPrimaryKeys ( :database_qual, :table_name ) B;  -- For creating missing optional ddfs

  SELECT "PK_NAME" INTO "#PrimaryKeyTable" FROM dbo.fSQLPrimaryKeys ( :database_qual, :table_name );
  SELECT "FK_NAME" INTO "#ForeignKeyTable" FROM dbo.fSQLForeignKeys ( :database_qual, '%', :table_name );

  SELECT A."TABLE_QUALIFIER",
         A."TABLE_OWNER", 
         A."TABLE_NAME",
         A."INDEX_NAME",

       CASE
        WHEN ( SELECT COUNT(B."PK_NAME") FROM "#PrimaryKeyTable" B WHERE B."PK_NAME" = A."INDEX_NAME" ) > 1 THEN 'PRIMARY KEY INDEX'
        WHEN ( SELECT COUNT(C."FK_NAME") FROM "#ForeignKeyTable" C WHERE C."FK_NAME" = A."INDEX_NAME" ) > 1 THEN 'FOREIGN KEY INDEX'       
        ELSE 'NORMAL INDEX'
       END AS "INDEX_TYPE",

         A."COLUMN_NAME",
         A."SEQ_IN_INDEX" AS "ORDINAL_POSITION",

       CASE A."NON_UNIQUE"
        WHEN 0 THEN 'NO'
        ELSE 'YES'
       END AS "DUPLICATES_ALLOWED",

       CASE 
        WHEN ( SELECT TOP 1 I.Xi$Flags & 2 FROM X$File F, X$Index I, X$Field E
               WHERE F.Xf$Name = RTRIM(A."TABLE_NAME")
                 AND E.Xe$Name = RTRIM( A."COLUMN_NAME" )
                 AND I.Xi$File = Xf$Id
                 AND I.Xi$File = E.Xe$File
                 AND I.Xi$Field = E.Xe$Id
                 AND I.Xi$Part = A."SEQ_IN_INDEX" - 1 ) = 2 THEN 'YES'
        ELSE 'NO'
       END AS "UPDATABLE",

       CASE 
        WHEN ( SELECT TOP 1 I.Xi$Flags & 32 FROM X$File F, X$Index I, X$Field E
               WHERE F.Xf$Name = RTRIM(A."TABLE_NAME")
                 AND E.Xe$Name = RTRIM( A."COLUMN_NAME" )
                 AND I.Xi$File = Xf$Id
                 AND I.Xi$File = E.Xe$File
                 AND I.Xi$Field = E.Xe$Id
                 AND I.Xi$Part = A."SEQ_IN_INDEX" - 1 ) = 32 THEN 'NO'
        ELSE 'YES'
       END AS "CASE_SENSITIVE",

         A."COLLATION" AS "ASC_DESC",

       CASE 
        WHEN ( SELECT TOP 1 I.Xi$Flags & 128 FROM X$File F, X$Index I, X$Field E
               WHERE F.Xf$Name = RTRIM(A."TABLE_NAME")
                 AND E.Xe$Name = RTRIM( A."COLUMN_NAME" )
                 AND I.Xi$File = Xf$Id
                 AND I.Xi$File = E.Xe$File
                 AND I.Xi$Field = E.Xe$Id
                 AND I.Xi$Part = A."SEQ_IN_INDEX" - 1 ) = 128 THEN 'YES'
        ELSE 'NO'
       END AS "NAMED_INDEX"

  FROM dbo.fSQLStatistics ( :database_qual, :table_name, 1 ) A

  WHERE A."INDEX_NAME" IS NOT NULL

  ORDER BY A."INDEX_NAME", "ORDINAL_POSITION";

END; -- End of the stored procedure.


?
/*
 * This system procedure return the primary key information
 * for the given table(s) from the database specified.
 */
CREATE PROCEDURE psp_pkeys
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :table_name      VARCHAR(255) -- Name of the table 
)

RETURNS
(
    TABLE_QUALIFIER     VARCHAR (20), -- Name of the database that contains the table
    TABLE_OWNER         VARCHAR (20),  -- owner of the table
    TABLE_NAME          VARCHAR(255), -- Name of the table
    COLUMN_NAME         VARCHAR(255), -- Name of the column
    COLUMN_SEQ          USMALLINT, -- Sequence of the column
    PK_NAME             VARCHAR(255)  -- Name of the primary key name
);

BEGIN
    -- Variable to hold the dynamic query
    DECLARE :stmt_psp_pkeys LONGVARCHAR;

    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

     -- Check if the table_name specified is a blank string
	if (rtrim(:table_name) = '') then
		SIGNAL 'S1000', 'Table name cannot be a blank string'; 
	end if;
    
    -- Check if the table_name is null
    if (:table_name is null) then
    	SIGNAL 'S1000', 'Table name cannot be null'; 
    end if;
    
    -- If database qualifier is null, use current database
	SET :database_qual = IFNULL(:database_qual, DATABASE());

	/* Enumerate the primary key information */
	
	SET :stmt_psp_pkeys =	' SELECT ''' +
							:database_qual +	/* TABLE_QUALIFIER */ 
							''', null, ' +		/* TABLE_OWNER */	
							' A.Xf$Name, ' +	/* TABLE_NAME */
							' B.Xe$Name, ' +	/* COLUMN_NAME */
							' C.Xi$Part, ' +	/* COLUMN_SEQUENCE */
							' D.Xe$Name	' +		/* PKEY_NAME */
							' FROM "' + :database_qual + '".X$File A, "' +
										:database_qual + '".X$Field B, "' + 
										:database_qual + '".X$Index C, "' + 
										:database_qual + '".X$Field D ' +
							' WHERE A.Xf$Name LIKE ''' + :table_name +
							''' AND A.Xf$Id = C.Xi$File AND ' +
    						' C.Xi$Flags & 16384 = 16384 AND ' + -- Check whether it is a primary key
    						' D.Xe$DataType = 227 AND ' + -- Check whether it is a index key
      						' C.Xi$Field = B.Xe$Id AND ' +
							' D.Xe$Offset = C.Xi$Number AND ' +
							' D.Xe$File = A.Xf$Id ' +
							' ORDER BY D.Xe$Name, C.Xi$Part ';

    exec (:stmt_psp_pkeys);

END; -- End of the stored procedure.       

?
/*
 * This system procedure is used to return the list
 * of stored procedures available in the database specified.
 */
CREATE PROCEDURE psp_stored_procedures
(   
	in :database_qual    VARCHAR(20) = DATABASE(), -- Name of the database
    in :procedure_name   VARCHAR(255) = NULL, -- Name of the procedure
    in :procedure_type   VARCHAR(32) = NULL -- Type of the procedure
)

RETURNS
(
    PROCEDURE_QUALIFIER VARCHAR (20), 	-- Name of the database that contains the Stored Procedure
    PROCEDURE_OWNER     VARCHAR (20), 	-- Owner of the Stored Procedure
    PROCEDURE_NAME      VARCHAR(255), -- Name of the procedure
    PROCEDURE_TYPE      VARCHAR(30), -- Type of the procedure
	NUM_INPUT_PARAMS    INTEGER,  -- No. of input paramaters
	NUM_OUTPUT_PARAMS   INTEGER, -- no. of out parameters
	NUM_RESULT_SETS     INTEGER, -- no. of columns in result set.
    REMARKS             VARCHAR(255),	-- Remarks
    TRUSTEE             INTEGER
);

BEGIN
    -- Variable to hold the dynamic query
    DECLARE :stmt_psp_stored_procedures LONGVARCHAR;

    -- Variable to hold the information about the proc type
    DECLARE :proc_flag CHAR(1);

    -- Variable to hold the database version
    DECLARE :version	INTEGER;

    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

     -- Check if the procedure_name specified is a blank string
	if (rtrim(:procedure_name) = '') then
		SIGNAL 'S1000', 'Procedure name cannot be a blank string'; 
	end if;
    
    -- Check if the table_name is null
--    if (:procedure_name is null) then
  --  	SIGNAL 'S1000', 'Procedure name cannot be null'; 
    --end if;
    
     -- Check if the procedure_type specified is a blank string
	if (rtrim(:procedure_type) = '') then
		SIGNAL 'S1000', 'Procedure type cannot be a blank string'; 
	end if;
    
	SET :database_qual = IFNULL(:database_qual,database());
    
    -- Check whether the Database is a V1 or a V2.
    CALL psp_database_version(:database_qual, :version);
	
	-- If procedure name is not supplied or value is null
	-- match all the procedure names
	SET :procedure_name = IFNULL(:procedure_name,'%');
	
    IF (:version = 1) THEN
	IF :procedure_type IS NULL THEN

	    SET :stmt_psp_stored_procedures = ' SELECT  ''' +
										:database_qual +
										''', null, ' +
										' Xp$Name, ' +
										' case Xp$Flags ' +
										   ' when 0 then ' + 
                                              '''STORED PROCEDURE''' +
											  ' when 2 then ' + 
                                                  '''ENCRYPTED STORED 
                                                  PROCEDURE''' +
												' when 4 then ' + 
                                                    '''SYSTEM STORED 
                                                    PROCEDURE''' +
										' end, ' +
										' null, ' +
										' null, ' +
										' null, ' +
                                            ' null, null '+
                                            ' FROM "' + :database_qual +
                                            '".X$Proc '+
	    								' WHERE Xp$Name LIKE ''' + 
                                        :procedure_name + 
                                        ''' AND Xp$Flags <> 1; '
	ELSE										
	    IF (ucase(:procedure_type) = 'SP') THEN
			SET :proc_flag = '0';
		ELSE 
			IF (ucase(:procedure_type) = 'SSP') THEN
				SET :proc_flag = '4'; -- Currently xp$flags for PSPs are set as 0.
			ELSE
				SIGNAL 'S1000', 'Procedure Type can be SP, SSP or null';
			END IF
		END IF

	    SET :stmt_psp_stored_procedures = ' SELECT  ''' +
											:database_qual +
											''', null, ' +
											' Xp$Name, ' +
										' case Xp$Flags ' +
											' when 0 then ' + 
                                               '''STORED PROCEDURE''' +
											' when 2 then ' + 
                                                   '''ENCRYPTED STORED 
                                                   PROCEDURE''' +
                                            ' when 4 then ' + 
                                                   '''SYSTEM STORED 
                                                   PROCEDURE''' +
											' end, ' +
											' null, ' +
											' null, ' +
											' null, ' +
                                                ' null, null '+
	    									' FROM "' + :database_qual +
                                            '".X$Proc ' +
	    									' WHERE Xp$Name LIKE ''' + 
                                            :procedure_name +
                                                ''' AND Xp$Flags = ' + 
                                                :proc_flag +
                                            '; '

	END IF
    ELSE
    --IF (:version = 2) THEN
        IF :procedure_type IS NULL THEN
            SET :stmt_psp_stored_procedures = ' SELECT  ''' +
                                            :database_qual +
                                            ''', null, ' +
                                            ' Xp$Name, ' +
                                            ' case Xp$Flags ' +
                                               ' when 0 then ' + 
                                                  '''STORED PROCEDURE''' +
                                                  ' when 2 then ' + 
                                                  '''ENCRYPTED STORED 
                                                  PROCEDURE''' +
                                                    ' when 4 then ' + 
                                                    '''SYSTEM STORED 
                                                    PROCEDURE''' +
                                            ' end, ' +
                                            ' null, ' +
                                            ' null, ' +
                                            ' null, ' +
                                            ' null, Xp$Trustee'+
                                            ' FROM "' + :database_qual +
                                            '".X$Proc '+
                                            ' WHERE Xp$Name LIKE ''' + 
                                            :procedure_name + 
                                            ''' AND Xp$Flags <> 1; '
        ELSE										
            IF (ucase(:procedure_type) = 'SP') THEN
                SET :proc_flag = '0';
            ELSE 
                IF (ucase(:procedure_type) = 'SSP') THEN
                    SET :proc_flag = '4'; -- Currently xp$flags for PSPs are set as 0.
                ELSE
                    SIGNAL 'S1000', 'Procedure Type can be SP, SSP or null';
                END IF
            END IF

            SET :stmt_psp_stored_procedures = ' SELECT  ''' +
                                                :database_qual +
                                                ''', null, ' +
                                                ' Xp$Name, ' +
                                            ' case Xp$Flags ' +
                                                ' when 0 then ' + 
                                                   '''STORED PROCEDURE''' +
                                                ' when 2 then ' + 
                                                   '''ENCRYPTED STORED 
                                                   PROCEDURE'''
                                               +
                                            ' when 4 then ' + 
                                                   '''SYSTEM STORED 
                                                   PROCEDURE''' +
											' end, ' +
											' null, ' +
											' null, ' +
											' null, ' +
                                                ' null, Xp$Trustee '+
	    									' FROM "' + :database_qual +
                                            '".X$Proc ' +
	    									' WHERE Xp$Name LIKE ''' + 
                                            :procedure_name +
                                                ''' AND Xp$Flags = ' + 
                                                :proc_flag +
                                            '; '

	END IF
    END IF
    exec (:stmt_psp_stored_procedures);
END; -- End of the stored procedure.

?
/*
 * This system procedure is used to return the list
 * of tables and the corresponding information from the
 * database specified
 */
 
CREATE PROCEDURE psp_tables
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :table_name      VARCHAR(255)  = NULL,  -- Name of the table
    in :tab_type        VARCHAR(32) = NULL   -- Table flag
)
RETURNS
(
    TABLE_QUALIFIER     VARCHAR (20), -- Name of the database that contains the table
    TABLE_OWNER         VARCHAR (20),  -- owner of the table
    TABLE_NAME          VARCHAR(255),  -- Name of the Table
    TABLE_TYPE          VARCHAR(15),  -- Table Type
    REMARKS	            VARCHAR(255),  -- Remarks
    FILE_LOCATION       VARCHAR(255)  -- Location of the file (path name)
);

BEGIN
    -- Variable to hold the dynamic query
    DECLARE :stmt_psp_tables LONGVARCHAR;

    -- Variable to hold the information about the table type
    DECLARE :table_flag VARCHAR(3);
    
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

     -- Check if the table name specified is a blank string
	if (rtrim(:table_name) = '') then
		SIGNAL 'S1000', 'Table name cannot be a blank string'; 
	end if;
    
     -- Check if the tab_type specified is a blank string
	if (rtrim(:tab_type) = '') then
		SIGNAL 'S1000', 'Table type cannot be a blank string'; 
	end if;

    SET :database_qual = IFNULL(:database_qual,DATABASE());

	--	If table name is null or not supplied, 
	--  match all the tables

	SET :table_name = IFNULL(:table_name,'%');
	
	-- Check for the table flag
	
	IF :tab_type IS NULL THEN
	
	set :stmt_psp_tables  = ' SELECT ''' + 
									:database_qual +
									''', null, ' +
									' Xf$Name, ' + 
									' case (Xf$Flags & 16) ' +
										' when 16 then ' + '''SYSTEM TABLE''' +
										' else ' + '''USER TABLE''' +
										'end, ' + 
									'null, ' +
									' Xf$Loc ' +
    								' FROM "' + :database_qual + '".X$File ' +
    								' WHERE ' + 
									'Xf$Name LIKE ''' + :table_name + ''';'
    	
	ELSE
	
		IF (ucase(:tab_type) = 'USER TABLE') THEN
			SET :table_flag = '64';
		ELSE 
			IF (ucase(:tab_type) = 'SYSTEM TABLE') THEN
				SET :table_flag = '16';
			ELSE
			    SIGNAL 'S1000', 'Table type can be System table or User table
                or null';
			END IF
		END IF
    	-- Enumerate tables when table flag is not null
    	
    	set :stmt_psp_tables  = ' SELECT ''' +
								:database_qual + 
								''', null, ' +
								'Xf$Name, ''' +
								:tab_type + 
								''', null, ' +
								' Xf$Loc ' +
    							' FROM "' + :database_qual + '".X$File ' +
    							' WHERE ' + 'Xf$Name LIKE ''' + :table_name +
								''' AND ( Xf$Flags & ' + :table_flag + ') = ' +
                                :table_flag + ';'
    	
	END IF

    exec (:stmt_psp_tables);
	
END; -- End of the stored procedure.

?
/**
 * This system procedure is used to return the
 * the table rights for the given table(s).
 */
CREATE PROCEDURE psp_table_rights
(  
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :table_name      VARCHAR(255) = NULL, -- Name of the table
    in :user_name       VARCHAR(255) = USER() -- Name of the user
)
RETURNS
(
    TABLE_QUALIFIER     VARCHAR (20), -- Name of the database that contains the table
    TABLE_OWNER         VARCHAR (20),  -- owner of the table
    USER_NAME           CHAR(255), -- Name of the user
    TABLE_NAME          CHAR(255), -- Name of the table
    RIGHTS              VARCHAR(12)  -- Table rights 
);
BEGIN

	-- This variable stores the dynamic query
	DECLARE :query              LONGVARCHAR;
    
	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :file_table_name    VARCHAR(30);
    DECLARE :rights_table_name  VARCHAR(30);
    DECLARE :user_table_name    VARCHAR(30);

	-- This variable stores the metadata version
    DECLARE :version           INTEGER;

    -- If the database qualifier is null, then use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;
    
    -- Check if the column name is a blank string
	if (rtrim(:table_name) = '') then
		SIGNAL 'S1000', 'Table name cannot be blank string';
	end if;		
    
    -- Check if the user name is a blank string
	if (rtrim(:user_name) = '') then
		SIGNAL 'S1000', 'User name cannot be a blank string';
	end if;
	
	   
    SET :file_table_name = '"' + :database_qual + '".X$File';
    SET :rights_table_name = '"' + :database_qual + '".X$Rights';
    SET :user_table_name = '"' + :database_qual + '".X$User';
    
	/* If table name is not supplied or value is null
	   match all the tables*/
	   
    SET :table_name = IFNULL(:table_name,'%');
    SET :user_name = IFNULL(:user_name , USER());
    
    -- call procedure to find the metadata version and store it in a variable
    CALL psp_database_version(:database_qual, :version);

    if (:version = 1) then  
        -- Metadata version is V1

	/* Enumerate the table rights */
                 -- SELECT rights for Table
    SET :query = 'SELECT ''' + :database_qual + ''' , null, ' +
        		 'B.Xu$Name, A.Xf$Name, ''SELECT'' ' +
            	 'FROM ' + :file_table_name + ' A, ' + 
                 :user_table_name + ' B, ' + :rights_table_name +
                 ' C WHERE 	A.Xf$Name LIKE ''' + :table_name +
                 ''' AND B.Xu$Name LIKE ''' + :user_name +
                 ''' AND A.Xf$Id = C.Xr$Table AND B.Xu$Id = C.Xr$User ' +
                 'AND C.Xr$rights & 64 = 64 AND	C.Xr$Column = 0 ' +
                 -- REFERENCES rights for Table
                 'UNION	SELECT ''' + :database_qual +
                 ''' , null, B.Xu$Name, A.Xf$Name, ''REFERENCES'' ' +
            	 'FROM ' + :file_table_name + ' A, ' + :user_table_name +
                 ' B, ' + :rights_table_name + ' C WHERE A.Xf$Name LIKE ''' +
                 :table_name + ''' AND	B.Xu$Name LIKE ''' + :user_name +
                 ''' AND A.Xf$Id = C.Xr$Table AND B.Xu$Id = C.Xr$User AND ' +
        		 'C.Xr$rights & 144 = 144 AND ' +	
        		 'C.Xr$Column = 0 ' +
                 -- ALTER rights for Table
                 'UNION SELECT ''' + :database_qual + 
                 ''', null, B.Xu$Name, A.Xf$Name, ''ALTER'' FROM ' + 
                 :file_table_name + ' A, ' + :user_table_name + ' B, ' 
                 + :rights_table_name + ' C WHERE 	A.Xf$Name LIKE ''' + 
                 :table_name + ''' AND B.Xu$Name LIKE ''' + :user_name + 
                 ''' AND A.Xf$Id = C.Xr$Table ' +
                 'AND B.Xu$Id = C.Xr$User AND C.Xr$rights & 160 = 160 AND ' +	
        		 'C.Xr$Column = 0 ' + 
                 -- UPDATE rights for Table
                 'UNION	SELECT ''' + :database_qual + ''', null, B.Xu$Name, ' +
                 'A.Xf$Name, ''UPDATE'' FROM ' + :file_table_name +
                 ' A, ' + :user_table_name + ' B, ' + :rights_table_name + 
                 ' C WHERE	A.Xf$Name LIKE ''' + :table_name + 
                 ''' AND	B.Xu$Name LIKE ''' + :user_name + 
                 ''' AND A.Xf$Id = C.Xr$Table AND	B.Xu$Id = C.Xr$User AND ' +
        		 'C.Xr$rights & 130 = 130 AND ' +	
        		 'C.Xr$Column = 0 ' +
                 -- INSERT rights for Table
                 'UNION	SELECT ''' + :database_qual + ''' , ' +
                 'null, B.Xu$Name, A.Xf$Name, ''INSERT'' FROM ' + 
                 :file_table_name + ' A, ' +
                 :user_table_name + ' B, ' + :rights_table_name + ' C ' +
            	 'WHERE 	A.Xf$Name LIKE ''' + :table_name + 
                 ''' AND	B.Xu$Name ' +
                 'LIKE ''' + :user_name + ''' AND A.Xf$Id = C.Xr$Table AND ' +
                 'B.Xu$Id = C.Xr$User AND C.Xr$rights & 132 = 132 AND ' +	
        		 'C.Xr$Column = 0 ' + 
                  -- DELETE rights for Table
                 'UNION	SELECT ''' + :database_qual + ''', ' +
        		 'null, B.Xu$Name, 	A.Xf$Name, ''DELETE'' FROM ' + 
                 :file_table_name + ' A, ' + :user_table_name + ' B, ' + 
                 :rights_table_name + ' C WHERE	A.Xf$Name LIKE ''' + 
                 :table_name + ''' AND	B.Xu$Name ' +
                 'LIKE ''' + :user_name + ''' AND A.Xf$Id = C.Xr$Table AND ' +
            	 'B.Xu$Id = C.Xr$User AND C.Xr$rights & 136 = 136 AND ' +	
            	 'C.Xr$Column = 0'	;
    else 
        if (:version = 2) then  
            -- Metadata version is V2

            /* Enumerate the table rights */
                         -- SELECT rights for Table
            SET :query = 'SELECT ''' + :database_qual + ''' , null, ' +
                         'B.Xu$Name, A.Xf$Name, ''SELECT'' ' +
                         'FROM ' + :file_table_name + ' A, ' + 
                         :user_table_name + ' B, ' + :rights_table_name +
                         ' C WHERE 	A.Xf$Name LIKE ''' + :table_name +
                         ''' AND B.Xu$Name LIKE ''' + :user_name +
                         ''' AND A.Xf$Id = C.Xr$Object AND B.Xu$Id = C.Xr$User ' +
                         'AND C.Xr$rights & 64 = 64 AND	C.Xr$Column = 0 AND C.Xr$Type = 1 ' +
                         -- REFERENCES rights for Table
                         'UNION	SELECT ''' + :database_qual +
                         ''' , null, B.Xu$Name, A.Xf$Name, ''REFERENCES'' ' +
                         'FROM ' + :file_table_name + ' A, ' + :user_table_name +
                         ' B, ' + :rights_table_name + ' C WHERE A.Xf$Name LIKE ''' +
                         :table_name + ''' AND	B.Xu$Name LIKE ''' + :user_name +
                         ''' AND A.Xf$Id = C.Xr$Object AND B.Xu$Id = C.Xr$User AND ' +
                         'C.Xr$rights & 144 = 144 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 1 ' +
                         -- ALTER rights for Table
                         'UNION SELECT ''' + :database_qual + 
                         ''', null, B.Xu$Name, A.Xf$Name, ''ALTER'' FROM ' + 
                         :file_table_name + ' A, ' + :user_table_name + ' B, ' 
                         + :rights_table_name + ' C WHERE 	A.Xf$Name LIKE ''' + 
                         :table_name + ''' AND B.Xu$Name LIKE ''' + :user_name + 
                         ''' AND A.Xf$Id = C.Xr$Object ' +
                         'AND B.Xu$Id = C.Xr$User AND C.Xr$rights & 160 = 160 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 1 ' + 
                         -- UPDATE rights for Table
                         'UNION	SELECT ''' + :database_qual + ''', null, B.Xu$Name, ' +
                         'A.Xf$Name, ''UPDATE'' FROM ' + :file_table_name +
                         ' A, ' + :user_table_name + ' B, ' + :rights_table_name + 
                         ' C WHERE	A.Xf$Name LIKE ''' + :table_name + 
                         ''' AND	B.Xu$Name LIKE ''' + :user_name + 
                         ''' AND A.Xf$Id = C.Xr$Object AND	B.Xu$Id = C.Xr$User AND ' +
                         'C.Xr$rights & 130 = 130 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 1 ' +
                         -- INSERT rights for Table
                         'UNION	SELECT ''' + :database_qual + ''' , ' +
                         'null, B.Xu$Name, A.Xf$Name, ''INSERT'' FROM ' + 
                         :file_table_name + ' A, ' +
                         :user_table_name + ' B, ' + :rights_table_name + ' C ' +
                         'WHERE 	A.Xf$Name LIKE ''' + :table_name + 
                         ''' AND	B.Xu$Name ' +
                         'LIKE ''' + :user_name + ''' AND A.Xf$Id = C.Xr$Object AND ' +
                         'B.Xu$Id = C.Xr$User AND C.Xr$rights & 132 = 132 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 1 ' + 
                          -- DELETE rights for Table
                         'UNION	SELECT ''' + :database_qual + ''', ' +
                         'null, B.Xu$Name, 	A.Xf$Name, ''DELETE'' FROM ' + 
                         :file_table_name + ' A, ' + :user_table_name + ' B, ' + 
                         :rights_table_name + ' C WHERE	A.Xf$Name LIKE ''' + 
                         :table_name + ''' AND	B.Xu$Name ' +
                         'LIKE ''' + :user_name + ''' AND A.Xf$Id = C.Xr$Object AND ' +
                         'B.Xu$Id = C.Xr$User AND C.Xr$rights & 136 = 136 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 1 '	;
        end if;                     
    end if;
    
    exec(:query);

END; -- End of the stored procedure.



?
/*
 * This system procedure is used to return the
 * list of triggers defined from the database specified
 */
CREATE PROCEDURE psp_triggers
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :table_name      VARCHAR(255) = null -- Name of the table
)

RETURNS
(
    TRIGGER_QUALIFIER   VARCHAR (20), -- Name of the database that contains the trigger
    TRIGGER_OWNER       VARCHAR (20),  -- owner of the trigger
    TABLE_NAME          VARCHAR(255), -- Name of the table
    TRIGGER_NAME        VARCHAR(255), -- Name of the trigger
    ISUPDATE            UTINYINT, -- Update trigger event
    ISDELETE            UTINYINT, -- Delete trigger event
    ISINSERT            UTINYINT, -- Insert trigger event
    ISAFTER             UTINYINT, -- Trigger action time - after
    ISBEFORE            UTINYINT,  -- Trigger action time - before
    REMARKS             VARCHAR(255)	-- Remarks
);

BEGIN
    -- Variable to hold the dynamic query   
    DECLARE :stmt_psp_triggers LONGVARCHAR;
    DECLARE :table_trigger VARCHAR(30);
    DECLARE :table_file  VARCHAR(30);

    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

     -- Check if the table name specified is a blank string
	if (rtrim(:table_name) = '') then
		SIGNAL 'S1000', 'Table name cannot be a blank string'; 
	end if;
    
	SET :database_qual = IFNULL(:database_qual,DATABASE());
	/* If table name is not supplied or value is null
	   match all */
	SET :table_name = IFNULL(:table_name,'%');
	
	/* Enumerate the triggers information */
    set :table_trigger = '"' + :database_qual + '".X$Trigger' ;
    set :table_file = '"' + :database_qual + '".X$File B' ;
	
    SET :stmt_psp_triggers = ' SELECT ''' +
								:database_qual +
								''',  null, ' +
								' B.Xf$Name, ' +
								' A.Xt$Name, ' +
								' IF(A.Xt$Event = 2, 1, 0), ' + 
								' IF(A.Xt$Event = 1, 1, 0), ' +
						        ' IF(A.Xt$Event = 0, 1, 0), ' +
								' IF(A.Xt$ActionTime = 1, 1, 0), ' +
								' IF(A.Xt$ActionTime = 0, 1, 0), ' +
								' null ' +
								' FROM ' + :table_trigger +' A, ' + 
										:table_file  +
								' WHERE A.Xt$File = B.Xf$Id AND A.Xt$Name ' +
                                'LIKE ''' +	:table_name + ''';'
    exec (:stmt_psp_triggers);
END; -- End of stored procedure.

?
/**
 * This system procedure is used to return
 * the list of udfs created, from the database specified.
 */
CREATE PROCEDURE psp_udfs
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :udf_name        VARCHAR(255) = NULL  -- Name of the user defined function
) 

RETURNS
(
    UDF_QUALIFIER       VARCHAR (20), -- Name of the database that contains the User Defined Function
    UDF_OWNER           VARCHAR (20),  -- owner of the User Defined Function
    UDF_NAME            VARCHAR(255),      -- Name of the user defined function
    UDF_TYPE            UTINYINT,      -- Type of the user defined function
	NUM_INPUT_PARAMS    INTEGER, -- no. of input parameters
	NUM_OUTPUT_PARAMS   INTEGER, -- no. of output parameters
	NUM_RESULT_SETS     INTEGER, -- no. of columns in result set.
    REMARKS             VARCHAR(255)	-- Remarks
);

BEGIN
   -- Variable to hold the dynamic query    
    DECLARE :stmt_psp_udfs LONGVARCHAR;

    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

     -- Check if the table name specified is a blank string
	if (rtrim(:udf_name) = '') then
		SIGNAL 'S1000', 'User-defined function name cannot be a blank string'; 
	end if;
    
	SET :database_qual = IFNULL(:database_qual,DATABASE());

	/* If udf name is not supplied or null value
	   match all */	
	SET :udf_name = IFNULL(:udf_name,'%');
	
        /* Enumerate all the udfs information */

    SET :stmt_psp_udfs = 'SELECT ''' +
						   :database_qual +
						   ''', null, ' +		
							' Xp$Name, ' +
							' Xp$Flags, ' +
							' null, ' +
							' 1, ' + 
							' 0, ' +
							' null ' +
							' FROM "' + :database_qual + '".X$Proc ' +
       						' WHERE Xp$Name LIKE ''' + :udf_name +
							''' and Xp$Flags = 1; '
    exec (:stmt_psp_udfs);
END; -- End of stored procedure.

?
/*
 * This system procedure is used to return the list
 * of users and the corresponding information from the
 * database specified.
 */
CREATE PROCEDURE psp_users
(
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :group_name      VARCHAR(254) = NULL, -- Name of the group
    in :user_name       VARCHAR(254) = NULL  -- Name of the user
)
RETURNS
(
    DATABASE_QUALIFIER  VARCHAR(20), -- Database Name
    GROUP_ID            USMALLINT, -- Group ID 
    GROUP_NAME          VARCHAR(254),  -- Name of the group
    USER_ID             USMALLINT, -- User ID
    USER_NAME           VARCHAR(254)   -- Name of the user
);

BEGIN
    -- This variable stores the dynamic query
	DECLARE :query      LONGVARCHAR;
	-- This variable stores the table name concatenated with 
	-- the database qualifier in the form dbname.tablename
    DECLARE :table_name VARCHAR(30);

    -- Check if the database qualifier is a blank string
    IF (rtrim(:database_qual) = '') THEN
        SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string';
    END IF;

    -- Check if the user name is a blank string
	IF (rtrim(:user_name) = '') THEN
        SIGNAL 'S1000', 'Please enter a valid user name. User name cannot be a  blank string';
    END IF;
    
    -- Check if the group name is a blank string
    IF (rtrim(:group_name) = '') THEN
        SIGNAL 'S1000', 'Please enter a valid group name. Group name cannot be a blank string';
    END IF;
    
	set :database_qual = IFNULL(:database_qual,DATABASE());

	/*	If user name is null or not supplied, 
	    match all the users */
	set :user_name  = IFNULL(:user_name,'%');

    set :table_name = '"' + :database_qual + '".X$User';
  	
	/* Enumerate all the users information */
	if :group_name like 'PUBLIC' THEN
	         SET :query = 'SELECT ''' + :database_qual + ''', A.Xu$Id, A.Xu$Name, ' +
                     ' B.Xu$Id, B.Xu$Name ' +
                     ' FROM ' + :table_name + ' A, ' + :table_name + 
                     ' B WHERE A.Xu$Flags & 64 = 64 AND ' + 
                     ' A.Xu$Name LIKE ''' + :group_name + ''' AND ' +
                     ' B.Xu$Name LIKE ''' + :user_name  +
                     ''' AND B.Xu$Flags & 64 = 0 AND B.Xu$Id > 0' ;
	 else
    IF( (:group_name IS NOT NULL) AND (:group_name not like 'PUBLIC'))THEN
    SET :query = 'SELECT ''' + :database_qual + ''', A.Xu$Id, A.Xu$Name, ' +
                     ' B.Xu$Id, B.Xu$Name ' +
                     ' FROM ' + :table_name + ' A, ' + :table_name + 
                     ' B WHERE A.Xu$Flags & 64 = 64 AND ' + 
                     ' A.Xu$Name LIKE ''' + :group_name + ''' AND ' +
                     ' B.Xu$Id = A.Xu$Id AND B.Xu$Name LIKE ''' + :user_name  +
                     ''' AND B.Xu$Flags & 64 = 0 ' ;
    ELSE
          SET :query = 'SELECT ''' + :database_qual + 
          ''', if(B.xu$name LIKE ' + '''Master''' + ', NULL, A.xu$id),' + 
          'if(B.xu$name LIKE ' + '''Master''' + ', NULL, A.xu$name), ' + 
                     'B.Xu$Id, B.Xu$Name FROM ' + :table_name + 
                     ' A , ' + :table_name + 
                     ' B WHERE B.Xu$Name LIKE ''' + :user_name + 
                     ''' AND A.Xu$Flags & 64 = 64 AND' + ' B.Xu$Flags & 64 = 0 AND ' + 
                     'A.xu$id = 1' + ' AND b.xu$name' + ' not IN ' + '(select B.xu$name from' + 
                     :table_name + ' A , ' + :table_name + 
                     ' B WHERE B.xu$flags & 64 =0 AND A.xu$flags & 64 =64 AND A.xu$id = B.xu$id) union ' + 
                     ' (select ''' + :database_qual + ''' , AA.xu$id, AA.xu$name, BB.xu$id, BB.xu$name from ' + :table_name + ' AA, ' + 
                     :table_name + ' BB where ' + ' BB.Xu$Name LIKE ''' + :user_name  + '''AND BB.xu$flags & 64 =0 AND AA.xu$flags & 64=64 AND AA.xu$id = BB.xu$id)';
    END IF;
        END IF;
        exec (:query);

END; -- End of stored procedure.


?
/**
 * This system procedure is used to return the 
 * list of views from the database specified.
 */
CREATE PROCEDURE psp_views
(   
	in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :view_name       VARCHAR(255) = NULL -- Name of the view
)

RETURNS
(
    VIEW_QUALIFIER  VARCHAR (20), -- Name of the database that contains the View
    VIEW_OWNER      VARCHAR (20),  -- owner of the View
    VIEW_NAME       VARCHAR(255), -- Name of the view
    REMARKS         VARCHAR(255),	-- Remarks
    TRUSTEE         INT          -- Trustee column for V2
);

BEGIN
    -- Variable to hold the dynamic query 
    DECLARE :stmt_psp_views LONGVARCHAR;

    -- Variable to store the Database version
    DECLARE :version INTEGER;

    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;

     -- Check if the view name specified is a blank string
	if (rtrim(:view_name) = '') then
		SIGNAL 'S1000', 'View name cannot be a blank string'; 
	end if;
    
	/*	If view name is null or not supplied, 
	    match all the views */

	SET :database_qual = IFNULL(:database_qual,database());

	SET :view_name = IFNULL(:view_name,'%');

    -- Get the DB Version
	CALL psp_database_version(:database_qual, :version);

    IF (:version = 1) THEN
	set :stmt_psp_views  = 'SELECT ''' +
						   	:database_qual + 
							''', null, ' + 
							' Xv$Name, ' +
                                ' null, null '+
							' FROM "'+ :database_qual + '".X$View ' +
							' WHERE Xv$Name LIKE ''' + :view_name + ''';'
    ELSE
    --IF (:version = 2) THEN
	set :stmt_psp_views  = 'SELECT ''' +
						   	:database_qual + 
							''', null, ' + 
							' Xv$Name, ' +
                                ' null, Xv$Trustee '+
							' FROM "'+ :database_qual + '".X$View ' +
							' WHERE Xv$Name LIKE ''' + :view_name + ''';'
    END IF;                                
        

    exec (:stmt_psp_views);
END; -- End of stored procedure.


?
/**
 * This system procedure is used to rename objects.
 *
 * Arguments
 *   object_name - the current name of the user object (table, view, column,
 *   stored procedure, user defined function, trigger, or index. If the object
 *   to be renamed is a column in a table, object_name must be in the form
 *   table.column. If the object to be renamed is an index, object_name must be
 *   in the form table.index. If the object to be renamed is a trigger,
 *   object_name must be in the form table.trigger. If the object to be renamed
 *   is a table, object_name must be in the form table. If the object to be renamed
 *   is a view, object_name must be in the form view. If the object to be renamed
 *   is a procedure, object_name must be in the form procedure. If the object to
 *   be renamed is a function, object_name must be in the form function.
 *   
 *   new_name - is the new name for the specified object. new_name must be a one-part
 *   name and must follow the rules for identifiers.
 *   
 *   object_type - is the type of object being renamed. object_type is varchar(13)
 *                 and can be one of these values:
 *                 COLUMN
 *                 INDEX
 *                 TRIGGER
 *                 TABLE
 *                 VIEW
 *                 PROCEDURE
 *                 FUNCTION
 */
CREATE PROCEDURE psp_rename
(
    in :object_name VARCHAR(776),   -- original object name
    in :new_name VARCHAR(776),      -- new object name
    in :object_type VARCHAR(13)     -- object type
)
as
BEGIN
    -- Variable to hold the dynamic query 
    DECLARE :stmt_psp_rename VARCHAR(1552);

    -- variable to keep table name
    DECLARE :table_name VARCHAR(776);

    -- variable to keep column name
    DECLARE :column_name VARCHAR(776);

    -- variable to keep table nam
    DECLARE :dot_position INTEGER;

    if (upper (:object_type) = 'TABLE') then

        if (position ('.', :object_name) > 0) then
            SIGNAL 'ST100', 'Table name cannot be qualified with database name.';
        end if -- end of check for dot in the name

        SET :stmt_psp_rename = 'ALTER TABLE RENAME ' + :object_name + ' TO ' + :new_name;

    else
        if (upper (:object_type) = 'VIEW') then

            if (position ('.', :object_name) > 0) then
                SIGNAL 'ST100', 'View name cannot be qualified with database name.';
            end if -- end of check for dot in the name

            SET :stmt_psp_rename = 'ALTER VIEW RENAME ' + :object_name + ' TO ' + :new_name;

        else
            if (upper (:object_type) = 'PROCEDURE') then

                if (position ('.', :object_name) > 0) then
                    SIGNAL 'ST100', 'Procedure name cannot be qualified with database name.';
                end if -- end of check for dot in the name

                SET :stmt_psp_rename = 'ALTER PROCEDURE RENAME ' + :object_name + ' TO ' + :new_name;

            else
                if (upper (:object_type) = 'FUNCTION') then

                    if (position ('.', :object_name) > 0) then
                        SIGNAL 'ST100', 'Function name cannot be qualified with database name.';
                    end if -- end of check for dot in the name

                    SET :stmt_psp_rename = 'ALTER FUNCTION RENAME ' + :object_name + ' TO ' + :new_name;

                else
                    if (upper (:object_type) = 'INDEX') then

                        SET :dot_position = position ('.', :object_name);

                        if (:dot_position > 0) then
                            SET :table_name = substring (:object_name, 1, :dot_position - 1);
                            SET :column_name = substring (:object_name, :dot_position + 1,
                                                          length (:object_name) - :dot_position);
                        else
                            SIGNAL 'ST100', 'Table name qualifier required for index renaming.';
                        end if

                        SET :stmt_psp_rename = 'ALTER INDEX RENAME ' + :object_name + ' TO ' + :new_name;

                    else

                        if (upper (:object_type) = 'TRIGGER') then

                            SET :dot_position = position ('.', :object_name);

                            if (:dot_position > 0) then
                                SET :table_name = substring (:object_name, 1, :dot_position - 1);
                                SET :column_name = substring (:object_name, :dot_position + 1,
                                                              length (:object_name) - :dot_position);
                            else
                                SIGNAL 'ST100', 'Table name qualifier required for trigger renaming.';
                            end if

                            SET :stmt_psp_rename = 'ALTER TRIGGER RENAME ' + :object_name + ' TO ' + :new_name;

                        else

                            if (upper (:object_type) = 'COLUMN') then

                                -- find the position of the dot in the table qualified name
                                SET :dot_position = position ('.', :object_name);

                                if (:dot_position > 0) then
                                    SET :table_name = substring (:object_name, 1, :dot_position - 1);
                                    SET :column_name = substring (:object_name, :dot_position + 1,
                                                                  length (:object_name) - :dot_position);
                                else
                                    SIGNAL 'ST100', 'Table name qualifier required for column renaming.';
                                end if

                                SET :stmt_psp_rename = 'ALTER TABLE ' + :table_name + 
                                                       ' RENAME COLUMN ' + :column_name + ' TO ' + :new_name;

                            else
                                SIGNAL 'ST100', 'Invalid object type. Type must be one of the following: Column, Function, Index, Procedure, Table, Trigger, or View.';
                            end if -- end if COLUMN object type comparision

                        end if -- end if TRIGGER object type comparision

                    end if -- end if INDEX object type comparision

                end if -- end if FUNCTION object type comparision

            end if -- end if PROCEDURE object type comparision

        end if -- end if VIEW type comparision

    end if -- end if TABLE type comparision

  execute (:stmt_psp_rename);

END; -- End of stored procedure.


?
/**
 * This system procedure is used to return the
 * the view rights for the given view(s).
 */
CREATE PROCEDURE psp_view_rights
(  
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :view_name       VARCHAR(255) = NULL, -- Name of the view
    in :user_name       VARCHAR(255) = USER() -- Name of the user
)
RETURNS
(
    VIEW_QUALIFIER      VARCHAR (20), -- Name of the database that contains the view
    VIEW_OWNER          VARCHAR (20),  -- owner of the view
    USER_NAME           CHAR(255), -- Name of the user
    VIEW_NAME           CHAR(255), -- Name of the view
    RIGHTS              VARCHAR(12)  -- View rights 
);
BEGIN

	-- This variable stores the dynamic query
	DECLARE :query              LONGVARCHAR;
    
	-- This variable stores the view name concatenated with 
	-- the database qualifier in the form dbname.viewname
    DECLARE :view_table_name    VARCHAR(30);
    DECLARE :rights_table_name  VARCHAR(30);
    DECLARE :user_table_name    VARCHAR(30);

	-- This variable stores the metadata version
    DECLARE :version           INTEGER;

    -- If the database qualifier is null, then use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;
    
    -- Check if the column name is a blank string
	if (rtrim(:view_name) = '') then
		SIGNAL 'S1000', 'View name cannot be blank string';
	end if;		
    
    -- Check if the user name is a blank string
	if (rtrim(:user_name) = '') then
		SIGNAL 'S1000', 'User name cannot be a blank string';
	end if;
	
	   
    SET :view_table_name = '"' + :database_qual + '".X$View';
    SET :rights_table_name = '"' + :database_qual + '".X$Rights';
    SET :user_table_name = '"' + :database_qual + '".X$User';

	/* If view name is not supplied or value is null
	   match all the views*/
	   
    SET :view_name = IFNULL(:view_name,'%');
    SET :user_name = IFNULL(:user_name , USER());
  
    -- call procedure to find the metadata version and store it in a variable
    CALL psp_database_version(:database_qual, :version);

    if (:version = 1) then  
        -- Metadata version is V1
        SIGNAL 'S1000', 'View and Stored Procedure permissions are not supported on metadata version 1';   
    else 
        if (:version = 2) then  
            -- Metadata version is V2

            /* Enumerate the view rights */
                         -- SELECT rights for View
            SET :query = 'SELECT ''' + :database_qual + ''' , null, ' +
                         'B.Xu$Name, A.Xv$Name, ''SELECT'' ' +
                         'FROM ' + :view_table_name + ' A, ' + 
                         :user_table_name + ' B, ' + :rights_table_name +
                         ' C WHERE 	A.Xv$Name LIKE ''' + :view_name +
                         ''' AND B.Xu$Name LIKE ''' + :user_name +
                         ''' AND A.Xv$Id = C.Xr$Object AND B.Xu$Id = C.Xr$User ' +
                         'AND C.Xr$rights & 64 = 64 AND	C.Xr$Column = 0 AND C.Xr$Type = 4 ' +
                         -- UPDATE rights for View
                         'UNION	SELECT ''' + :database_qual + ''', null, B.Xu$Name, ' +
                         'A.Xv$Name, ''UPDATE'' FROM ' + :view_table_name +
                         ' A, ' + :user_table_name + ' B, ' + :rights_table_name + 
                         ' C WHERE	A.Xv$Name LIKE ''' + :view_name + 
                         ''' AND	B.Xu$Name LIKE ''' + :user_name + 
                         ''' AND A.Xv$Id = C.Xr$Object AND	B.Xu$Id = C.Xr$User AND ' +
                         'C.Xr$rights & 130 = 130 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 4 ' +
                         -- INSERT rights for View
                         'UNION	SELECT ''' + :database_qual + ''' , ' +
                         'null, B.Xu$Name, A.Xv$Name, ''INSERT'' FROM ' + 
                         :view_table_name + ' A, ' +
                         :user_table_name + ' B, ' + :rights_table_name + ' C ' +
                         'WHERE 	A.Xv$Name LIKE ''' + :view_name + 
                         ''' AND	B.Xu$Name ' +
                         'LIKE ''' + :user_name + ''' AND A.Xv$Id = C.Xr$Object AND ' +
                         'B.Xu$Id = C.Xr$User AND C.Xr$rights & 132 = 132 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 4 ' + 
                          -- DELETE rights for View
                         'UNION	SELECT ''' + :database_qual + ''', ' +
                         'null, B.Xu$Name, 	A.Xv$Name, ''DELETE'' FROM ' + 
                         :view_table_name + ' A, ' + :user_table_name + ' B, ' + 
                         :rights_table_name + ' C WHERE	A.Xv$Name LIKE ''' + 
                         :view_name + ''' AND	B.Xu$Name ' +
                         'LIKE ''' + :user_name + ''' AND A.Xv$Id = C.Xr$Object AND ' +
                         'B.Xu$Id = C.Xr$User AND C.Xr$rights & 136 = 136 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 4 ' +

                          -- ALTER rights for View
                         'UNION	SELECT ''' + :database_qual + ''', ' +
                         'null, B.Xu$Name, 	A.Xv$Name, ''ALTER'' FROM ' + 
                         :view_table_name + ' A, ' + :user_table_name + ' B, ' + 
                         :rights_table_name + ' C WHERE	A.Xv$Name LIKE ''' + 
                         :view_name + ''' AND	B.Xu$Name ' +
                         'LIKE ''' + :user_name + ''' AND A.Xv$Id = C.Xr$Object AND ' +
                         'B.Xu$Id = C.Xr$User AND C.Xr$rights & 160 = 160 AND ' +	
                         'C.Xr$Column = 0 AND C.Xr$Type = 4 '	;
        end if;                     
    end if;
    
    exec(:query);

END; -- End of the stored procedure.


?
/**
 * This system procedure is used to return the
 * the procedure rights for the given procedure(s).
 */
CREATE PROCEDURE psp_procedure_rights
(  
    in :database_qual   VARCHAR(20) = DATABASE(), -- Name of the database
    in :procedure_name  VARCHAR(255) = NULL, -- Name of the procedure
    in :user_name       VARCHAR(255) = USER() -- Name of the user
)
RETURNS
(
    PROCEDURE_QUALIFIER     VARCHAR (20), -- Name of the database that contains the procedure
    PROCEDURE_OWNER         VARCHAR (20),  -- owner of the procedure
    USER_NAME           CHAR(255), -- Name of the user
    PROCEDURE_NAME      CHAR(255), -- Name of the procedure
    RIGHTS              VARCHAR(12)  -- Procedure rights 
);
BEGIN

	-- This variable stores the dynamic query
	DECLARE :query              LONGVARCHAR;
    
	-- This variable stores the procedure name concatenated with 
	-- the database qualifier in the form dbname.procedurename
    DECLARE :procedure_table_name    VARCHAR(30);
    DECLARE :rights_table_name  VARCHAR(30);
    DECLARE :user_table_name    VARCHAR(30);

    -- Variable to store the Metadata version
    DECLARE :version INTEGER;

    -- If the database qualifier is null, then use the current database
	if (:database_qual is null) then
		set :database_qual = DATABASE();
	end if;
	
    -- Check if the database qualifier specified is a blank string
	if (rtrim(:database_qual) = '') then
		SIGNAL 'S1000', 'Please enter a valid database name. Database name cannot be a blank string'; 
	end if;
    
    -- Check if the column name is a blank string
	if (rtrim(:procedure_name) = '') then
		SIGNAL 'S1000', 'Procedure name cannot be blank string';
	end if;		
    
    -- Check if the user name is a blank string
	if (rtrim(:user_name) = '') then
		SIGNAL 'S1000', 'User name cannot be a blank string';
	end if;
	
	   
    SET :procedure_table_name = '"' + :database_qual + '".X$Proc';
    SET :rights_table_name = '"' + :database_qual + '".X$Rights';
    SET :user_table_name = '"' + :database_qual + '".X$User';
    
	/* If procedure name is not supplied or value is null
	   match all the tables*/
	   
    SET :procedure_name = IFNULL(:procedure_name,'%');
    SET :user_name = IFNULL(:user_name , USER());

    -- Get the Metadata Version
    CALL psp_database_version(:database_qual, :version);

    IF (:version = 1) THEN
    
	SIGNAL 'S1000', 'View and Stored Procedure permissions are not supported for metadata version 1';

    ELSE
     SET :query = 'SELECT ''' + :database_qual + ''' , null, ' +
        		 'B.Xu$Name, A.Xp$Name, ''EXECUTE'' ' +
            	 'FROM ' + :procedure_table_name + ' A, ' + 
                 :user_table_name + ' B, ' + :rights_table_name +
                 ' C WHERE 	A.Xp$Name LIKE ''' + :procedure_name +
                 ''' AND B.Xu$Name LIKE ''' + :user_name +
                 ''' AND A.Xp$Id = C.Xr$Object AND B.Xu$Id = C.Xr$User ' +
                 'AND C.Xr$rights & 192 = 192 AND C.Xr$Column = 0 AND C.Xr$Type = 3 ' + 
                 'UNION SELECT ''' + :database_qual + ''' , null, ' +
        		 'B.Xu$Name, A.Xp$Name, ''ALTER'' ' +
            	 'FROM ' + :procedure_table_name + ' A, ' + 
                 :user_table_name + ' B, ' + :rights_table_name +
                 ' C WHERE 	A.Xp$Name LIKE ''' + :procedure_name +
                 ''' AND B.Xu$Name LIKE ''' + :user_name +
                 ''' AND A.Xp$Id = C.Xr$Object AND B.Xu$Id = C.Xr$User ' +
                 'AND C.Xr$rights & 160 = 160 AND C.Xr$Column = 0 AND C.Xr$Type = 3 ' ;
    END IF;
		 
    exec(:query);

END;
 -- End of the stored procedure.



?

CREATE PROCEDURE psp_dumpTable ( IN :tableName VARCHAR(128), IN :inDict INT DEFAULT 0, OUT :sqlString LONGVARCHAR ) 
WITH DEFAULT HANDLER AS
BEGIN

  DECLARE :fId             UINT;
  DECLARE :fName           VARCHAR(128);
  DECLARE :fLoc            VARCHAR(250);
  DECLARE :fFlags          UINT;
  DECLARE :iWarning        INT = 0;
  DECLARE :iWarning2       INT = 0;

  DECLARE :iPageSize        INT = 0;
  DECLARE :iUnusedLinkedDup INT = 0;
  DECLARE :dCompress        VARCHAR(5);
  DECLARE :pCompress        VARCHAR(5);
  DECLARE :iUsedLinkedDup   INT = 0;
  DECLARE :sysData          VARCHAR(16);

  DECLARE :setStmtTrueNull VARCHAR(30) = 'SET TRUENULLCREATE = ON;';
  DECLARE :setStmtLegacy   VARCHAR(30) = 'SET TRUENULLCREATE = OFF;';

  SELECT "Page Size", "Unused LinkedDup Ptr", "Record Compression", "Page Compression", "Used LinkedDup Ptr", "System Data" INTO :iPageSize, :iUnusedLinkedDup, :dCompress, :pCompress, :iUsedLinkedDup, :sysData FROM DBO.fSQLDBTableStat( :tableName );
  SELECT xf$Id, xf$Name, xf$Loc, xf$Flags INTO :fId, :fName, :fLoc, :fFlags FROM x$File WHERE xf$Name = :tableName;

  IF SQLSTATE = '00000' THEN

    DECLARE :columnName     VARCHAR(128);
    DECLARE :dataType       INT;
    DECLARE :typeName       VARCHAR(128);
    DECLARE :columnSize     INT;
    DECLARE :decimalDigits  INT;
    DECLARE :columnDef      VARCHAR(128);
    DECLARE :isNullable     INT;
    DECLARE :typeDef        VARCHAR(128);
    
    DECLARE :eType          INT;
    DECLARE :eSize          INT;
    DECLARE :eId            UINT;
    DECLARE :eDec           UINT;
    DECLARE :eFlags         UINT;
    DECLARE :eConstName     VARCHAR(128);
    DECLARE :eAutoKeyNum    INT = NULL;
    DECLARE :eKeyNum        INT = NULL;
    DECLARE :eOffset        INT;
    DECLARE :ePrevOffset    INT = 99999999;
    DECLARE :ePrevType      INT = 11111111;

    DECLARE :bitCounts      INT = 0;
    DECLARE :bitOffset      INT = 0;
    DECLARE :alterTable     INT = 0;
    DECLARE :bitNoHoles     INT = 0;

    DECLARE :alterTableString VARCHAR(256);
    DECLARE :dropColString    VARCHAR(512);

    DECLARE :aAttrs         VARCHAR(17);
    DECLARE :aType          CHAR(1);

    DECLARE :aAttrsM        VARCHAR(100);
    DECLARE :aTypeM         CHAR(1);

    DECLARE :firstColumn    INT = 1;
    DECLARE :error          INT = 0;
    DECLARE :inDictionary   VARCHAR(16);

    DECLARE :tblOptions     VARCHAR(100);
    DECLARE :totalLinkedDup INT = 0;

    DECLARE :iFlags         UINT;
    DECLARE :notUniqueUsing VARCHAR(20);

    DECLARE :tableName2     VARCHAR(256);

    IF ( :iPageSize IS NULL ) THEN
      SET :tblOptions = '';
    ELSE

      IF ( :dCompress = 'YES' ) THEN
        SET :tblOptions = 'DCOMPRESS ';
      	IF ( :pCompress = 'YES' ) THEN
          SET :tblOptions = :tblOptions + 'PCOMPRESS ';
        END IF;
      ELSE
        IF ( :pCompress = 'YES' ) THEN
          SET :tblOptions = 'PCOMPRESS ';
        ELSE
          SET :tblOptions = '';
        END IF;
      END IF;

      IF ( :sysData = 'YES(sys$update)' ) THEN
        SET :tblOptions = :tblOptions + 'SYSDATA_KEY_2 ';
      END IF;

      IF ( :iPageSize > 0 ) THEN
        SET :tblOptions = :tblOptions + 'PAGESIZE=' + CAST ( :iPageSize AS VARCHAR(6) ) + ' ';
      END IF;

      SET :totalLinkedDup = :iUnusedLinkedDup + :iUsedLinkedDup;
      IF ( :totalLinkedDup > 0 ) THEN
        SET :tblOptions = :tblOptions + 'LINKDUP=' + CAST ( :totalLinkedDup AS VARCHAR(4) ) + ' ';
      END IF;

    END IF;

    SELECT REPLACE( :tableName, '\', '\\')  INTO :tableName2;
    SELECT REPLACE( :tableName2, '%', '\%') INTO :tableName2;
    SELECT REPLACE( :tableName2, '_', '\_') INTO :tableName2;

    DECLARE columnCursor CURSOR FOR
      SELECT "COLUMN_NAME",  "DATA_TYPE", "TYPE_NAME", "PRECISION", "SCALE", "COLUMN_DEF", "NULLABLE" FROM dbo.fSQLColumns (NULL, :tableName2, NULL) ORDER BY "ORDINAL_POSITION";
 
    IF ( :inDict = 0 ) THEN
      SET :inDictionary = ' ';
    ELSE
      SET :inDictionary = ' IN DICTIONARY ';
    END IF;

    IF ( (:fFlags & 64) = 0 ) THEN
      SET :sqlString = :setStmtLegacy + 'CREATE TABLE ' + '"' + RTRIM(:fName) + '"' + :inDictionary  + 'USING ''' + RTRIM(:fLoc) + ''' ' + :tblOptions;
      SET :alterTableString = 'ALTER TABLE ' + '"' + RTRIM(:fName) + '"';
    ELSE
      SET :sqlString = 'CREATE TABLE ' + '"' + RTRIM(:fName) + '"' + :inDictionary + 'USING ''' + RTRIM(:fLoc) + ''' ' + :tblOptions;
      SET :alterTableString = 'ALTER TABLE ' + '"' + RTRIM(:fName) + '"' + :inDictionary;
    END IF;

    SET :dropColString = '';
    SET SQLSTATE = '00000';

    OPEN columnCursor;

    ColumnCursorLoop:
    LOOP

      FETCH NEXT FROM columnCursor INTO :columnName, :dataType, :typeName, :columnSize, :decimalDigits, :columnDef, :isNullable;

      IF SQLSTATE = '02000' THEN

        SET :error = 100;
        SET :sqlString = :sqlString + ' );'

        IF ( (:fFlags & 64) = 0 ) THEN
           SET :sqlString = :sqlString + :setStmtTrueNull;
        END IF;

        LEAVE ColumnCursorLoop;

      ELSE

        IF SQLSTATE = '00000' OR SQLSTATE = '01004' THEN

           IF ( :firstColumn = 1 ) THEN

             SET :sqlString = :sqlString + ' ( ';
             SET :firstColumn = 0;

           ELSE
             SET :sqlString = :sqlString + ', ';
           END IF;

        ELSE
           SET :error = -1;
        END IF;
      END IF;

      -- for legacy types including lstring, note, lvar
      SELECT TOP 1 xe$Id, xe$DataType, xe$Size, xe$Dec, xe$Flags, xe$Offset INTO :eId, :eType, :eSize, :eDec, :eFlags, :eOffset FROM x$Field WHERE xe$File = :fId AND xe$Name = :columnName;
      -- for collate info
      SELECT TOP 1 RTRIM( SUBSTRING(xa$Attrs, 2) ), xa$Type INTO :aAttrs, :aType FROM x$Attrib WHERE xa$Id = :eId AND ( LEFT( xa$Type, 1 ) = 'O' OR LEFT( xa$Type, 1 ) = 'I' );
      SELECT TOP 1 xa$Attrs, xa$Type INTO :aAttrsM, :aTypeM FROM x$Attrib WHERE xa$Id = :eId AND LEFT( xa$Type, 1 ) = 'M';

      SET SQLSTATE = '00000';
 
      -- handling error conditions
      IF (    ( :eType = 1 AND :eSize != 1 AND :eSize != 2 AND :eSize != 4 AND :eSize != 8 )                            -- signed
           OR ( :eType = 14 AND (:eFlags & 4096 = 0) AND  :eSize != 1 AND :eSize != 2 AND :eSize != 4 AND :eSize != 8 ) -- unsigned
           OR ( :eType= 2 AND :eSize != 4 AND :eSize != 8 )                                                             -- float
           OR ( :eType= 9 AND :eSize != 4 AND :eSize != 8 )                                                             -- bfloat
           OR ( (:eType = 10 OR :eType = 11) AND :eSize = 1 )                                                           -- lstring/zstring
           OR ( :eSize = 0 )                                                                                            -- all types
           OR ( (:eType = 3 OR :eType = 4) AND :eSize != 4 )                                                            -- date/time
           OR ( (:eType = 20 OR :eType = 30 OR :eType = 32 OR :eType = 34) AND :eSize != 8 ) ) THEN                     -- timestamp/datetime/autotimestamp/timestamp2
        -- SET :sqlString = ' -- Column ' + :columnName + ' in table ' + :tableName + ' has an incorrect size of ' + CAST( :eSize AS VARCHAR(3) );
        SET :iWarning = 1;
        SET :iWarning2 = 1;
        -- LEAVE ColumnCursorLoop;
      END IF;

      -- handling AUTOINC/AUTOTIMESTAMP
      IF ( :eType = 15 OR :eType = 32 ) THEN

        SELECT xe$Name, xe$Offset, xe$Flags INTO :eConstName, :eAutoKeyNum, :iFlags
        FROM x$Field D, 
             ( SELECT COUNT(*) c , xi$Number FROM ( 
             SELECT xi$Number, xi$Part,  xe$Id  FROM x$Index I, x$Field E WHERE E.xe$File = :fId and  I.xi$File = :fId  and E.xe$Id = I.xi$Field ) A 
             GROUP BY A.xi$Number HAVING c = 1 ) B,
             x$Index G
        WHERE D.xe$File = :fId and D.xe$Datatype = 255 and B.xi$Number = D.xe$Offset and G.xi$Number = B.xi$Number and G.xi$Field = :eId;

        IF :eAutoKeyNum IS NULL THEN  -- index name is missing

          SELECT xi$Number INTO :eAutoKeyNum FROM x$index WHERE xi$File = :fId AND xi$Field = :eId AND xi$Part = 0;

          /* No need to do this because SQL engine would take care of it.
          IF :eAutoIncKeyNum IS NOT NULL THEN

            SET :eConstName = 'UK_' + ( SELECT RTRIM(xe$Name) FROM x$Field WHERE xe$Id = :eId );

          END IF;*/

          IF :eAutoKeyNum IS NULL THEN

            SELECT xi$Number INTO :eKeyNum FROM x$index WHERE xi$File = :fId AND xi$Field = :eId;

            IF :eKeyNum IS NULL THEN

              SELECT MAX(xi$Number) INTO :eAutoKeyNum FROM x$index WHERE xi$File = :fId;

              IF :eAutoKeyNum IS NULL THEN
                SET :eAutoKeyNum = 1;
              ELSE
                sET :eAutoKeyNum = :eAutoKeyNum + 1;
              END IF;

            END IF;

          END IF;

        END IF;

        SET SQLSTATE = '00000';

      END IF;

      -- handling BIT
      IF ( :eType = 16 ) THEN

        IF ( :bitCounts = 0 ) THEN

          SET :bitOffset = :eOffset;
          SET :bitCounts = :bitCounts + 1;

        ELSE

          IF ( :bitCounts < 8 ) THEN

            IF ( :bitOffset != :eOffset AND :bitNoHoles = 0 ) THEN

              SET :alterTable = 1;

              SET :sqlString = :sqlString + '"' + CAST( :bitCounts AS VARCHAR(1) ) + :columnName + '"' + ' INT, ';
              SET :dropColString = :dropColString + :alterTableString + ' DROP "' + CAST( :bitCounts AS VARCHAR(1) ) + :columnName + '";';

              SET :bitCounts = 1;
              SET :bitOffset = :eOffset;

            ELSE

              IF ( :bitNoHoles = 1 ) THEN
                SET :bitOffset = :eOffset;
              END IF;

              SET :bitCounts = :bitCounts + 1;
              SET :bitNoHoles = 0;

            END IF;

          ELSE
            IF ( :bitCounts = 8 ) THEN
              SET :bitNoHoles = 1;
            ELSE
              SET :bitNoHoles = 0;
            END IF;

            SET :bitCounts = 1;
          END IF;

        END IF;

      ELSE

        SET :bitCounts = 0;

      END IF;

      IF ( :ePrevOffset = :eOffset AND :ePrevType != 16 ) THEN

        SET :iWarning2 = 2

      END IF;

      SET :ePrevType = :eType;
      SET :ePrevOffset = :eOffset;

      -- handling all datatypes
      IF (    :dataType = -6 OR :dataType = 5 OR :dataType = 4 OR :dataType = -5     -- tinyint/utinyint, smallint/usmallint, int/uint, bigint/ubigint, smallidentity/identity
           OR :dataType = 7 OR :dataType = 8 OR :dataType = -7 OR :dataType = -11    -- bfloat4, bfloat8, real, float, double, bit, uniqueidentifier
           OR :dataType = 9 OR :dataType = 91 OR :dataType = 10 OR :dataType = 92    -- date, time
           OR :dataType = 11 OR :dataType = 93                                       -- datetime, timestamp, timestamp2
           OR ( :dataType = 3 AND :typeName = 'CURRENCY')                            -- currency
           OR ( :dataType = 3 AND :eType = 1 AND :eDec != 0 ) ) THEN                 -- pseudo-integer with decimal

        IF ( :eType = 7 ) THEN                                                       -- logical
          SET :typeDef = IF ( :eSize = 1, 'LOGICAL', IF ( :eSize = 2, 'LOGICAL2', :typeName) );
        ELSE
          IF ( :dataType = 3 AND :eType = 1 ) THEN
            SET :typeDef = :typeName + ' /* Decimal value ignored for integer column ' + :tableName + '.' + :columnName + ' */';
          ELSE
            IF ( (:eFlags & 16) = 16 ) THEN                                          -- timestamp(p)/timestamp2(p)
              SET :typeDef = :typeName + '(' +  CAST( :eDec AS VARCHAR(2) ) + ')';
            ELSE
              SET :typeDef = :typeName;
            END IF;
          END IF;

        END IF;

      ELSE 

        IF ( :dataType = 2 OR :dataType = 3 ) THEN -- decimal, money, numeric, numericsa, numericsts, numericslb, numericsls, numericstb
          SET :typeDef = :typeName + '(' + CAST( :columnSize AS VARCHAR(5) )  + ',' + CAST( :decimalDigits AS VARCHAR(5) ) + ')';                 
        ELSE

          IF ( :dataType = 1 OR :dataType = 12 OR :dataType = -2 OR :dataType = -8 OR :dataType = -9 ) THEN              -- char, varchar, binary, nchar, nvarchar

            IF ( :eType = 10 ) THEN                                                  -- lstring
               SET :typeName = 'LSTRING';
               SET :columnSize = :columnSize + 1;
            END IF;

            SET :typeDef = :typeName + '(' + CAST( :columnSize AS VARCHAR(5) )  + ')';
          ELSE

            IF ( :dataType = -1 OR :dataType = -4 OR :dataType = -10 ) THEN          -- longvarchar, longvarbinary, nlongvarchar

              IF ( :eType = 12 ) THEN                                                -- note
                SET :typeDef = 'NOTE' + '(' + CAST( :eSize AS VARCHAR(5) )  + ')';
              ELSE
                IF ( :eType = 13 ) THEN                                              -- lvar
                  SET :typeDef = 'LVAR' + '(' + CAST( :eSize AS VARCHAR(5) )  + ')';
                ELSE
                  SET :typeDef = :typeName;
                END IF;
              END IF;
            END IF;              
          END IF;

        END IF;
         
      END IF;

      -- handling DEFAULT clause
      IF ( :columnDef != 'NULL' ) THEN
        SET :typeDef = :typeDef + ' DEFAULT ' + :columnDef;
      END IF;

      -- handling NOT NULL clause
      IF ( :isNullable = 0 AND :eAutoKeyNum IS NULL ) THEN
        SET :typeDef = :typeDef + ' NOT NULL';
      END IF;

      -- handling CASE/COLLATE
      IF ( :eFlags & 1 = 1 ) THEN
          SET :typeDef = :typeDef + ' CASE';
      ELSE

        IF ( :aAttrs IS NOT NULL ) THEN
          IF ( :aType = 'O' ) THEN
            SET :typeDef = :typeDef + ' COLLATE ' + '''' + :aAttrs + '.ALT' + '''';
          ELSE
            SET :typeDef = :typeDef + ' COLLATE ' + '''' + :aAttrs + '''';
          END IF;      
        END IF;

      END IF;

      -- handling MASK function
      IF ( :aAttrsM is NOT NULL ) THEN
            SET :typeDef = :typeDef + ' MASKED WITH ( FUNCTION = ' + :aAttrsM + ' )';
      END IF;

      IF ( :eAutoKeyNum IS NOT NULL ) THEN

        IF ( (:iFlags & 1) = 1 ) THEN
          SET :notUniqueUsing = ' NOT UNIQUE USING ';
        ELSE
          SET :notUniqueUsing = ' UNIQUE USING ';
        END IF;

        IF ( :eConstName IS NOT NULL ) THEN
          SET :typeDef = :typeDef + ' CONSTRAINT "' + RTRIM(:eConstName) + '"' + :notUniqueUsing + CAST(:eAutoKeyNum  AS VARCHAR(5));
        ELSE
          SET :typeDef = :typeDef + :notUniqueUsing + CAST(:eAutoKeyNum  AS VARCHAR(5));
        END IF;

        SET :eAutoKeyNum = NULL;

      END IF;

      IF :error != 0 THEN
        LEAVE ColumnCursorLoop;
      ELSE
        IF ( :iWarning2 = 1 ) THEN
          SET :sqlString = :sqlString + '"' + :columnName + '"' + ' ' + :typeDef + ' /* Column ' + :tableName + '.' + :columnName + ' has an incorrect size of ' + CAST( :eSize AS VARCHAR(3) ) + ' */';
          SET :iWarning2 = 0;
        ELSE

          IF ( :iWarning2 = 2 ) THEN
            SET :sqlString = :sqlString + '"' + :columnName + '"' + ' ' + :typeDef + ' /* Column ' + :tableName + '.' + :columnName + ' has the same offset as another column that is not type BIT */';
            SET :iWarning2 = 0;
          ELSE
            SET :sqlString = :sqlString + '"' + :columnName + '"' + ' ' + :typeDef;
          END IF;

        END IF;
      END IF;

    END LOOP;

    CLOSE columnCursor;

    IF ( :iWarning = 1 ) THEN
      INSERT INTO "x$DumpLog" (SQLString) Values ( 'Warning: CREATE TABLE on table ' + :tableName + ' may have issues.' );
    END IF;

    IF ( :alterTable = 1 ) THEN

      SET :sqlString = :sqlString + :dropColString;

    END IF;

  END IF;

  SET SQLSTATE = '00000';

END;
?

CREATE PROCEDURE psp_dumpIndexOnTable ( IN :tableName VARCHAR(128), IN :inDict INT DEFAULT 0, OUT :sqlString LONGVARCHAR) 
WITH DEFAULT HANDLER AS
BEGIN

  DECLARE :nonUnique     INT;
  DECLARE :indexName     VARCHAR(128);
  DECLARE :segNum        INT;
  DECLARE :columnName    VARCHAR(128);
  DECLARE :segCollation  VARCHAR(1);
  DECLARE :firstIdx      INT = 1;
  DECLARE :error         INT = 0;
  DECLARE :keyNum        INT = -1;

  DECLARE :fId           UINT;
  -- DECLARE :eId           UINT;
  -- DECLARE :eFlags        UINT;
  DECLARE :iFlags        UINT;
  DECLARE :iNumber       UINT
  DECLARE :inDictionary  VARCHAR(16);
  DECLARE :iWarning      INT = 0;
  DECLARE :withReplace   VARCHAR(16) = '';

  DECLARE :iField        UINT;
  DECLARE :skipAuto      INT = 0;
  DECLARE :partialIndex  INT = 0;
  DECLARE :eType         UINT;

  DECLARE :tableName2    VARCHAR(256);

  SELECT REPLACE( :tableName, '\', '\\')  INTO :tableName2;
  SELECT REPLACE( :tableName2, '%', '\%') INTO :tableName2;
  SELECT REPLACE( :tableName2, '_', '\_') INTO :tableName2;

  DECLARE idxCursor CURSOR FOR
    SELECT "NON_UNIQUE", "INDEX_NAME",  "SEQ_IN_INDEX", "COLUMN_NAME", "COLLATION" FROM dbo.fSQLStatistics ( NULL, :tableName2, 1 ) A
     WHERE A."INDEX_NAME"  IS NOT NULL
       AND A."INDEX_NAME" NOT IN ( SELECT  "PK_NAME" FROM dbo.fSQLPrimaryKeys ( NULL, :tableName2 ) )
    -- AND A."INDEX_NAME" NOT IN ( SELECT  "FK_NAME" FROM dbo.fSQLForeignKeys ( NULL, '%', :tableName2  ) )
       AND A."INDEX_NAME" NOT IN ( SELECT  "Xr$Name" FROM x$relate R, x$file F WHERE F.xf$name = :tableName AND F.xf$Id = R.Xr$FId )
    ORDER BY "INDEX_NAME", "SEQ_IN_INDEX";

  IF ( :inDict = 0 ) THEN
    SET :inDictionary = ' ';
  ELSE
    SET :inDictionary = ' IN DICTIONARY ';

    if ( :inDict = 2 ) THEN
       SET :withReplace = ' WITH REPLACE';
    END IF;
  END IF;

  SELECT xf$Id INTO :fId FROM x$File WHERE xf$Name = :tableName;

  IF SQLSTATE = '00000' THEN

    OPEN idxCursor;

    idxCursorLoop:
    LOOP

      FETCH NEXT FROM idxCursor INTO :nonUnique, :indexName, :segNum, :columnName, :segCollation;

      -- handle error conditions
      IF SQLSTATE = 'S0012' THEN
        SET :sqlString  = ' /* Phantom index found in table ' + '"' + RTRIM(:tableName) + '" */';
        SET :iWarning = 1;
        SET SQLSTATE = '00000';
        LEAVE idxCursorLoop;
      END IF;

      IF SQLSTATE = '02000' THEN
        SET :error = 100;

        IF ( :firstIdx = 0 ) THEN
          SET :sqlString = :sqlString + ' )' + :withReplace + ';'
        ELSE
          SET :sqlString = NULL;
        END IF;

        SET SQLSTATE = '00000';

        LEAVE idxCursorLoop;

      ELSE

        IF SQLSTATE = '00000' OR SQLSTATE = '01004' THEN

           -- SELECT xe$Id, xe$Flags INTO :eId, :eFlags FROM x$Field WHERE xe$File = :fId AND xe$Name = :columnName; 

           IF ( :segNum = 1 ) THEN

             SELECT xe$Offset INTO :keyNum FROM x$Field WHERE xe$File = :fId AND xe$Name = :indexName;

             IF ( :keyNum IS NULL ) THEN
               SET :keyNum = CAST(SUBSTRING ( :indexName, 4 ) AS INTEGER);
             END IF;

             IF EXISTS ( SELECT * FROM x$Index WHERE xi$File = :fId AND xi$Number = :keyNum AND xi$Flags & 512 = 512 ) THEN
               SET :partialIndex = 1;
             ELSE
               SET :partialIndex = 0;
             END IF;

             SELECT xi$Number, xi$Flags, xi$Field INTO :iNumber, :iFlags, :iField FROM x$Index WHERE xi$File = :fId AND xi$Part = 0 AND xi$Number = :keyNum;         

             SELECT xe$Datatype INTO :eType FROM x$Field WHERE xe$Id = :iField;

             IF ( :nonUnique = 0 ) THEN
               IF ( :eType != 15 AND :eType != 32 ) OR ( SELECT COUNT(*) FROM x$Index WHERE xi$File = :fId AND xi$Number = :iNumber ) > 1 THEN
                 SET :skipAuto = 0;
               ELSE
                 SET :skipAuto = 1;
               END IF;
             ELSE
               IF ( :eType != 32 ) OR ( SELECT COUNT(*) FROM x$Index WHERE xi$File = :fId AND xi$Number = :iNumber ) > 1 THEN
                 SET :skipAuto = 0;
               ELSE
                 SET :skipAuto = 1;
               END IF;                 
             END IF;

             IF ( :skipAuto = 0 ) THEN

               SET SQLSTATE = '00000';

               IF ( :firstIdx = 1 ) THEN

                 SET :sqlString = 'CREATE ';

                 IF ( :nonUnique = 0 ) THEN
                   SET :sqlString = :sqlString + 'UNIQUE ';
                 END IF;

                 IF ( :iFlags & 2 = 0 ) THEN
                   SET :sqlString = :sqlString + 'NOT MODIFIABLE ';
                 END IF;

                 IF ( :partialIndex = 1 ) THEN
                   SET :sqlString = :sqlString + 'PARTIAL ';
                 END IF;

                 IF ( :iFlags & 8 != 0 ) THEN
                   SET :sqlString = :sqlString + '/* Btrieve NULL key flag not supported in SQL index ' + :indexName + ' on table ' + :tableName + ' */ ';
                 END IF;

                 SET :sqlString = :sqlString + 'INDEX ' + '"' + :indexName + '"';

                 IF ( :keyNum IS NOT NULL ) THEN -- missing an entry if it is NULL...
                   SET :sqlString = :sqlString + ' USING ' + CAST(:keyNum AS VARCHAR(5));
                 ELSE
                   IF ( :iNumber IS NOT NULL ) THEN
                     SET :sqlString = :sqlString + ' USING ' + CAST(:iNumber AS VARCHAR(5));
                   END IF;
                 END IF;

                 SET :sqlString = :sqlString + :inDictionary + ' ON ' + '"' + RTRIM(:tableName) + '"' + ' ( ';           
                 SET :firstIdx = 0;

               ELSE

                 SET :sqlString = :sqlString + ' )' + :withReplace + ';'

                 SET :sqlString = :sqlString + ' CREATE ';

                 IF ( :nonUnique = 0 ) THEN
                   SET :sqlString = :sqlString + 'UNIQUE ';
                 END IF;

                 IF ( :iFlags & 2 = 0 ) THEN
                   SET :sqlString = :sqlString + 'NOT MODIFIABLE ';
                 END IF;

                 IF ( :partialIndex = 1 ) THEN
                   SET :sqlString = :sqlString + 'PARTIAL ';
                 END IF;

                 IF ( :iFlags & 8 != 0 ) THEN
                   SET :sqlString = :sqlString + '/* Btrieve NULL key flag not supported in SQL index ' + :indexName + ' on table ' + :tableName + ' */ ';
                   SET :iWarning = 1;
                 END IF;

                 SET :sqlString = :sqlString + 'INDEX ' + '"' + :indexName + '"';

                 IF ( :keyNum IS NOT NULL ) THEN -- missing an entry if it is NULL...
                   SET :sqlString = :sqlString + ' USING ' + CAST(:keyNum AS VARCHAR(5));
                 ELSE
                   IF ( :iNumber IS NOT NULL ) THEN
                     SET :sqlString = :sqlString + ' USING ' + CAST(:iNumber AS VARCHAR(5));
                   END IF;
                 END IF;

                 SET :sqlString = :sqlString + :inDictionary + ' ON ' + '"' + RTRIM(:tableName) + '"' + ' ( ';           

               END IF;

             END IF; -- skip Auto

           ELSE
             SET :sqlString = :sqlString + ', '
           END IF;

           SET SQLSTATE = '00000';

        ELSE
           SET :error = -1;
        END IF;

      END IF;

      IF :error != 0 THEN
        LEAVE idxCursorLoop;
      ELSE

        IF ( :skipAuto = 0 ) THEN

          SET :sqlString = :sqlString + '"' + :columnName + '"';

          IF ( :segCollation = 'D' ) THEN
            SET :sqlString = :sqlString + ' DESC';
          END IF;

        ELSE

          SET :skipAuto = 0;

        END IF;

      END IF;
    
    END LOOP;

    CLOSE idxCursor;

    IF ( :iWarning = 1 ) THEN
      INSERT INTO "x$DumpLog" (SQLString) Values ( 'Warning: CREATE INDEX on table ' + :tableName + ' may have issues.' );
    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpPKOnTable ( IN :tableName VARCHAR(128), IN :inDict INT DEFAULT 0, OUT :sqlString LONGVARCHAR ) AS
BEGIN

  DECLARE :columnName    VARCHAR(128);
  DECLARE :pkName        VARCHAR(128);
  DECLARE :firstColumn   INT = 1;
  DECLARE :error         INT = 0;
  DECLARE :fId           UINT;
  DECLARE :inDictionary  VARCHAR(16);

  DECLARE :tableName2    VARCHAR(256);

  SELECT REPLACE( :tableName, '\', '\\')  INTO :tableName2;
  SELECT REPLACE( :tableName2, '%', '\%') INTO :tableName2;
  SELECT REPLACE( :tableName2, '_', '\_') INTO :tableName2;

  DECLARE pkCursor CURSOR FOR
    SELECT "COLUMN_NAME", "PK_NAME" FROM dbo.fSQLPrimaryKeys (NULL, :tableName2 ) ORDER BY "KEY_SEQ";

  IF ( :inDict = 0 ) THEN
    SET :inDictionary = ' ';
  ELSE
    SET :inDictionary = ' IN DICTIONARY ';
  END IF;

  SELECT xf$Id INTO :fId FROM x$File WHERE xf$Name = :tableName;

  IF SQLSTATE = '00000' THEN

    SET :sqlString = 'ALTER TABLE ' + '"' + RTRIM(:tableName) + '"' + :inDictionary  + 'ADD CONSTRAINT ';

    OPEN pkCursor;

    PKCursorLoop:
    LOOP

      FETCH NEXT FROM pkCursor INTO :columnName, :pkName;

      IF SQLSTATE = '02000' THEN
        SET :error = 100;

        IF ( :firstColumn = 0 ) THEN
          SET :sqlString = :sqlString + ' )'
        ELSE
          SET :sqlString = NULL;
        END IF;

        SET SQLSTATE = '00000';

        LEAVE PKCursorLoop;

      ELSE

        IF SQLSTATE = '00000' OR SQLSTATE = '01004' THEN

          IF ( :firstColumn = 1 ) THEN
            SET :sqlString = :sqlString + '"' + :pkName + '"' + ' PRIMARY KEY ' + ' ( ';
            SET :firstColumn = 0;
          ELSE
            SET :sqlString = :sqlString + ', '
          END IF;

        ELSE
          SET :error = -1;
        END IF;

      END IF;

      IF :error != 0 THEN
        LEAVE PKCursorLoop;
      ELSE
        SET :sqlString = :sqlString + '"' + :columnName + '"';
      END IF;
    
    END LOOP;

    CLOSE pkCursor;

    IF ( :firstColumn = 0 ) THEN
      BEGIN
        DECLARE :keyNum INT = -1;

        SELECT xe$Offset INTO :keyNum FROM x$Field WHERE xe$File = :fId AND xe$Name = :pkName;
        SET SQLSTATE = '00000';

        SET :sqlString = :sqlString + ' USING ' + CAST(:keyNum AS VARCHAR(5));

      END;
    END IF;

  END IF;

  SET :sqlString = :sqlString + ';';

END;
?

CREATE PROCEDURE psp_dumpPK2OnTable ( IN :tableName VARCHAR(128), IN :inDict INT DEFAULT 0, OUT :sqlString LONGVARCHAR ) AS
BEGIN

  DECLARE :pkTabName     VARCHAR(128);
  DECLARE :pkColumnName  VARCHAR(128);
  DECLARE :keySeq        INT;
  DECLARE :fkName        VARCHAR(128);
  DECLARE :firstFK       INT = 1;
  DECLARE :error         INT = 0;
  DECLARE :keyNum        INT = -1;
  DECLARE :inDictionary  VARCHAR(16);

  DECLARE :tableName2    VARCHAR(256);

  SELECT REPLACE( :tableName, '\', '\\')  INTO :tableName2;
  SELECT REPLACE( :tableName2, '%', '\%') INTO :tableName2;
  SELECT REPLACE( :tableName2, '_', '\_') INTO :tableName2;

  DECLARE fkCursor CURSOR FOR
    SELECT "PKTABLE_NAME", "PKCOLUMN_NAME", "KEY_SEQ", "FK_NAME" FROM dbo.fSQLForeignKeys (NULL, :tableName2, '%' ) ORDER BY "PKTABLE_NAME", "KEY_SEQ";

  IF ( :inDict = 0 ) THEN
    SET :inDictionary = ' ';
  ELSE
    SET :inDictionary = ' IN DICTIONARY ';
  END IF;

  IF SQLSTATE = '00000' THEN

    OPEN fkCursor;

    FKCursorLoop:
    LOOP

      FETCH NEXT FROM fkCursor INTO :pkTabName, :pkColumnName, :keySeq, :fkName;

      IF SQLSTATE = '02000' THEN
        SET :error = 100;

        IF ( :firstFK = 0 ) THEN

          SET :sqlString = :sqlString + ' )';

          SELECT xr$Index INTO :keyNum FROM x$Relate WHERE xr$Name = :fkName;

          SET :sqlString = :sqlString + ' USING ' + CAST(:keyNum AS VARCHAR(5)) + ';';

        ELSE
          SET :sqlString = NULL;
        END IF;
           
        SET SQLSTATE = '00000';

        LEAVE FKCursorLoop;

      ELSE

        IF SQLSTATE = '00000' OR SQLSTATE = '01004' THEN

          IF ( :keySeq = 1 ) THEN

            IF ( :firstFK = 1 ) THEN
              SET :sqlString = 'ALTER TABLE ' + '"' + RTRIM(:tableName) + '"' + :inDictionary + ' ADD PRIMARY KEY ' + ' ( ';           
              SET :firstFK = 0;
            ELSE
              
              SET :sqlString = :sqlString + ' )';

              SELECT xr$Index INTO :keyNum FROM x$Relate WHERE xr$Name = :fkName;

              SET :sqlString = :sqlString + ' USING ' + CAST(:keyNum AS VARCHAR(5)) + ';';

              SET :error = 100;  -- done

            END IF;

          ELSE
            SET :sqlString = :sqlString + ', '
          END IF;

          SET SQLSTATE = '00000';

        ELSE
          SET :error = -1;
        END IF;

      END IF;

      IF :error != 0 THEN
        LEAVE FKCursorLoop;
      ELSE
        SET :sqlString = :sqlString + '"' + :pkColumnName + '"';
      END IF;
    
    END LOOP;

    CLOSE fkCursor;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpFKOnTable ( IN :tableName VARCHAR(128), IN :inDict INT DEFAULT 0, OUT :sqlString LONGVARCHAR ) AS
BEGIN

  DECLARE :pkTabName     VARCHAR(128);
  DECLARE :fkColumnName  VARCHAR(128);
  DECLARE :keySeq        INT;
  DECLARE :deleteRule    INT;
  DECLARE :fkName        VARCHAR(128);
  DECLARE :firstFK       INT = 1;
  DECLARE :error         INT = 0;
  DECLARE :pkTabName2    VARCHAR(128);
  DECLARE :deleteRule2   INT;
  DECLARE :fkName2       VARCHAR(128);
  DECLARE :keyNum        INT = -1;
  DECLARE :inDictionary  VARCHAR(16);

  DECLARE :tableName2    VARCHAR(256);

  SELECT REPLACE( :tableName, '\', '\\')  INTO :tableName2;
  SELECT REPLACE( :tableName2, '%', '\%') INTO :tableName2;
  SELECT REPLACE( :tableName2, '_', '\_') INTO :tableName2;

  DECLARE fkCursor CURSOR FOR
    SELECT "PKTABLE_NAME", "FKCOLUMN_NAME", "KEY_SEQ", "DELETE_RULE", "FK_NAME" FROM dbo.fSQLForeignKeys (NULL, '%', :tableName2 ) ORDER BY "PKTABLE_NAME", "FK_NAME", "KEY_SEQ";

  IF ( :inDict = 0 ) THEN
    SET :inDictionary = ' ';
  ELSE
    SET :inDictionary = ' IN DICTIONARY ';
  END IF;

  IF SQLSTATE = '00000' THEN

    OPEN fkCursor;

    FKCursorLoop:
    LOOP

      FETCH NEXT FROM fkCursor INTO :pkTabName, :fkColumnName, :keySeq, :deleteRule, :fkName;

      IF SQLSTATE = '02000' THEN
        SET :error = 100;

        IF ( :firstFK = 0 ) THEN

          IF ( :deleteRule2 = 0 ) THEN
            SET :sqlString = :sqlString + ' ) REFERENCES ' + '"' + :pkTabName2 + '"' + ' ON DELETE CASCADE';
          ELSE
            SET :sqlString = :sqlString + ' ) REFERENCES ' + '"' + :pkTabName2 + '"';
          END IF;

          SELECT xr$FIndex INTO :keyNum FROM x$Relate WHERE xr$Name = :fkName;

          SET :sqlString = :sqlString + ' USING ' + CAST(:keyNum AS VARCHAR(5)) + ';';

        ELSE
          SET :sqlString = NULL;
        END IF;
           
        SET SQLSTATE = '00000';

        LEAVE FKCursorLoop;

      ELSE

        IF SQLSTATE = '00000' OR SQLSTATE = '01004' THEN

          IF ( :keySeq = 1 ) THEN

            IF ( :firstFK = 1 ) THEN
              SET :sqlString = 'ALTER TABLE ' + '"' + RTRIM(:tableName) + '"' + :inDictionary + 'ADD CONSTRAINT ';           
              SET :sqlString = :sqlString + '"' + :fkName + '"' + ' FOREIGN KEY ' + ' ( ';
              SET :firstFK = 0;
            ELSE

              SELECT xr$FIndex INTO :keyNum FROM x$Relate WHERE xr$Name = :fkName2;

              IF ( :deleteRule2 = 0 ) THEN
                SET :sqlString = :sqlString + ' ) REFERENCES ' + '"' + :pkTabName2 + '"' + ' ON DELETE CASCADE';
              ELSE
                SET :sqlString = :sqlString + ' ) REFERENCES ' + '"' + :pkTabName2 + '"';
              END IF;

              SET :sqlString = :sqlString + ' USING ' + CAST(:keyNum AS VARCHAR(5)) + ';';

              SET :sqlString = :sqlString + ' ALTER TABLE ' + '"' + RTRIM(:tableName) + '"' + :inDictionary + 'ADD CONSTRAINT ';
              SET :sqlString = :sqlString + '"' + :fkName + '"' + ' FOREIGN KEY ' + ' ( ';
            END IF;

          ELSE
            SET :sqlString = :sqlString + ', '
          END IF;

          SET SQLSTATE = '00000';

        ELSE
          SET :error = -1;
        END IF;

      END IF;

      IF :error != 0 THEN
        LEAVE FKCursorLoop;
      ELSE
        SET :sqlString = :sqlString + '"' + :fkColumnName + '"';
        SET :pkTabName2 = :pkTabName;
        SET :deleteRule2 = :deleteRule;
        SET :fkName2 = :fkName;
      END IF;
    
    END LOOP;

    CLOSE fkCursor;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpView ( IN :viewName VARCHAR(128), OUT :sqlString LONGVARCHAR ) AS
BEGIN

  DECLARE :viewText      LONGVARCHAR;
  DECLARE :columnName    VARCHAR(128);
  DECLARE :firstColumn   INT = 1;
  DECLARE :error         INT = 0; 
  DECLARE :v2MetaData    INT = 0;
  DECLARE :trustedView   INT = 0;
  DECLARE :viewName2     VARCHAR(256);

  SELECT CASE LOWER(RTRIM(xf$Loc)) WHEN 'pvfile.ddf' THEN 1 ELSE 0 END INTO :v2MetaData FROM x$File WHERE xf$Id = 1;

  SELECT REPLACE( :viewName, '\', '\\')  INTO :viewName2;
  SELECT REPLACE( :viewName2, '%', '\%') INTO :viewName2;
  SELECT REPLACE( :viewName2, '_', '\_') INTO :viewName2;

  DECLARE columnCursor CURSOR FOR
    SELECT "COLUMN_NAME" FROM dbo.fSQLColumns (NULL, :viewName2, NULL) ORDER BY "ORDINAL_POSITION";
 
  SET :viewText = 'CREATE VIEW ' + '"' + :viewName + '"';
  SET SQLSTATE = '00000';

  OPEN columnCursor;

  ColumnCursorLoop:
  LOOP

    FETCH NEXT FROM columnCursor INTO :columnName;

    IF SQLSTATE = '02000' THEN
      SET :error = 100;

      IF ( :v2MetaData = 0 ) THEN
        SET :viewText = :viewText + ' ) AS '
      ELSE

        SELECT TOP 1 xv$Trustee INTO :trustedView FROM x$View WHERE xv$Name = :viewName;

        IF ( :trustedView = 0 ) THEN
          SET :viewText = :viewText + ' ) WITH EXECUTE AS ''Master'' AS ';
        ELSE
          SET :viewText = :viewText + ' ) AS '
        END IF; 
 
      END IF;  

      LEAVE ColumnCursorLoop;
    ELSE
      IF SQLSTATE = '00000' OR SQLSTATE = '01004' THEN

         IF ( :firstColumn = 1 ) THEN
           SET :viewText = :viewText + ' ( '
           SET :firstColumn = 0;
         ELSE
           SET :viewText = :viewText + ', '
         END IF

      ELSE
         SET :error = -1;
      END IF
    END IF;

    IF :error != 0 THEN
      LEAVE ColumnCursorLoop;
    ELSE
      SET :viewText = :viewText + '"' + :columnName + '"';
    END IF;
    
  END LOOP;

  CLOSE columnCursor;

  SELECT :viewText + xv$Misc INTO :sqlString FROM x$View WHERE xv$Name = :viewName;

  SET :sqlString = :sqlString + ';';

END;
?

CREATE PROCEDURE psp_dumpTables (IN :inDict INT DEFAULT 0, IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR );
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :tabName   VARCHAR(128);
  DECLARE :location  VARCHAR(252);
  DECLARE :dumpPK2   INTEGER;
  DECLARE :inDict2   INTEGER;
  DECLARE :inDict3   INTEGER;
  DECLARE :nullLoc   INTEGER;
  DECLARE :fileFlags INTEGER;

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF SQLSTATE = '00000' THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTables' ) THEN
      DELETE FROM "x$DumpTables";
    ELSE
      CREATE TABLE "x$DumpTables" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTables' ) THEN  

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start dumping tables: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      IF SQLSTATE = '00000' THEN

        INSERT INTO "x$DumpTables" ( SQLString ) VALUES ( 'SET LEGACYTYPESALLOWED = ON;' );

        DECLARE tableCursor CURSOR FOR
          SELECT "TABLE_NAME" FROM dbo.fSQLTables (NULL, NULL, NULL) WHERE "TABLE_TYPE" = 'TABLE';

          OPEN tableCursor;

          TableCursorLoop:
          LOOP

            FETCH NEXT FROM tableCursor INTO :tabName;

            IF SQLSTATE = '02000' THEN
              LEAVE TableCursorLoop;
            END IF;

            SELECT xf$Flags INTO :fileFlags FROM x$File WHERE xf$Name = :tabName;

            IF ( :fileFlags & 32 = 32 OR :fileFlags & 2 = 2 OR :fileFlags & 4 = 4 ) THEN

                    INSERT INTO "x$DumpTables" (SQLString) VALUES ( '/* COBOL table "' + RTRIM(:tabName) + '" export not supported. */' );

            ELSE

              SET :inDict2 = :inDict;
              SET :inDict3 = 0;

              IF ( :inDict2 = 0 ) THEN

                SELECT xf$Loc, LOCATE(char(0), xf$Loc, 1) INTO :location, :nullLoc FROM x$File WHERE xf$Name = :tabName;

                SET :location = LTRIM( :location );
                SET :location = RTRIM( :location );

                IF (LEFT(:location, 2) = '\\') OR (LEFT(:location, 2) = '//') OR (LOCATE(':\', :location, 1) = 2) THEN

                  SET :inDict2 = 2;

                ELSE

                  IF :nullLoc > 0 THEN

                    IF ( SELECT COUNT(*) FROM x$File WHERE LEFT(UPPER( xf$Loc ), :nullLoc - 1) = UPPER( :location ) ) > 1 THEN

                      SET :inDict3 = 1;

                    END IF;

                  ELSE

                    IF ( SELECT COUNT(*) FROM x$File WHERE UPPER( xf$Loc ) = UPPER( :location ) ) > 1 THEN

                      SET :inDict3 = 1;

                    END IF;

                  END IF;

                END IF;

              END IF;

              CALL psp_dumpTable ( :tabName, :inDict2, :sqlString );

              IF ( :sqlString IS NOT NULL ) THEN

                INSERT INTO "x$DumpTables" (SQLString) VALUES ( :sqlString );

                CALL psp_dumpIndexOnTable ( :tabName, :inDict2, :sqlString );

                IF ( :sqlString IS NOT NULL ) THEN

                  INSERT INTO "x$DumpTables" (SQLString) VALUES ( :sqlString );

                  IF ( :inDict2 = 0 AND :inDict3 = 1 ) THEN

                    CALL psp_dumpIndexOnTable ( :tabName, 1, :sqlString );

                    IF ( :sqlString IS NOT NULL ) THEN

                      INSERT INTO "x$DumpTables" (SQLString) VALUES ( :sqlString );

                    END IF;

                  END IF;

                END IF;

              ELSE
                INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Warning: No CREATE TABLE statement for table ' + :tabName + '.' );
              END IF;

            END IF;

          END LOOP;

          CLOSE tableCursor;

        -- tables with PK
          OPEN tableCursor;
          SET SQLSTATE = '00000';

          TableCursorLoop2:
          LOOP

            FETCH NEXT FROM tableCursor INTO :tabName;

            IF SQLSTATE = '02000' THEN
              LEAVE TableCursorLoop2;
            END IF;

            IF ( :fileFlags & 32 != 32 OR :fileFlags & 2 != 2 OR :fileFlags & 4 != 4 ) THEN

              SET :inDict2 = :inDict;
              SET :inDict3 = 0;

              IF ( :inDict2 = 0 ) THEN

                SELECT xf$Loc, LOCATE(char(0), xf$Loc, 1) INTO :location, :nullLoc FROM x$File WHERE xf$Name = :tabName;

                SET :location = LTRIM( :location );
                SET :location = RTRIM( :location );

                IF (LEFT(:location, 2) = '\\') OR (LEFT(:location, 2) = '//') OR (LOCATE(':\', :location, 1) = 2) THEN

                  SET :inDict2 = 2;

                ELSE

                  IF :nullLoc > 0 THEN

                    IF ( SELECT COUNT(*) FROM x$File WHERE LEFT(UPPER( xf$Loc ), :nullLoc - 1) = UPPER( :location ) ) > 1 THEN

                      SET :inDict3 = 1;

                    END IF;

                  ELSE

                    IF ( SELECT COUNT(*) FROM x$File WHERE UPPER( xf$Loc ) = UPPER( :location ) ) > 1 THEN

                      SET :inDict3 = 1;

                    END IF;

                  END IF;

                END IF;

              END IF;

              CALL psp_dumpPKOnTable ( :tabName, :inDict2, :sqlString );

              SET :dumpPK2 = 0;

              IF ( :sqlString IS NULL ) THEN -- In case when xi$Flags is NOT set to be a primary key

                CALL psp_dumpPK2OnTable ( :tabName, :inDict2, :sqlString );
                SET :dumpPK2 = 1;

              END IF;

              IF ( :sqlString IS NOT NULL ) THEN

                INSERT INTO "x$DumpTables" (SQLString) VALUES ( :sqlString );

                IF ( :inDict2 = 0 AND :inDict3 = 1 ) THEN

                  IF ( :dumpPK2 = 0 ) THEN
                    CALL psp_dumpPKOnTable ( :tabName, 1, :sqlString );
                  ELSE
                    CALL psp_dumpPK2OnTable ( :tabName, 1, :sqlString );
                  END IF;

                  IF ( :sqlString IS NOT NULL ) THEN

                    INSERT INTO "x$DumpTables" (SQLString) VALUES ( :sqlString );

                  END IF;

                END IF;

              END IF;

            END IF;

          END LOOP;

          CLOSE tableCursor;

        -- tables with FK
          OPEN tableCursor;
          SET SQLSTATE = '00000';

          TableCursorLoop3:
          LOOP

            FETCH NEXT FROM tableCursor INTO :tabName;

            IF SQLSTATE = '02000' THEN
              LEAVE TableCursorLoop3;
            END IF;

            IF ( :fileFlags & 32 != 32 OR :fileFlags & 2 != 2 OR :fileFlags & 4 != 4 ) THEN

              IF 0 <> ( SELECT COUNT(*) FROM "X$Relate" WHERE xr$fid = ( SELECT xf$id FROM x$file WHERE xf$name = :tabName ) ) THEN

                SET :inDict2 = :inDict;
                SET :inDict3 = 0;

                IF ( :inDict2 = 0 ) THEN

                  SELECT xf$Loc, LOCATE(char(0), xf$Loc, 1) INTO :location, :nullLoc FROM x$File WHERE xf$Name = :tabName;

                  SET :location = LTRIM( :location );
                  SET :location = RTRIM( :location );

                  IF (LEFT(:location, 2) = '\\') OR (LEFT(:location, 2) = '//') OR (LOCATE(':\', :location, 1) = 2) THEN

                    SET :inDict2 = 2;

                  ELSE

                    IF :nullLoc > 0 THEN

                      IF ( SELECT COUNT(*) FROM x$File WHERE LEFT(UPPER( xf$Loc ), :nullLoc - 1) = UPPER( :location ) ) > 1 THEN

                        SET :inDict3 = 1;

                      END IF;

                    ELSE

                      IF ( SELECT COUNT(*) FROM x$File WHERE UPPER( xf$Loc ) = UPPER( :location ) ) > 1 THEN

                        SET :inDict3 = 1;

                      END IF;

                    END IF;

                  END IF;

                END IF;

                CALL psp_dumpFKOnTable ( :tabName, :inDict2, :sqlString );

                IF ( :sqlString IS NOT NULL ) THEN

                  INSERT INTO "x$DumpTables" (SQLString) VALUES ( :sqlString );

                  IF ( :inDict2 = 0 AND :inDict3 = 1 ) THEN

                    CALL psp_dumpFKOnTable ( :tabName, 1, :sqlString );

                    IF ( :sqlString IS NOT NULL ) THEN

                      INSERT INTO "x$DumpTables" (SQLString) VALUES ( :sqlString );

                    END IF;

                  END IF;

                END IF;

              END IF;

            END IF;

          END LOOP;

          CLOSE tableCursor;

          INSERT INTO "x$DumpTables" ( SQLString ) VALUES ( 'SET LEGACYTYPESALLOWED = OFF;' );

          INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping tables: ' + CAST(NOW() AS VARCHAR(30))  );
          COMMIT WORK;

          -- SELECT SQLString FROM "x$DumpTables" ORDER BY ID;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Fails to create table "x$DumpTables".';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_dumpViews (IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR )
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :tabName   VARCHAR(128);

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$View' ) ) THEN

    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$View is missing.' );

  ELSE

    IF SQLSTATE = '00000' THEN

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpViews' ) THEN
        DELETE FROM "x$DumpViews";
      ELSE
        CREATE TABLE "x$DumpViews" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpViews' ) THEN 

        START TRANSACTION;

        IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
          INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
          UPDATE "x$DumpLog" SET SQLString = 'Start dumping views: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
        ELSE
          UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
        END IF;

        IF SQLSTATE = '00000' THEN

          DECLARE viewCursor CURSOR FOR
            SELECT "TABLE_NAME" FROM dbo.fSQLTables (NULL, NULL, NULL) WHERE "TABLE_TYPE" = 'VIEW';

          OPEN viewCursor;
          SET SQLSTATE = '00000';

          ViewCursorLoop:
          LOOP

            FETCH NEXT FROM viewCursor INTO :tabName;

            IF SQLSTATE = '02000' THEN
              LEAVE ViewCursorLoop;
            END IF;

            CALL psp_dumpView ( :tabName, :sqlString );
            INSERT INTO "x$DumpViews" (SQLString) VALUES ( :sqlString  );

          END LOOP;

          CLOSE viewCursor;

          INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping views: ' + CAST(NOW() AS VARCHAR(30))  );
          COMMIT WORK;

          -- SELECT SQLString FROM "x$DumpViews" ORDER BY ID;

        ELSE

          ROLLBACK WORK;
          SIGNAL 'S1000', 'Dump process is already running.';

        END IF;

      ELSE

        SIGNAL 'S1000', 'Failed to create table "x$DumpViews".';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpProcedures (IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR );
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :procName  VARCHAR(128);

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$Proc' ) ) THEN

    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$Proc is missing.' );

  ELSE

    IF SQLSTATE = '00000' THEN

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpProcedures' ) THEN
        DELETE FROM "x$DumpProcedures";
      ELSE
        CREATE TABLE "x$DumpProcedures" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpProcedures' ) THEN 

        START TRANSACTION;

        IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
          INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
          UPDATE "x$DumpLog" SET SQLString = 'Start dumping procedures: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
        ELSE
          UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
        END IF;

        IF SQLSTATE = '00000' THEN

          DECLARE procedureCursor CURSOR FOR
            SELECT "PROCEDURE_NAME" FROM dbo.fSQLProcedures (NULL, NULL);

          OPEN procedureCursor;
          SET SQLSTATE = '00000';

          ProcedureCursorLoop:
          LOOP

            FETCH NEXT FROM procedureCursor INTO :procName;

            IF SQLSTATE = '02000' THEN
              LEAVE ProcedureCursorLoop;
            END IF;

            INSERT INTO "x$DumpProcedures" (SQLString) SELECT xp$Misc FROM x$Proc WHERE xp$Name = :procName;

          END LOOP;

          CLOSE procedureCursor;

          INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping procedures: ' + CAST(NOW() AS VARCHAR(30))  );
          COMMIT WORK;

          -- SELECT SQLString FROM "x$DumpProcedures" ORDER BY ID;

        ELSE

          ROLLBACK WORK;
          SIGNAL 'S1000', 'Dump process is already running.';

        END IF;

      ELSE

        SIGNAL 'S1000', 'Fails to create table "x$DumpProcedures".';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpTriggers (IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR );
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :trigName  VARCHAR(128);

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$Trigger' ) ) THEN

    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$Trigger is missing.' );

  ELSE

    IF SQLSTATE = '00000' THEN

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTriggers' ) THEN
        DELETE FROM "x$DumpTriggers";
      ELSE
        CREATE TABLE "x$DumpTriggers" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTriggers' ) THEN 

        START TRANSACTION;

        IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
          INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
          UPDATE "x$DumpLog" SET SQLString = 'Start dumping triggers: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
        ELSE
          UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
        END IF;

        IF SQLSTATE = '00000' THEN

          DECLARE triggerCursor CURSOR FOR
            SELECT DISTINCT xt$Name FROM x$Trigger;

          OPEN triggerCursor;
          SET SQLSTATE = '00000';

          TriggerCursorLoop:
          LOOP

            FETCH NEXT FROM triggerCursor INTO :trigName;

            IF SQLSTATE = '02000' THEN
              LEAVE TriggerCursorLoop;
            END IF;

            INSERT INTO "x$DumpTriggers" (SQLString) SELECT xt$Misc FROM x$Trigger WHERE xt$Name = :trigName;

          END LOOP;

          CLOSE triggerCursor;

          INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping triggers: ' + CAST(NOW() AS VARCHAR(30))  );
          COMMIT WORK;

          -- SELECT SQLString FROM "x$DumpTriggers" ORDER BY ID;

        ELSE

          ROLLBACK WORK;
          SIGNAL 'S1000', 'Dump process is already running.';

        END IF;

      ELSE

        SIGNAL 'S1000', 'Fails to create table "x$DumpTriggers".';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpUsers (IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR );
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :uId       INT;
  DECLARE :gName     VARCHAR(128);
  DECLARE :uName     VARCHAR(128);
  DECLARE :uFlags    INT;

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$User' ) ) THEN

    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$User is missing.' );

  ELSE

    IF SQLSTATE = '00000' THEN

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUsers' ) THEN
        DELETE FROM "x$DumpUsers";
      ELSE
        CREATE TABLE "x$DumpUsers" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUsers' ) THEN 

        START TRANSACTION;

        IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
          INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
          UPDATE "x$DumpLog" SET SQLString = 'Start dumping users: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
        ELSE
          UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
        END IF;

        IF SQLSTATE = '00000' THEN

          -- groups
          DECLARE groupCursor CURSOR FOR
            SELECT xu$Name FROM x$User WHERE xu$Name NOT IN ( 'PUBLIC' ) AND xu$Flags & 64 = 64;

          OPEN groupCursor;

          IF SQLSTATE = '00000' THEN

            GroupCursorLoop:
            LOOP

              FETCH NEXT FROM groupCursor INTO :gName;

              IF SQLSTATE = '02000' THEN
                LEAVE GroupCursorLoop;
              END IF;

              SET :sqlString = 'CREATE GROUP ' + '"' + RTRIM(:gName) + '"' + ';';
              INSERT INTO "x$DumpUsers" (SQLString) VALUES (:sqlString);

            END LOOP;

            CLOSE groupCursor;

            SET SQLSTATE = '00000';

            -- users
            DECLARE userCursor CURSOR FOR
              SELECT xu$Id, xu$Name FROM x$User WHERE xu$Name NOT IN ( 'Master', 'PUBLIC' ) AND xu$Flags & 64 = 0;

            OPEN userCursor;

            IF SQLSTATE = '00000' THEN

              UserCursorLoop:
              LOOP

                FETCH NEXT FROM userCursor INTO :uId, :uName;

                IF SQLSTATE = '02000' THEN
                  LEAVE UserCursorLoop;
                END IF;

                SELECT A.xu$Name INTO :gName FROM x$User A, x$User B WHERE A.xu$Id = :uId AND A.xu$Flags & 64 = 64 AND B.xu$Flags & 64 = 64;
                SET SQLSTATE = '00000';

                SET :sqlString = 'CREATE USER ' + '"' + RTRIM(:uName) + '"';

                IF ( :gName IS NOT NULL ) THEN
                  SET :sqlString = :sqlString +  ' IN GROUP ' + '"' + RTRIM(:gName) + '"';
                END IF;

                SET :sqlString = :sqlString + ';';

                INSERT INTO "x$DumpUsers" (SQLString) VALUES (:sqlString);

              END LOOP;

              CLOSE userCursor;

              INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping users: ' + CAST(NOW() AS VARCHAR(30))  );
              COMMIT WORK;

              -- SELECT SQLString FROM "x$DumpUsers" ORDER BY ID;

            ELSE

              ROLLBACK WORK;
              SIGNAL 'S1000', 'Unable to open table: x$User.';

            END IF;

          ELSE

            ROLLBACK WORK;
            SIGNAL 'S1000', 'Unable to open table: x$User.';

          END IF;

        ELSE

          ROLLBACK WORK;
          SIGNAL 'S1000', 'Dump process is already running.';

        END IF;

      ELSE

        SIGNAL 'S1000', 'Fails to create table "x$DumpUsers".';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpUserRights (IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR );
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :uId       INT;
  DECLARE :gName     VARCHAR(128);
  DECLARE :uName     VARCHAR(128);
  DECLARE :uFlags    INT;

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$Rights' ) ) THEN

    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$Rights is missing.' );

  ELSE

    IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$User' ) ) THEN

      INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$User is missing.' );

    ELSE

      IF SQLSTATE = '00000' THEN

        IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUserRights' ) THEN
          DELETE FROM "x$DumpUserRights";
        ELSE
          CREATE TABLE "x$DumpUserRights" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
        END IF;

        IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUserRights' ) THEN 

          START TRANSACTION;

          IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
            INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
            UPDATE "x$DumpLog" SET SQLString = 'Start dumping user rights: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
          ELSE
            UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
          END IF;

          IF SQLSTATE = '00000' THEN

            -- user rights
            DECLARE userRightsCursor CURSOR FOR
              SELECT xu$Id, xu$Name, xu$Flags FROM x$User ORDER BY xu$Id;

            OPEN userRightsCursor;

            IF SQLSTATE = '00000' THEN

              UserRightsCursorLoop:
              LOOP

                FETCH NEXT FROM userRightsCursor INTO :uId, :uName, :uFlags;

                IF SQLSTATE = '02000' THEN
                  LEAVE UserRightsCursorLoop;
                END IF;

                IF ( :uFlags & 128 = 128 ) THEN
                  SET :sqlString = 'GRANT CREATETAB TO ' + '"' + RTRIM(:uName) + '";';
                END IF;

                IF ( :uFlags & 256 = 256 ) THEN
                  IF ( :sqlString IS NULL ) THEN
                    SET :sqlString = 'GRANT CREATEVIEW TO ' + '"' + RTRIM(:uName) + '";';
                  ELSE
                    SET :sqlString = :sqlString + 'GRANT CREATEVIEW TO ' + '"' + RTRIM(:uName) + '";';
                  END IF;
                END IF;

                IF ( :uFlags & 512 = 512 ) THEN
                  IF ( :sqlString IS NULL ) THEN
                    SET :sqlString = 'GRANT CREATESP TO ' + '"' + RTRIM(:uName) + '";';
                  ELSE
                    SET :sqlString = :sqlString + 'GRANT CREATESP TO ' + '"' + RTRIM(:uName) + '";';
                  END IF;
                END IF;

                IF ( :sqlString IS NOT NULL ) THEN
                  INSERT INTO "x$DumpUserRights" (SQLString) VALUES (:sqlString);
                END IF;

                SET :sqlString = NULL;

              END LOOP;

              CLOSE userRightsCursor;

              INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping user rights: ' + CAST(NOW() AS VARCHAR(30))  );
              COMMIT WORK;

              -- SELECT SQLString FROM "x$DumpUserRights" ORDER BY ID;

            ELSE

              ROLLBACK WORK;
              SIGNAL 'S1000', 'Unable to open table: x$User.';

            END IF;

          ELSE

            ROLLBACK WORK;
            SIGNAL 'S1000', 'Dump process is already running.';

          END IF;

        ELSE

          SIGNAL 'S1000', 'Fails to create table "x$DumpUserRights".';

        END IF;

      ELSE

        SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

      END IF;

    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpTabColRights (IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR );
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :uId       INT;
  DECLARE :uName     VARCHAR(128);
  DECLARE :uFlags    INT;

  DECLARE :rUser     INT;
  DECLARE :rFlags    INT;
  DECLARE :fName     VARCHAR(128);
  DECLARE :eName     VARCHAR(128);

  DECLARE :dVer      DECIMAL(8, 2);
  DECLARE :uId2      INT;
  DECLARE :uVerLen   INT;

  SET :uId  = ( SELECT POSITION ( '-', @@version ) + 2 ); -- position to version number
  SET :uId2 = ( SELECT POSITION ( '.', @@version ) + 2 ); -- position at the end of minor version
  SET :uVerLen = :uId2 - :uId + 1;

  SET :dVer = ( SELECT CAST ( SUBSTRING ( @@version, :uId, :uVerLen ) AS DECIMAL(8, 2) ) );

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' OR SQLSTATE = '02000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$User' ) ) OR NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$Rights' ) ) THEN

    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Either x$User or x$Rights is missing.' );

  ELSE

    IF SQLSTATE = '00000' THEN

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTabColRights' ) THEN
        DELETE FROM "x$DumpTabColRights";
      ELSE
        CREATE TABLE "x$DumpTabColRights" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTabColRights' ) THEN 

        START TRANSACTION;

        IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
          INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
          UPDATE "x$DumpLog" SET SQLString = 'Start dumping user table and column rights: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
        ELSE
          UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
        END IF;

        IF SQLSTATE = '00000' THEN

          -- db rights
          DECLARE dbRightsCursor CURSOR FOR
            SELECT xr$User, xu$Name, xr$Rights, xu$flags FROM x$Rights, x$User WHERE xr$Table = 0 AND xr$Column = 0 AND xr$User = xu$Id ORDER BY xr$User;

          OPEN dbRightsCursor;

          IF SQLSTATE = '00000' THEN

            DBRightsCursorLoop:
            LOOP

              FETCH NEXT FROM dbRightsCursor INTO :rUser, :uName, :rFlags, :uFlags;

              IF SQLSTATE = '02000' THEN
                LEAVE DBRightsCursorLoop;
              END IF;

              IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                IF ( (:rFlags = 254 AND :dVer < 16.10) OR :rFlags = 255 ) THEN
                  SET :sqlString = 'GRANT ALL ON * TO "' + RTRIM(:uName) + '";';
                ELSE
                  IF ( :rFlags & 64 = 64 ) THEN
                    SET :sqlString = 'GRANT SELECT ON * TO "' + RTRIM(:uName) + '";';
                  END IF;

                  IF ( :rFlags & 1 = 1 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT UNMASK ON * TO "' + RTRIM(:uName) + '";'
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT UNMASK ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;                  

                  IF ( :rFlags & 130 = 130 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT UPDATE ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT UPDATE ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 132 = 132 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT INSERT ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT INSERT ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 136 = 136 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT DELETE ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT DELETE ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 144 = 144 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT REFERENCES ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT REFERENCES ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 160 = 160 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT ALTER ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT ALTER ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                END IF;

                IF ( :sqlString IS NOT NULL ) THEN
                  INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                END IF;

              END IF;

              SET :sqlString = NULL;

            END LOOP;

            CLOSE dbRightsCursor;  

            -- table rights
            DECLARE tableRightsCursor CURSOR FOR
              SELECT xr$User, xf$Name, xu$Name, xr$Rights, xu$Flags FROM x$Rights, x$File, x$User WHERE xr$Table = xf$Id AND xr$Column = 0 AND xr$User = xu$Id ORDER BY xr$User;

            OPEN tableRightsCursor;

            IF SQLSTATE = '00000' THEN

              TableRightsCursorLoop:
              LOOP

                FETCH NEXT FROM tableRightsCursor INTO :rUser, :fName, :uName, :rFlags, :uFlags;

                IF SQLSTATE = '02000' THEN
                  LEAVE TableRightsCursorLoop;
                END IF;

                IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                  IF ( (:rFlags = 254 AND :dVer < 16.10) OR :rFlags = 255 ) THEN
                    SET :sqlString = 'GRANT ALL ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                  ELSE
                    IF ( :rFlags & 64 = 64 ) THEN
                        SET :sqlString = 'GRANT SELECT ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                    END IF;

                    IF ( :rFlags & 1 = 1 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UNMASK ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UNMASK ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 130 = 130 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UPDATE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UPDATE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 132 = 132 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT INSERT ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT INSERT ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 136 = 136 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT DELETE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT DELETE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 144 = 144 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT REFERENCES ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT REFERENCES ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 160 = 160 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT ALTER ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT ALTER ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                  END IF;

                  IF ( :sqlString IS NOT NULL ) THEN
                    INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                  END IF;

                END IF;

                SET :sqlString = NULL;

              END LOOP;

              CLOSE tableRightsCursor;

              -- column rights
              DECLARE columnRightsCursor CURSOR FOR
                SELECT xr$User, xf$Name, xe$Name, xu$Name, xr$Rights, xu$Flags FROM x$Rights, x$File, x$User, x$Field WHERE xr$Table = xf$Id AND xr$Column > 0 AND xr$User = xu$Id AND xf$Id = xe$File AND xe$Id = xr$Column ORDER BY xr$User, xf$Name, xr$Column;

              OPEN columnRightsCursor;

              IF SQLSTATE = '00000' THEN

                ColumnRightsCursorLoop:
                LOOP

                  FETCH NEXT FROM columnRightsCursor INTO :rUser, :fName, :eName, :uName, :rFlags, :uFlags;

                  IF SQLSTATE = '02000' THEN
                    LEAVE ColumnRightsCursorLoop;
                  END IF;

                  IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                    IF ( :rFlags & 64 = 64 ) THEN
                      SET :sqlString = 'GRANT SELECT ( "' + RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                    END IF;

                    IF ( :rFlags & 1 = 1 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UNMASK ( "' + RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UNMASK ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 130 = 130 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UPDATE ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UPDATE ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 132 = 132 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT INSERT ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT INSERT ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :sqlString IS NOT NULL ) THEN
                      INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                    END IF;

                  END IF;

                  SET :sqlString = NULL;

                END LOOP;

                CLOSE columnRightsCursor;

                INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping  user table and column rights: ' + CAST(NOW() AS VARCHAR(30))  );
                COMMIT WORK;

                -- SELECT SQLString FROM "x$DumpTabColRights" ORDER BY ID;

              ELSE

                ROLLBACK WORK;
                SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

              END IF;

            ELSE

              ROLLBACK WORK;
              SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

            END IF;

          ELSE

            ROLLBACK WORK;
            SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

          END IF;

        ELSE

          ROLLBACK WORK;
          SIGNAL 'S1000', 'Dump process is already running.';

        END IF;

      ELSE

        SIGNAL 'S1000', 'Fails to create table "x$DumpTabColRights".';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_dumpTabColRights2 (IN :clearLogs INT DEFAULT 1) WITH DEFAULT HANDLER
-- RETURNS ( SQL LONGVARCHAR );
AS
BEGIN
  DECLARE :sqlString LONGVARCHAR;
  DECLARE :uId       INT;
  DECLARE :uName     VARCHAR(128);
  DECLARE :uFlags    INT;

  DECLARE :rUser     INT;
  DECLARE :rFlags    INT;
  DECLARE :fName     VARCHAR(128);
  DECLARE :eName     VARCHAR(128);

  DECLARE :dVer      DECIMAL(8, 2);
  DECLARE :uId2      INT;
  DECLARE :uVerLen   INT;

  SET :uId  = ( SELECT POSITION ( '-', @@version ) + 2 ); -- position to version number
  SET :uId2 = ( SELECT POSITION ( '.', @@version ) + 2 ); -- position at the end of minor version
  SET :uVerLen = :uId2 - :uId + 1;

  SET :dVer = ( SELECT CAST ( SUBSTRING ( @@version, :uId, :uVerLen ) AS DECIMAL(8, 2) ) );

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' OR SQLSTATE = '02000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$User' ) ) OR NOT EXISTS ( SELECT xe$Name FROM x$Field where xe$File = ( SELECT xf$Id from x$File where xf$Name = 'X$Rights' ) ) THEN

    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Either x$User or x$Rights is missing.' );

  ELSE

    IF SQLSTATE = '00000' THEN

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTabColRights' ) THEN
        DELETE FROM "x$DumpTabColRights";
      ELSE
        CREATE TABLE "x$DumpTabColRights" ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTabColRights' ) THEN 

        START TRANSACTION;

        IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
          INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
          UPDATE "x$DumpLog" SET SQLString = 'Start dumping user table and column rights: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
        ELSE
          UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
        END IF;

        IF SQLSTATE = '00000' THEN

          -- db rights
          DECLARE dbRightsCursor CURSOR FOR
            SELECT xr$User, xu$Name, xr$Rights, xu$flags FROM x$Rights, x$User WHERE xr$Object = 0 AND xr$Column = 0 AND xr$User = xu$Id ORDER BY xr$User;

          OPEN dbRightsCursor;

          IF SQLSTATE = '00000' THEN

            DBRightsCursorLoop:
            LOOP

              FETCH NEXT FROM dbRightsCursor INTO :rUser, :uName, :rFlags, :uFlags;

              IF SQLSTATE = '02000' THEN
                LEAVE DBRightsCursorLoop;
              END IF;

              IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                IF ( (:rFlags = 254 AND :dVer < 16.10) OR :rFlags = 255 ) THEN
                  SET :sqlString = 'GRANT ALL ON * TO "' + RTRIM(:uName) + '";';
                ELSE
                  IF ( :rFlags & 64 = 64 ) THEN
                    SET :sqlString = 'GRANT SELECT ON * TO "' + RTRIM(:uName) + '";';
                  END IF;

                  IF ( :rFlags & 1 = 1 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT UNMASK ON * TO "' + RTRIM(:uName) + '";'
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT UNMASK ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;                  

                  IF ( :rFlags & 130 = 130 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT UPDATE ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT UPDATE ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 132 = 132 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT INSERT ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT INSERT ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 136 = 136 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT DELETE ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT DELETE ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 144 = 144 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT REFERENCES ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT REFERENCES ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                  IF ( :rFlags & 160 = 160 ) THEN
                    IF ( :sqlString IS NULL ) THEN
                      SET :sqlString = 'GRANT ALTER ON * TO "' + RTRIM(:uName) + '";';
                    ELSE
                      SET :sqlString = :sqlString + 'GRANT ALTER ON * TO "' + RTRIM(:uName) + '";'; 
                    END IF;
                  END IF;

                END IF;

                IF ( :sqlString IS NOT NULL ) THEN
                  INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                END IF;

              END IF;

              SET :sqlString = NULL;

            END LOOP;

            CLOSE dbRightsCursor;  

            -- table rights
            DECLARE tableRightsCursor CURSOR FOR
              SELECT xr$User, xf$Name, xu$Name, xr$Rights, xu$Flags FROM x$Rights, x$File, x$User WHERE xr$Object = xf$Id AND xr$Column = 0 AND xr$User = xu$Id AND xr$Type = 1 ORDER BY xr$User;

            OPEN tableRightsCursor;

            IF SQLSTATE = '00000' THEN

              TableRightsCursorLoop:
              LOOP

                FETCH NEXT FROM tableRightsCursor INTO :rUser, :fName, :uName, :rFlags, :uFlags;

                IF SQLSTATE = '02000' THEN
                  LEAVE TableRightsCursorLoop;
                END IF;

                IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                  IF ( (:rFlags = 254 AND :dVer < 16.10) OR :rFlags = 255 ) THEN
                    SET :sqlString = 'GRANT ALL ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                  ELSE
                    IF ( :rFlags & 64 = 64 ) THEN
                      SET :sqlString = 'GRANT SELECT ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                    END IF;

                    IF ( :rFlags & 1 = 1 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UNMASK ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UNMASK ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 130 = 130 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UPDATE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UPDATE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 132 = 132 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT INSERT ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT INSERT ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 136 = 136 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT DELETE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT DELETE ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 144 = 144 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT REFERENCES ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT REFERENCES ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 160 = 160 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT ALTER ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT ALTER ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                  END IF;

                  IF ( :sqlString IS NOT NULL ) THEN
                    INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                  END IF;

                END IF;

                SET :sqlString = NULL;

              END LOOP;

              CLOSE tableRightsCursor;

              -- column rights
              DECLARE columnRightsCursor CURSOR FOR
                SELECT xr$User, xf$Name, xe$Name, xu$Name, xr$Rights, xu$Flags FROM x$Rights, x$File, x$User, x$Field WHERE xr$Object = xf$Id AND xr$Column > 0 AND xr$User = xu$Id AND xf$Id = xe$File AND xe$Id = xr$Column AND xr$Type = 1 ORDER BY xr$User, xf$Name, xr$Column;

              OPEN columnRightsCursor;

              IF SQLSTATE = '00000' THEN

                ColumnRightsCursorLoop:
                LOOP

                  FETCH NEXT FROM columnRightsCursor INTO :rUser, :fName, :eName, :uName, :rFlags, :uFlags;

                  IF SQLSTATE = '02000' THEN
                    LEAVE ColumnRightsCursorLoop;
                  END IF;

                  IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                    IF ( :rFlags & 64 = 64 ) THEN
                      SET :sqlString = 'GRANT SELECT ( "' + RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                    END IF;

                    IF ( :rFlags & 1 = 1 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UNMASK ( "' + RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UNMASK ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 130 = 130 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT UPDATE ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT UPDATE ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :rFlags & 132 = 132 ) THEN
                      IF ( :sqlString IS NULL ) THEN
                        SET :sqlString = 'GRANT INSERT ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        SET :sqlString = :sqlString + 'GRANT INSERT ( "'+ RTRIM(:eName) + '" ) ON ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                      END IF;
                    END IF;

                    IF ( :sqlString IS NOT NULL ) THEN
                      INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                    END IF;

                  END IF;

                  SET :sqlString = NULL;

                END LOOP;

                CLOSE columnRightsCursor;

                -- procedure rights
                DECLARE procedureRightsCursor CURSOR FOR
                  SELECT xr$User, xp$Name, xu$Name, xr$Rights, xu$Flags FROM x$Rights, x$Proc, x$User WHERE xr$Object = xp$Id AND xp$flags = 0 AND xr$User = xu$Id AND xr$Type = 3 ORDER BY xr$User;

                OPEN procedureRightsCursor;

                IF SQLSTATE = '00000' THEN

                  ProcedureRightsCursorLoop:
                  LOOP

                    FETCH NEXT FROM procedureRightsCursor INTO :rUser, :fName, :uName, :rFlags, :uFlags;

                    IF SQLSTATE = '02000' THEN
                      LEAVE ProcedureRightsCursorLoop;
                    END IF;

                    IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                      IF ( :rFlags = 224 ) THEN
                        SET :sqlString = 'GRANT ALL ON PROCEDURE ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                      ELSE
                        IF ( :rFlags = 192 ) THEN
                          SET :sqlString = 'GRANT EXECUTE ON PROCEDURE ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                        END IF;
                      END IF;

                      IF ( :sqlString IS NOT NULL ) THEN
                        INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                      END IF;

                    END IF;

                    SET :sqlString = NULL;

                  END LOOP;

                  CLOSE procedureRightsCursor;

                  -- view rights
                  DECLARE viewRightsCursor CURSOR FOR
                    SELECT xr$User, xv$Name, xu$Name, xr$Rights, xu$Flags FROM x$Rights, x$View, x$User WHERE xr$Object = xv$Id AND xr$User = xu$Id AND xr$Type = 4 ORDER BY xr$User;

                  OPEN viewRightsCursor;

                  IF SQLSTATE = '00000' THEN

                    ViewRightsCursorLoop:
                    LOOP

                      FETCH NEXT FROM viewRightsCursor INTO :rUser, :fName, :uName, :rFlags, :uFlags;

                      IF SQLSTATE = '02000' THEN
                        LEAVE ViewRightsCursorLoop;
                      END IF;

                      IF ( NOT EXISTS ( SELECT xu$Flags & 64 FROM x$User WHERE xu$Id = :rUser AND xu$Flags & 64 = 64 ) OR :uFlags != 0 ) THEN

                        IF ( :rFlags = 238 ) THEN
                          SET :sqlString = 'GRANT ALL ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                        ELSE
                          IF ( :rFlags & 64 = 64 ) THEN
                            SET :sqlString = 'GRANT SELECT ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                          END IF;

                          IF ( :rFlags & 130 = 130 ) THEN
                            IF ( :sqlString IS NULL ) THEN
                              SET :sqlString = 'GRANT UPDATE ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                            ELSE
                              SET :sqlString = :sqlString + 'GRANT UPDATE ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                            END IF;
                          END IF;

                          IF ( :rFlags & 132 = 132 ) THEN
                            IF ( :sqlString IS NULL ) THEN
                              SET :sqlString = 'GRANT INSERT ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                            ELSE
                              SET :sqlString = :sqlString + 'GRANT INSERT ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                            END IF;
                          END IF;

                          IF ( :rFlags & 136 = 136 ) THEN
                            IF ( :sqlString IS NULL ) THEN
                              SET :sqlString = 'GRANT DELETE ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                            ELSE
                              SET :sqlString = :sqlString + 'GRANT DELETE ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                            END IF;
                          END IF;

                          IF ( :rFlags & 160 = 160 ) THEN
                            IF ( :sqlString IS NULL ) THEN
                              SET :sqlString = 'GRANT ALTER ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";';
                            ELSE
                              SET :sqlString = :sqlString + 'GRANT ALTER ON VIEW ' + '"' + RTRIM(:fName) + '"' + ' TO "' + RTRIM(:uName) + '";'; 
                            END IF;
                          END IF;

                        END IF;

                        IF ( :sqlString IS NOT NULL ) THEN
                          INSERT INTO "x$DumpTabColRights" (SQLString) VALUES (:sqlString);
                        END IF;

                      END IF;

                      SET :sqlString = NULL;

                    END LOOP;

                    CLOSE viewRightsCursor;

                    INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping  user table and column rights: ' + CAST(NOW() AS VARCHAR(30))  );
                    COMMIT WORK;

                    -- SELECT SQLString FROM "x$DumpTabColRights" ORDER BY ID;

                  ELSE

                    ROLLBACK WORK;
                    SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

                  END IF;

                ELSE

                  ROLLBACK WORK;
                  SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

                END IF;

              ELSE

                ROLLBACK WORK;
                SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

              END IF;

            ELSE

              ROLLBACK WORK;
              SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

            END IF;

          ELSE

            ROLLBACK WORK;
            SIGNAL 'S1000', 'Unable to open table: x$User/x$Rights.';

          END IF;

        ELSE

          ROLLBACK WORK;
          SIGNAL 'S1000', 'Dump process is already running.';

        END IF;

      ELSE

        SIGNAL 'S1000', 'Fails to create table "x$DumpTabColRights".';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

    END IF;

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpTables ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :clearLogs INT DEFAULT 1 ) WITH DEFAULT HANDLER;
BEGIN

  DECLARE :SQLString         LONGVARCHAR;
  DECLARE :CreateTableStmt   VARCHAR(200);
  DECLARE :Id                INT;
  DECLARE :SubSQLString      LONGVARCHAR;
  DECLARE :Start             INT;
  DECLARE :End               INT = 1;
  DECLARE :LENGTH            INT;

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF SQLSTATE = '00000' THEN

    IF ( UCASE(:srcDBName) = UCASE(DATABASE()) ) THEN -- copy x$DumpTables.mkd from source DB

      IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTables' ) THEN
        CREATE TABLE "x$DumpTables" IN DICTIONARY USING 'x$DumpTables.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;
    
    ELSE

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTables' ) THEN
        DELETE FROM "x$DumpTables";
      ELSE
        CREATE TABLE "x$DumpTables" USING 'x$DumpTables.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );      
      END IF;

      IF SQLSTATE != '00000' THEN
         SIGNAL 'S0002', 'Table "x$DumpTables" not found.';
      ELSE

        SET :CreateTableStmt = 'INSERT INTO x$DumpTables SELECT * FROM ' + RTRIM( :srcDBName ) + '.x$DumpTables ORDER BY ID';
        EXECUTE ( :CreateTableStmt );

      END IF;

    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTables' ) THEN

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump tables: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      IF SQLSTATE = '00000' THEN

        DECLARE x$DumpTablesCursor CURSOR FOR
          SELECT ID, SQLString FROM "x$DumpTables" ORDER BY ID;

        OPEN x$DumpTablesCursor;

        DumpTablesCursorLoop:
        LOOP

          FETCH NEXT FROM x$DumpTablesCursor INTO :Id, :SQLString;

          IF SQLSTATE = '02000' THEN
            LEAVE DumpTablesCursorLoop;
          END IF;

          SET :Start = 1;

          WHILE ( :Start > 0 ) DO

            SELECT LOCATE( ';', SQLString, :Start ) INTO :End FROM "x$DumpTables" WHERE ID = :Id;

            IF ( :End > 0 ) THEN

              SET :LENGTH = :End - :Start;

              SELECT SUBSTRING( SQLString, :Start, :LENGTH ) INTO :SubSQLString FROM "x$DumpTables" WHERE ID = :Id;
               
              EXECUTE ( :SubSQLString );

              IF SQLSTATE != '00000' THEN
                INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Possible failure: ' + :SubSQLString);
              END IF;

              SET SQLSTATE = '00000';
              SET :Start = :End + 1;

            ELSE

              SET :Start = 0;

            END IF;

          END WHILE;

          SET SQLSTATE = '00000';

        END LOOP;

        CLOSE x$DumpTablesCursor;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump tables: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S0002', 'Table "x$DumpTables" not found.';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpViews ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :clearLogs INT DEFAULT 1 ) WITH DEFAULT HANDLER;
BEGIN

  DECLARE :SQLString         LONGVARCHAR;
  DECLARE :CreateTableStmt   VARCHAR(200);
  DECLARE :ViewsTotal        INTEGER;
  DECLARE :ViewsFailed       INTEGER;
  DECLARE :ID                INTEGER;

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  CREATE TABLE "#NestedViewsFixedUp" ( ID INTEGER UNIQUE, SQLString LONGVARCHAR CASE );

  IF SQLSTATE = '00000' THEN

    IF ( UCASE(:srcDBName) = UCASE(DATABASE()) ) THEN -- copy x$DumpViews.mkd from source DB

      IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpViews' ) THEN
        CREATE TABLE "x$DumpViews" IN DICTIONARY USING 'x$DumpViews.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;
    
    ELSE

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpViews' ) THEN
        DELETE FROM "x$DumpViews";
      ELSE
        CREATE TABLE "x$DumpViews" USING 'x$DumpViews.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF SQLSTATE != '00000' THEN
         SIGNAL 'S0002', 'Table "x$DumpViews" not found.';
      ELSE

        SET :CreateTableStmt = 'INSERT INTO x$DumpViews SELECT * FROM ' + RTRIM( :srcDBName ) + '.x$DumpViews ORDER BY ID';
        EXECUTE ( :CreateTableStmt );

      END IF;

    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpViews' ) THEN

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump views: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      SELECT COUNT(*) INTO :ViewsTotal FROM "x$DumpViews";
      SET SQLSTATE = '00000'; -- workaround SQLSTATE for cursor type changed

      IF SQLSTATE = '00000' THEN

        DECLARE x$DumpViewsCursor CURSOR FOR
          SELECT ID, SQLString FROM "x$DumpViews" ORDER BY ID;

        OPEN x$DumpViewsCursor;

        DumpViewsCursorLoop:
        LOOP

          FETCH NEXT FROM x$DumpViewsCursor INTO :ID, :SQLString;

          IF SQLSTATE = '02000' THEN
            LEAVE DumpViewsCursorLoop;
          END IF;
            
          EXECUTE ( :SQLString );

          IF SQLSTATE != '00000' THEN
            INSERT INTO "#NestedViewsFixedUp" VALUES ( :ID, :SQLString );
          END IF;

          SET SQLSTATE = '00000';

        END LOOP;

        CLOSE x$DumpViewsCursor;

        SELECT COUNT(*) INTO :ViewsFailed FROM "#NestedViewsFixedUp";

        WHILE ( :ViewsTotal > :ViewsFailed AND :ViewsFailed != 0 ) DO

          SET :ViewsTotal = :ViewsFailed;
          SET SQLSTATE = '00000';

          OPEN x$DumpViewsCursor;

          DumpViewsCursorLoop2:
          LOOP

            FETCH NEXT FROM x$DumpViewsCursor INTO :ID, :SQLString;

            IF SQLSTATE = '02000' THEN
              LEAVE DumpViewsCursorLoop2;
            END IF;

            IF EXISTS ( SELECT ID FROM "#NestedViewsFixedUp" WHERE ID = :ID ) THEN

              EXECUTE ( :SQLString );

              IF SQLSTATE = '00000' THEN
                DELETE FROM "#NestedViewsFixedUp" WHERE ID = :ID;
              END IF;

            END IF;

            SET SQLSTATE = '00000';

          END LOOP;

          CLOSE x$DumpViewsCursor;

          SELECT COUNT(*) INTO :ViewsFailed FROM "#NestedViewsFixedUp";
          SET SQLSTATE = '00000';

        END WHILE;

        IF :ViewsFailed != 0 THEN
          INSERT INTO "x$DumpLog" (SQLString) SELECT 'Possible failure: ' + SQLString FROM "#NestedViewsFixedUp";
        END IF;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump views: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S0002', 'Table "x$DumpViews" not found.';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpProcedures ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :clearLogs INT DEFAULT 1 ) WITH DEFAULT HANDLER;
BEGIN

  DECLARE :SQLString         LONGVARCHAR;
  DECLARE :CreateTableStmt   VARCHAR(200);

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF SQLSTATE = '00000' THEN

    IF ( UCASE(:srcDBName) = UCASE(DATABASE()) ) THEN -- copy x$DumpProcedures.mkd from source DB

      IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpProcedures' ) THEN
        CREATE TABLE "x$DumpProcedures" IN DICTIONARY USING 'x$DumpProcedures.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;
    
    ELSE

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpProcedures' ) THEN
        DELETE FROM "x$DumpProcedures";
      ELSE
        CREATE TABLE "x$DumpProcedures" USING 'x$DumpProcedures.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF SQLSTATE != '00000' THEN
         SIGNAL 'S0002', 'Table "x$DumpProcedures" not found.';
      ELSE

        SET :CreateTableStmt = 'INSERT INTO x$DumpProcedures SELECT * FROM ' + RTRIM( :srcDBName ) + '.x$DumpProcedures ORDER BY ID';
        EXECUTE ( :CreateTableStmt );

      END IF;

    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpProcedures' ) THEN

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump procedures: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      IF SQLSTATE = '00000' THEN

        DECLARE x$DumpProceduresCursor CURSOR FOR
          SELECT SQLString FROM "x$DumpProcedures" ORDER BY ID;

        OPEN x$DumpProceduresCursor;

        DumpProceduresCursorLoop:
        LOOP

          FETCH NEXT FROM x$DumpProceduresCursor INTO :SQLString;

          IF SQLSTATE = '02000' THEN
            LEAVE DumpProceduresCursorLoop;
          END IF;

          EXECUTE ( :SQLString );

          IF SQLSTATE != '00000' THEN
            INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Possible failure: ' + :SQLString);
          END IF;

          SET SQLSTATE = '00000';

        END LOOP;

        CLOSE x$DumpProceduresCursor;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump procedures: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S0002', 'Table "x$DumpProcedures" not found.';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpTriggers ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :clearLogs INT DEFAULT 1 ) WITH DEFAULT HANDLER;
BEGIN

  DECLARE :SQLString         LONGVARCHAR;
  DECLARE :CreateTableStmt   VARCHAR(200);

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF SQLSTATE = '00000' THEN

    IF ( UCASE(:srcDBName) = UCASE(DATABASE()) ) THEN -- copy x$DumpTriggers.mkd from source DB

      IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTriggers' ) THEN
        CREATE TABLE "x$DumpTriggers" IN DICTIONARY USING 'x$DumpTriggers.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

    ELSE

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTriggers' ) THEN
        DELETE FROM "x$DumpTriggers";
      ELSE
        CREATE TABLE "x$DumpTriggers" USING 'x$DumpTriggers.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF SQLSTATE != '00000' THEN
        SIGNAL 'S0002', 'Table "x$DumpTriggers" not found.';
      ELSE

        SET :CreateTableStmt = 'INSERT INTO x$DumpTriggers SELECT * FROM ' + RTRIM( :srcDBName ) + '.x$DumpTriggers ORDER BY ID';
        EXECUTE ( :CreateTableStmt );

      END IF;

    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTriggers' ) THEN

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump triggers: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      IF SQLSTATE = '00000' THEN

        DECLARE x$DumpTriggersCursor CURSOR FOR
          SELECT SQLString FROM "x$DumpTriggers" ORDER BY ID;

        OPEN x$DumpTriggersCursor;

        DumpTriggersCursorLoop:
        LOOP

          FETCH NEXT FROM x$DumpTriggersCursor INTO :SQLString;

          IF SQLSTATE = '02000' THEN
            LEAVE DumpTriggersCursorLoop;
          END IF;

          EXECUTE ( :SQLString );

          IF SQLSTATE != '00000' THEN
            INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Possible failure: ' + :SQLString);
          END IF;

          SET SQLSTATE = '00000';

        END LOOP;

        CLOSE x$DumpTriggersCursor;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump triggers: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S0002', 'Table "x$DumpTriggers" not found.';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpUsers ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :clearLogs INT DEFAULT 1 ) WITH DEFAULT HANDLER;
BEGIN

  DECLARE :SQLString         LONGVARCHAR;
  DECLARE :CreateTableStmt   VARCHAR(200);

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF SQLSTATE = '00000' THEN

    IF ( UCASE(:srcDBName) = UCASE(DATABASE()) ) THEN -- copy x$DumpUsers.mkd from source DB

      IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUsers' ) THEN
        CREATE TABLE "x$DumpUsers" IN DICTIONARY USING 'x$DumpUsers.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

    ELSE

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUsers' ) THEN
        DELETE FROM "x$DumpUsers";
      ELSE
        CREATE TABLE "x$DumpUsers" USING 'x$DumpUsers.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF SQLSTATE != '00000' THEN
        SIGNAL 'S0002', 'Table "x$DumpUsers" not found.';
      ELSE

        SET :CreateTableStmt = 'INSERT INTO x$DumpUsers SELECT * FROM ' + RTRIM( :srcDBName ) + '.x$DumpUsers ORDER BY ID';
        EXECUTE ( :CreateTableStmt );

      END IF;

    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUsers' ) THEN

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump users: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      IF SQLSTATE = '00000' THEN

        IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$User' ) THEN

          INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$User is missing.' );

        ELSE

          DECLARE x$DumpUsersCursor CURSOR FOR
            SELECT SQLString FROM "x$DumpUsers" ORDER BY ID;

          OPEN x$DumpUsersCursor;

          DumpUsersCursorLoop:
          LOOP

            FETCH NEXT FROM x$DumpUsersCursor INTO :SQLString;

            IF SQLSTATE = '02000' THEN
              LEAVE DumpUsersCursorLoop;
            END IF;

            EXECUTE ( :SQLString );

            IF SQLSTATE != '00000' THEN
              INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Possible failure: ' + :SQLString);
            END IF;

            SET SQLSTATE = '00000';

          END LOOP;

          CLOSE x$DumpUsersCursor;

        END IF;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump users: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S0002', 'Table "x$DumpUsers" not found.';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpUserRights ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :clearLogs INT DEFAULT 1 ) WITH DEFAULT HANDLER;
BEGIN

  DECLARE :SQLString         LONGVARCHAR;
  DECLARE :CreateTableStmt   VARCHAR(200);
  DECLARE :Id                INT;
  DECLARE :SubSQLString      LONGVARCHAR;
  DECLARE :Start             INT;
  DECLARE :End               INT = 1;
  DECLARE :LENGTH            INT;

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF SQLSTATE = '00000' THEN

    IF ( UCASE(:srcDBName) = UCASE(DATABASE()) ) THEN -- copy x$DumpUserRights.mkd from source DB

      IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUserRights' ) THEN
        CREATE TABLE "x$DumpUserRights" IN DICTIONARY USING 'x$DumpUserRights.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

    ELSE

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUserRights' ) THEN
        DELETE FROM "x$DumpUserRights";
      ELSE
        CREATE TABLE "x$DumpUserRights" USING 'x$DumpUserRights.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF SQLSTATE != '00000' THEN
        SIGNAL 'S0002', 'Table "x$DumpUserRights" not found.';
      ELSE

        SET :CreateTableStmt = 'INSERT INTO x$DumpUserRights SELECT * FROM ' + RTRIM( :srcDBName ) + '.x$DumpUserRights ORDER BY ID';
        EXECUTE ( :CreateTableStmt );

      END IF;

    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpUserRights' ) THEN

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump user rights: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      IF SQLSTATE = '00000' THEN

        IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$User' ) THEN

          INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'x$User is missing.' );

        ELSE

          DECLARE x$DumpUserRightsCursor CURSOR FOR
            SELECT ID, SQLString FROM "x$DumpUserRights" ORDER BY ID;

          OPEN x$DumpUserRightsCursor;

          DumpUserRightsCursorLoop:
          LOOP

            FETCH NEXT FROM x$DumpUserRightsCursor INTO :Id, :SQLString;

            IF SQLSTATE = '02000' THEN
              LEAVE DumpUserRightsCursorLoop;
            END IF;

            SET :Start = 1;

            WHILE ( :Start > 0 ) DO

              SELECT LOCATE( ';', SQLString, :Start ) INTO :End FROM "x$DumpUserRights" WHERE ID = :Id;

              IF ( :End > 0 ) THEN

                SET :LENGTH = :End - :Start;

                SELECT SUBSTRING( SQLString, :Start, :LENGTH ) INTO :SubSQLString FROM "x$DumpUserRights" WHERE ID = :Id;
               
                EXECUTE ( :SubSQLString );

                IF SQLSTATE != '00000' THEN
                  INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Possible failure: ' + :SubSQLString);
                END IF;

                SET SQLSTATE = '00000';
                SET :Start = :End + 1;

              ELSE

                SET :Start = 0;

              END IF;

            END WHILE;

            SET SQLSTATE = '00000';

          END LOOP;

          CLOSE x$DumpUserRightsCursor;

        END IF;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump user rights: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S0002', 'Table "x$DumpUserRights" not found.';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpTabColRights ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :clearLogs INT DEFAULT 1 ) WITH DEFAULT HANDLER;
BEGIN

  DECLARE :SQLString         LONGVARCHAR;
  DECLARE :CreateTableStmt   VARCHAR(200);
  DECLARE :Id                INT;
  DECLARE :SubSQLString      LONGVARCHAR;
  DECLARE :Start             INT;
  DECLARE :End               INT = 1;
  DECLARE :LENGTH            INT;

  IF ( :clearLogs = 1 ) THEN

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
      DROP TABLE "x$DumpLog";
    END IF;

    IF SQLSTATE = '00000' THEN
      CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
    END IF;

  END IF;

  IF SQLSTATE = '00000' THEN

    IF ( UCASE(:srcDBName) = UCASE(DATABASE()) ) THEN -- copy x$DumpTabColRights.mkd from source DB

      IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTabColRights' ) THEN
        CREATE TABLE "x$DumpTabColRights" IN DICTIONARY USING 'x$DumpTabColRights.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

    ELSE

      IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTabColRights' ) THEN
        DELETE FROM "x$DumpTabColRights";
      ELSE
        CREATE TABLE "x$DumpTabColRights" USING 'x$DumpTabColRights.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
      END IF;

      IF SQLSTATE != '00000' THEN
        SIGNAL 'S0002', 'Table "x$DumpTabColRights" not found.';
      ELSE

        SET :CreateTableStmt = 'INSERT INTO x$DumpTabColRights SELECT * FROM ' + RTRIM( :srcDBName ) + '.x$DumpTabColRights ORDER BY ID';
        EXECUTE ( :CreateTableStmt );

      END IF;

    END IF;

    IF EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpTabColRights' ) THEN

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
        UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump user table and column rights: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;
      ELSE
        UPDATE "x$DumpLog" SET ID = 1 WHERE ID = 1;
      END IF;

      IF SQLSTATE = '00000' THEN

        IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$User' ) OR NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$Rights' ) THEN

          INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Either x$User or x$Rights is missing.' );

        ELSE

          DECLARE x$DumpTabColRightsCursor CURSOR FOR
            SELECT Id, SQLString FROM "x$DumpTabColRights" ORDER BY ID;

          OPEN x$DumpTabColRightsCursor;

          DumpTabColRightsCursorLoop:
          LOOP

            FETCH NEXT FROM x$DumpTabColRightsCursor INTO :Id, :SQLString;

            IF SQLSTATE = '02000' THEN
              LEAVE DumpTabColRightsCursorLoop;
            END IF;

            SET :Start = 1;

            WHILE ( :Start > 0 ) DO

              SELECT LOCATE( ';', SQLString, :Start ) INTO :End FROM "x$DumpTabColRights" WHERE ID = :Id;

              IF ( :End > 0 ) THEN

                SET :LENGTH = :End - :Start;

                SELECT SUBSTRING( SQLString, :Start, :LENGTH ) INTO :SubSQLString FROM "x$DumpTabColRights" WHERE ID = :Id;
               
                EXECUTE ( :SubSQLString );

                IF SQLSTATE != '00000' THEN
                  INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Possible failure: ' + :SubSQLString);
                END IF;

                SET SQLSTATE = '00000';
                SET :Start = :End + 1;

              ELSE

                SET :Start = 0;

              END IF;

            END WHILE;

            SET SQLSTATE = '00000';

          END LOOP;

          CLOSE x$DumpTabColRightsCursor;

        END IF;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump user table and column rights: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S0002', 'Table "x$DumpTabColRights" not found.';

    END IF;

  ELSE

    SIGNAL 'S1000', 'Table "x$DumpLog" is in use.';

  END IF;

END;
?

CREATE PROCEDURE psp_dumpDB (IN :inDict INT DEFAULT 0) WITH DEFAULT HANDLER 
AS
BEGIN

  DECLARE :v2MetaData    INT = 0;

  SELECT CASE LOWER(RTRIM(xf$Loc)) WHEN 'pvfile.ddf' THEN 1 ELSE 0 END INTO :v2MetaData FROM x$File WHERE xf$Id = 1;

  IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
    CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
  END IF;

  IF SQLSTATE = '00000' THEN

    DELETE FROM "x$DumpLog";

    IF SQLSTATE = '00000' THEN

      DELETE FROM "x$DumpTables";
      DELETE FROM "x$DumpViews";
      DELETE FROM "x$DumpProcedures";
      DELETE FROM "x$DumpTriggers";
      DELETE FROM "x$DumpUsers";
      DELETE FROM "x$DumpUserRights";
      DELETE FROM "x$DumpTabColRights";

      SET SQLSTATE = '00000';

      START TRANSACTION;

      IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
        INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
      END IF;
      UPDATE "x$DumpLog" SET SQLString = 'Start dumping database: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;

      IF SQLSTATE = '00000' THEN
  
        CALL psp_dumpTables ( :inDict, 0 );
        CALL psp_dumpViews ( 0 );
        CALL psp_dumpProcedures ( 0 );
        CALL psp_dumpTriggers ( 0 );
        CALL psp_dumpUsers ( 0 );
        CALL psp_dumpUserRights ( 0 );

        IF :v2MetaData = 1 THEN
          CALL psp_dumpTabColRights2 ( 0 );
        ELSE
          CALL psp_dumpTabColRights ( 0 );
        END IF;

        INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish dumping database: ' + CAST(NOW() AS VARCHAR(30))  );
        COMMIT WORK;

      ELSE

        ROLLBACK WORK;   
        SIGNAL 'S1000', 'Dump process is already running.';

      END IF;

    ELSE

      SIGNAL 'S1000', 'Dump process is already running.';

    END IF;

  ELSE

    SIGNAL 'S0002', 'Table "x$DumpLog" not found.';

  END IF;

END;
?

CREATE PROCEDURE psp_execDumpDB ( IN :srcDBName VARCHAR(30) DEFAULT DATABASE(), IN :secureDb INT DEFAULT 0 )
AS
BEGIN

  IF NOT EXISTS ( SELECT xf$Name FROM x$File WHERE xf$Name = 'x$DumpLog' ) THEN
    CREATE TABLE "x$DumpLog" USING 'x$DumpLog.MKD' ( ID IDENTITY, SQLString LONGVARCHAR CASE );
  END IF;

  IF SQLSTATE = '00000' THEN

    START TRANSACTION;

    IF NOT EXISTS (SELECT ID FROM "x$DumpLog" WHERE ID = 1) THEN
      INSERT INTO "x$DumpLog" VALUES ( 1, NULL );
    END IF;

    UPDATE "x$DumpLog" SET SQLString = 'Start executing from dump database: ' + CAST(NOW() AS VARCHAR(30)) WHERE ID = 1;

    IF SQLSTATE = '00000' THEN

      CALL psp_execDumpTables ( :srcDBName, 0 );
      CALL psp_execDumpProcedures ( :srcDBName, 0 );
      CALL psp_execDumpViews ( :srcDBName, 0 );
      CALL psp_execDumpTriggers ( :srcDBName, 0 );

      IF ( :secureDb = 1 ) THEN

        CALL psp_execDumpUsers ( :srcDBName, 0 );
        CALL psp_execDumpUserRights ( :srcDBName, 0 );
        CALL psp_execDumpTabColRights ( :srcDBName, 0 );

      END IF;

      INSERT INTO "x$DumpLog" (SQLString) VALUES ( 'Finish executing from dump database: ' + CAST(NOW() AS VARCHAR(30))  );
      COMMIT WORK;

    ELSE

      ROLLBACK WORK;   
      SIGNAL 'S1000', 'Dump process is already running.';

    END IF;

  ELSE

    SIGNAL 'S0002', 'Table "x$DumpLog" not found.';

  END IF;

END;
?

/*
 * This system procedure is used to validate the columns
 * between two databases.
 */
CREATE PROCEDURE psp_verifyDumpColumnsEtc (
	in :database_source   VARCHAR(20), -- Name of the source database
	in :check_type		  INTEGER	   -- Which query to run: 
	                                   -- 1=extra/mismatched columns in new db, 
	                                   -- 2=missing columns from old
)
RETURNS (
	NEW_COLUMN_NAME   VARCHAR (255),	-- Extra Column Name
	NEW_OFFSET	 	  UINTEGER,			-- Extra Column's Offset
	NEW_DATATYPE	  UTINYINT,  		-- Extra Column's Datatype
	NEW_TABLE_NAME	  VARCHAR(255),	    -- Extra Column's Table Name
	OLD_COLUMN_NAME   VARCHAR(255),		-- Missing Column Name
	OLD_OFFSET		  UINTEGER,			-- Missing Column's Offset
	OLD_DATATYPE	  UTINYINT,			-- Missing Column's Datatype
	OLD_TABLE_NAME	  VARCHAR(255)	    -- Missing Column's Table Name
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    SET :database_qual = DATABASE();
    SET :stmt_psp_verify = '';
    
    IF (:check_type = 1) THEN
      SET :stmt_psp_verify = 'select ' +
            'N.xe$name NewColumn, N.xe$offset NewOffset, N.xe$datatype NewDataType, NF.xf$Name NewName, ' + 
            'O.xe$name OLDColumn, O.xe$offset OLDOffset, O.xe$datatype OLDDataType, OxF.Xf$Name OldName ' + 
            'from ' + :database_qual + '.x$field N inner join ' + :database_qual + '.x$file NF on N.xe$file = NF.xf$id ' + 
            'inner join ' + :database_source + '.x$file OxF on NF.xf$name = OxF.xf$name ' +  
            'left outer join ' + :database_source + '.x$field O on O.xe$file = OxF.xf$id and N.xe$name = O.xe$name ' + 
            'where NewDataType < 200 and NewName not like ''x$Dump%'' and NF.xf$Flags <> 16 ' +
            'and ( (O.xe$name is null) or (O.xe$offset <> N.xe$offset) or (O.xe$Dec <> N.xe$Dec) or ' +
            '      (O.xe$datatype <> N.xe$datatype and O.xe$datatype<>1 ) or ' +
            '      (O.xe$datatype = 1 AND O.xe$size = 1 AND NF.xf$flags = 0 AND O.xe$Flags&8=0 AND N.xe$datatype <> 14) )'; 
    END IF;
 
  IF (:check_type = 2) THEN
      SET :stmt_psp_verify = 'select ' +
            'N.xe$name NewColumn, N.xe$offset NewOffset, N.xe$datatype NewDataType, NF.xf$Name NewName, ' +
            'O.xe$name OLDColumn, O.xe$offset OLDOffset, O.xe$datatype OLDDataType, OxF.xf$name OldName ' + 
            'from ' + :database_source + '.x$field O inner join ' + :database_source + '.x$file OxF on O.xe$file = OxF.xf$id ' + 
            'inner join ' + :database_qual + '.x$file NF on OxF.xf$name = NF.xf$name ' +  
            'left outer join ' + :database_qual + '.x$field N on N.xe$file = NF.xf$id and O.xe$name = N.xe$name ' +
            'where OldDataType < 200 and OldName not like ''x$Dump%'' and OxF.xf$Flags <> 16 ' +
            'and ( N.xe$name is null or N.xe$offset is null or N.xe$datatype is null )';
    END IF;

    IF (:stmt_psp_verify = '') then
 	  SIGNAL 'S1000', 'Valid Check types are 1-2.'; 
	end if;                  
                               
    exec (:stmt_psp_verify);
END; -- End of stored procedure. 
?

/*
 * This system procedure is used to validate the foreign keys
 * between two databases.
 */
 
CREATE PROCEDURE psp_verifyDumpFKeys (
	in :database_source   VARCHAR(20) -- Name of the source database
)
RETURNS (
	NEW_PK_TABLE_NAME   VARCHAR (255),	-- Extra PK Table Name
	NEW_FK_TABLE_NAME	VARCHAR (255),	-- Extra FK Table Name
	NEW_FK_NAME			VARCHAR (255),  -- Extra Foreign Key Name
	OLD_PK_TABLE_NAME   VARCHAR (255),	-- Missing PK Table Name
	OLD_FK_TABLE_NAME	VARCHAR (255),	-- Missing FK Table Name
	OLD_PK_NAME			VARCHAR (255)	-- Missing Foreign Key Name
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    
    SET :database_qual = DATABASE();
    SET :stmt_psp_verify = 'select N.PKTABLE_NAME,  N.FKTABLE_NAME, N.FK_NAME, ' +
                           'O.PKTABLE_NAME,  O.FKTABLE_NAME, O.FK_NAME from dbo.fsqlforeignkeys ( ''' +
                           :database_qual +
                           ''',  ''%'', ''%''  ) N left join dbo.fsqlforeignkeys ( ''' +
                           :database_source +
                           ''',  ''%'' , ''%'' ) O on N.FK_NAME = O.FK_NAME where O.FK_NAME is null union all '+
                           'select N.PKTABLE_NAME,  N.FKTABLE_NAME, N.FK_NAME, ' +
                           'O.PKTABLE_NAME,  O.FKTABLE_NAME, O.FK_NAME from dbo.fsqlforeignkeys ( ''' +
                           :database_qual +
                           ''',  ''%'', ''%''  ) N right join dbo.fsqlforeignkeys ( ''' +
                           :database_source +
                           ''',  ''%'' , ''%'' ) O on N.FK_NAME = O.FK_NAME where N.FK_NAME is null';
   exec (:stmt_psp_verify);
END; -- End of stored procedure. 
?

/*
 * This system procedure is used to validate the foreign key rules
 * between two databases.
 */
 
CREATE PROCEDURE psp_verifyDumpFKRules (
	in :database_source   VARCHAR(20), -- Name of the source database
	in :FK_rule			  CHAR(1)	   -- Rule type to check - U or D
)
RETURNS (
	NEW_PK_TABLE_NAME   VARCHAR (255),	-- New PK Table Name
	NEW_FK_TABLE_NAME	VARCHAR (255),	-- New FK Table Name
	NEW_FK_NAME			VARCHAR (255),  -- New Foreign Key Name
	NEW_FK_RULE			SMALLINT,	    -- New FK Rule value
	OLD_PK_TABLE_NAME   VARCHAR (255),	-- Old PK Table Name
	OLD_FK_TABLE_NAME	VARCHAR (255),	-- Old FK Table Name
	OLD_PK_NAME			VARCHAR (255),	-- Old Foreign Key Name
	OLD_FK_RULE	        SMALLINT	    -- Old FK Rule value
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    
    SET :stmt_psp_verify = '';
    SET :database_qual = DATABASE();
    
    IF (ucase(:FK_rule) = 'U') THEN
    SET :stmt_psp_verify = 'select N."PKTABLE_NAME",  N."FKTABLE_NAME", N."FK_NAME", N."UPDATE_RULE", ' +
                           'O."PKTABLE_NAME",  O."FKTABLE_NAME", O."FK_NAME", O."UPDATE_RULE" from dbo.fsqlforeignkeys (''' +
                           :database_qual + ''', ''%'', ''%'' ) N left join dbo.fsqlforeignkeys (''' +
                           :database_source + ''', ''%'', ''%'') O ' +
                           'on N."FK_NAME" = O."FK_NAME" and N."FKTABLE_NAME" = O."FKTABLE_NAME" ' +
                           'where N."UPDATE_RULE" <> O."UPDATE_RULE"';
    END IF;
    
    IF (ucase(:FK_rule) = 'D') THEN
    SET :stmt_psp_verify = 'select N."PKTABLE_NAME",  N."FKTABLE_NAME", N."FK_NAME", N."DELETE_RULE", ' +
                           'O."PKTABLE_NAME",  O."FKTABLE_NAME", O."FK_NAME", O."DELETE_RULE" from dbo.fsqlforeignkeys (''' +
                           :database_qual + ''', ''%'', ''%'') N inner join dbo.fsqlforeignkeys (''' +
                           :database_source + ''', ''%'', ''%'') O ' +
                           'on N."FK_NAME" = O."FK_NAME" and N."FKTABLE_NAME" = O."FKTABLE_NAME" ' +
                           'where N."DELETE_RULE" <> O."DELETE_RULE"';
    END IF;
    
    if (:stmt_psp_verify = '') then
 		SIGNAL 'S1000', 'Valid Foreign Key rule types are U and D.'; 
	end if;    
       
   exec (:stmt_psp_verify);
END; -- End of stored procedure. 
?

/*
 * This system procedure is used to validate the table indexes
 * between two databases.
 */
 
CREATE PROCEDURE psp_verifyDumpIndexes (
	in :database_source   VARCHAR(20) -- Name of the source database
)
RETURNS (
	OLD_TABLE_NAME   VARCHAR (255),	-- Missing Index's Table Name
	OLD_INDEX_NAME	 VARCHAR (255)	-- Missing Index Name
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    
    SET :database_qual = DATABASE();
    SET :stmt_psp_verify = 'select distinct bf.xf$name, ifnull (bef.xe$name, bi.xi$number) ' +
                           'from ' + :database_source + '.x$file bf ' + 
                           'inner join ' + :database_source + '.x$index bi on bf.xf$id = bi.xi$file ' +
                           'inner join ' + :database_qual + '.x$file b1f on bf.xf$name = b1f.xf$name ' +
                           'left outer join ' + :database_source + '.x$field bef on ' +
                           'bf.xf$id = bef.xe$file and bi.xi$number = bef.xe$offset ' + 
                           'where (bef.xe$datatype is null OR bef.xe$datatype = 255) and ' + 
                           'bi.xi$number not in (select b2i.xi$number from ' + 
                            :database_qual + '.x$index b2i,' + :database_qual + '.x$file b2f ' +
                            'where b2f.xf$name = bf.xf$name and b2f.xf$id = b2i.xi$file) and bf.xf$name NOT LIKE ''x$%''';
   exec (:stmt_psp_verify);
END; -- End of stored procedure. 
?

/*
 * This system procedure is used to validate the primary keys
 * between two databases.
 */
 
CREATE PROCEDURE psp_verifyDumpPKeys (
	in :database_source   VARCHAR(20) -- Name of the source database
)
RETURNS (
	NEW_TABLE_NAME      VARCHAR (255),	-- Extra Table Name
	NEW_PK_NAME			VARCHAR(255),   -- Extra Primary Key Name
	OLD_TABLE_NAME      VARCHAR (255),	-- Missing Table Name
	OLD_PK_NAME			VARCHAR(255)	-- Missing Primary Key Name
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    
    SET :database_qual = DATABASE();
    SET :stmt_psp_verify = 'select N.TABLE_NAME, N.PK_NAME , O.TABLE_NAME,  O.PK_NAME from dbo.fsqlprimarykeys (''' +
                            :database_source +
                            ''',  ''%'' ) N left join dbo.fsqlprimarykeys (''' +
                            :database_qual +
                            ''',  ''%'' ) O on N.PK_NAME = O.PK_NAME where O.PK_NAME is null ' ;
   exec (:stmt_psp_verify);
END; -- End of stored procedure. 
?

/*
 * This system procedure is used to check for missing Procedures/UDFs, Views, 
 * Triggers, Users/Groups between two databases.
 */
 
CREATE PROCEDURE psp_verifyDumpProcEtc (
	in :database_source   VARCHAR(20),  -- Name of the source database
	in :meta_type		  CHAR(1)		-- Type of search: P, V, T, U
)
RETURNS (
	OLD_NAME   VARCHAR (255)	-- Missing P/V/T/U Name
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    
    SET :stmt_psp_verify = '';
    SET :database_qual = DATABASE();    
    
    --check for missing procedures/udfs:
    IF (ucase(:meta_type) = 'P') THEN
    SET :stmt_psp_verify = 'select distinct xp$name from ' +
                           :database_source +'.x$proc where xp$name not in (select xp$name from ' +
                           :database_qual +'.x$proc)';
    END IF;

    --check for missing views:
    IF (ucase(:meta_type) = 'V') THEN
    SET :stmt_psp_verify = 'select distinct xv$name from ' +
                           :database_source + '.x$view where xv$name not in (select xv$name from ' +
                           :database_qual +'.x$view)';
    END IF;                       

    --check for missing triggers:
    IF (ucase(:meta_type) = 'T') THEN
    SET :stmt_psp_verify = 'select distinct xt$name from ' +
                           :database_source + '.x$trigger where xt$name not in (select xt$name from ' +
                           :database_qual + '.x$trigger)';
    END IF;
    
    --check for missing users/groups if security is enabled:
    IF (ucase(:meta_type) = 'U') THEN
    SET :stmt_psp_verify = 'select distinct xu$name from ' +
                           :database_source + '.x$user where xu$name not in (select xu$name from ' +
                           :database_qual + '.x$user)';
    END IF;     
    
    if (:stmt_psp_verify = '') then
 		SIGNAL 'S1000', 'Valid Metadata types are P, V, T, and U.'; 
	end if;                  
	
   exec (:stmt_psp_verify);
END; -- End of stored procedure. 
?

/*
 * This system procedure is used to validate the table names
 * between two databases.
 */
 
CREATE PROCEDURE psp_verifyDumpTables (
	in :database_source   VARCHAR(20) -- Name of the source database
)
RETURNS (
	NEW_TABLE_NAME      VARCHAR (255),	-- Extra Table Name
	OLD_TABLE_NAME      VARCHAR (255)	-- Missing Table Name
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    
    SET :database_qual = DATABASE();
    SET :stmt_psp_verify = 'select N.xf$name, O.xf$name from ' + 
                           :database_qual +
                           '.x$file N left outer join ' + 
        	               :database_source +
        	               '.x$file  O on N.xf$name = O.xf$name where O.xf$name is null union all ' +
                           'select N.xf$name, O.xf$name from ' + 
                           :database_qual + 
                           '.x$file N right outer join ' + 
                           :database_source + 
                           '.x$file  O on N.xf$name = O.xf$name where N.xf$name is null and O.xf$name NOT LIKE ''x$%'''; 

   exec (:stmt_psp_verify);
END; -- End of stored procedure. 
?

/*
 * This system procedure is used to validate the column defaults
 * between two databases.
 */
CREATE PROCEDURE psp_verifyDumpColumnAttribs (
	in :database_source   VARCHAR(20) -- Name of the source database
)
RETURNS (
	NEW_COLUMN_NAME   VARCHAR (255),	-- Extra/Mismatched Attrib Column Name
	NEW_TYPE	 	  VARCHAR(1),		-- Extra/Mismatched Attrib Type (D/O)
	NEW_ATTRIB		  VARCHAR(255),  	-- Extra/Mismatched Attrib Value
	NEW_TABLE_NAME	  VARCHAR(255),	    -- Extra/Mismatched Attrib Table Name
	OLD_COLUMN_NAME   VARCHAR(255),		-- Missing Attrib Column Name
	OLD_TYPE		  VARCHAR(1),		-- Missing Attrib Type (D/O)
	OLD_ATTRIB		  VARCHAR(255),		-- Missing Attrib Value
	OLD_TABLE_NAME	  VARCHAR(255)	    -- Missing Attrib Table Name
)
as 
BEGIN
	-- Check if the database_source parameter is null
	if (:database_source is null) then
 		SIGNAL 'S1000', 'Source Database cannot be null'; 
	end if;
    
    DECLARE :stmt_psp_verify LONGVARCHAR;
    DECLARE :database_qual VARCHAR(20);
    SET :database_qual = DATABASE();
    SET :stmt_psp_verify = '';
    
    SET :stmt_psp_verify = 'select ' +
       'NE.xe$name NewColumn, left(NA.Xa$Type,1) NewType, left(NA.Xa$Attrs, 255) NewAttrib, NF.Xf$name NewName, ' +
       'OE.xe$name OldColumn, left(OA.Xa$Type,1) OldType, left(OA.Xa$Attrs, 255) OldAttrib, OxF.Xf$name OldName ' +
       'from ' + :database_qual + '.x$attrib NA inner join ' + :database_qual + '.x$field NE on NA.xa$id = NE.xe$id ' + 
       'inner join ' + :database_qual + '.x$file NF on NE.xe$file = NF.xf$id ' + 
       'inner join ' + :database_source + '.x$file OxF on NF.xf$name = OxF.xf$name ' +
       'inner join ' + :database_source + '.x$field OE on OxF.xf$id = OE.xe$file AND OE.xe$name = NE.xe$name ' +
       'left outer join ' + :database_source + '.x$attrib OA on OE.xe$id = OA.xa$id AND NewType = OldType ' + 
       'where NE.xe$Datatype < 200 AND NewName not like ''x$Dump%'' and NF.xf$Flags <> 16 AND ' +
       '((OA.xa$id is null AND OE.xe$datatype <> 15) OR (NewType <> OldType) OR ' +
       '(cast(NA.Xa$Attrs as varchar(2000)) <> cast(OA.Xa$Attrs as varchar(2000))) ) ' + 
       'UNION select ' +
       'NE.xe$name NewColumn, left(NA.Xa$Type, 1) NewType, left(NA.Xa$Attrs, 255) NewAttrib, NF.Xf$name NewName, ' + 
       'OE.xe$name OldColumn, left(OA.Xa$Type, 1) OldType, left(OA.Xa$Attrs, 255) OldAttrib, OxF.Xf$name OldName ' +
       'from ' + :database_source + '.x$attrib OA inner join ' + :database_source +'.x$field OE on OA.xa$id = OE.xe$id ' + 
       'inner join ' + :database_source + '.x$file OxF on OE.xe$file = OxF.xf$id ' + 
       'inner join ' + :database_qual + '.x$file NF on OxF.xf$name = NF.xf$name ' +
       'inner join ' + :database_qual + '.x$field NE on NF.xf$id = NE.xe$file AND Ne.xe$name = OE.xe$name ' +
       'left outer join ' + :database_qual + '.x$attrib NA on NE.xe$id = NA.xa$id AND OldType = NewType ' +
       'where OE.xe$Datatype < 200 AND OldName not like ''x$Dump%'' and OxF.xf$Flags <> 16 AND ' +
       'NA.xa$id is null';
                             
    exec (:stmt_psp_verify);
END;
?