Chinaunix首页 | 论坛 | 博客
  • 博客访问: 94609
  • 博文数量: 6
  • 博客积分: 205
  • 博客等级: 入伍新兵
  • 技术积分: 1304
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-25 13:37
文章分类
文章存档

2012年(6)

我的朋友

分类: DB2/Informix

2012-05-25 13:44:30

Motivation

Imagine you are running a website like online banking. 

Imagine further you want to get as close to zero downtime as possible.

 

So you will invest into DB2 pureScale, HADR, or perhaps you choose a replication solution.

All these capabilities will get you close, protecting you against unplanned outages as well as outages related to hardware and OS upgrades. 

 

What none of them do is helping you deploy application upgrades!

Traditionally when you upgrade an application you will take a planned outage. 
Then you tear down the existing application objects on the database and then rebuild the new version.

This can take some time.

 

A customer I am working with is however asking to do scheduled application upgrades once a quarter - with zero impact to their online presence.

The lay of the land

Let's first take a look at the topology of such an app:

1.    There is the client side application which is most likely written in Java

2.    There are multiple application servers (Websphere or Weblogic most likely)

3.    There is server side logic present in

o    Modules

o    PL/SQL Packages

o    Functions

o    Procedures

o    Types

o    Variables

This application is ultimately running on a database schema consisting of

·        Tables

·        Views

·        Indexes

·        Sequences

·        Triggers

·        .. and data :-)

In a serious development environment the application source will be tightly controlled  in some version control system with methods to deploy on a test system, integration system and production system.

 

Upgrading the client side part of the application is rather straight forward. 
All it takes is a rolling upgrade through the web servers.
 One web server at a time is taken offline, upgraded and then re-integrated.
But how do we upgrade the server side application? DB2 does not support explicit versioning of modules and the likes.   

 

Making it happen

Luckily DB2 supports something else which does the job very nicely as long as some basic best practices are followed. 
DB2 resolves application objects by PATH.

In DB2 you can have objects with the same name in multiple schemata. 
Using the session level PATH registry variable you can control which application sees which objects.

There is no hard limit on how many schemata can be used.  
Unlike with other DBMS such as Oracle, there is no also security concern because a schema is orthogonal to a user. 
Therefor the same set of users can own objects in many schemata.

 

So let's establish a few ground rules for our application:

·        The source code of the objects must not use explicit qualifiers when referencing another application objects.
Good:

  SET x = foo();

Bad:

  SET x = myapp.foo();

·        This includes the name of the application object itself:
Good:

  CREATE OR REPLACE foo() ...

Bad:

  CREATE OR REPLACE myapp.foo()...

·        When referencing the db schema objects a schema name must be used unless a public synonym exists
Good:

  SELECT * FROM myschema.t;

Bad:

  SELECT * FROM t;

·        The schemata which are holding tables, views, sequences must be distinct from the application schema.
Good:
   Routine 
myapp.foo() and table myschema.t
Bad:
   Routine 
myschema.foo() and table myschema.t
 

·        Avoid overloading procedures and functions by name across multiple schemata, unless they are in modules or PL/SQL packages
Good:
  Procedures 
hr.hire() and finance.income()
Bad:
  Procedures 
hr.add() and finance.add()

With these rules we can deploy the application into any set of schemata simply by setting the CURRENT SCHEMA and CURRENT PATH registers before executing the DDL.

The client application then uses the same CURRENT PATH to find the application.

From here it's just a small step and we are there.
We just have to come up with a naming convention to version application schema names and a means to manage which application server connects to which version.

For naming we can trail the version number to the original schema name  HR_1_1 and FINANCE_2_3. The names may not be pretty, but we will never see them in the source code.
As for managing the mapping between application servers, versions and paths the log-on procedure I introduced in my very first blog-post provides the perfect intercept point.

 

This was a lot of theory. Time to build a real example.

Example

 Of course we won't use a three tier architecture here. That would blow my modest set-up.
While you can choose any number of schemata for your application we'll also stay with one schema here.

 

First we need to install a "version management system". 
You will find the source code at the end of this blog post. 
If you store it as vms.sql in the current directory you can execute it from CLPPlus like this:

@@vms

The DB schema:

点击(此处)折叠或打开

  1. SET SCHEMA = DATA;
  2. CREATE TABLE emp(id INTEGER NOT NULL PRIMARY KEY,
  3. name VARCHAR(20),
  4. salary INTEGER);
  5. CREATE SEQUENCE empid;

The application in our version control system represented by a file:


app_v1.sql

点击(此处)折叠或打开

  1. CREATE OR REPLACE PROCEDURE hire_emp(OUT id ANCHOR TO data.emp.id,
  2. IN name ANCHOR TO data.emp.name,
  3. IN salary ANCHOR TO data.emp.salary)
  4. BEGIN SET id = NEXT VALUE FOR data.empid;
  5. INSERT INTO data.emp VALUES(id, name, salary);
  6. END;
  7. /
  8. CREATE OR REPLACE PROCEDURE fire_emp(IN name ANCHOR TO data.emp.name)
  9. BEGIN
  10. DELETE FROM data.emp WHERE fire_emp.name = name;
  11. END;
  12. /

Now we install the application and assign the application server by host name.
Note that all names are case sensitive including the host name.

Install App 1.0

点击(此处)折叠或打开

  1. SET SCHEMA = APP_1_0;
  2. SET PATH = SYSTEM PATH, APP_1_0;
  3. @@app_v1.sql
  4. CALL version.register('App 1.0', 'DATA', CURRENT_PATH);
  5. CALL server.register('srielau', 'App 1.0');
  6. COMMIT;

Run App 1.0

 Time to fire up our application in a separate CLPPlus window.
When we connect the connect procedure will match the host name with the application version.
it then sets the PATH to include "APP_1_0" and the SCHEMA to "DATA".

点击(此处)折叠或打开

  1. BEGIN
  2. DECLARE id ANCHOR TO data.emp.id;
  3. LOOP
  4. CALL hire_emp(id, 'John', 20000);
  5. COMMIT;
  6. CALL fire_emp('John');
  7. COMMIT;
  8. END LOOP;
  9. END;
  10. /

While our application is being exercised we want to do an upgrade. 

Firing employees by name seems like a recipe for disaster in case of duplicates. 
Instead we should use employee ids since they are primary keys.

Our server side application logic will now look like this:

app_v1.1.sql:

点击(此处)折叠或打开

  1. CREATE PROCEDURE hire_emp(OUT id ANCHOR TO data.emp.id,
  2. IN name ANCHOR TO data.emp.name,
  3. IN salary ANCHOR TO data.emp.salary)
  4. BEGIN
  5. SET id = NEXT VALUE FOR data.empid;
  6. INSERT INTO data.emp VALUES(id, name, salary);
  7. END;
  8. /
  9. CREATE PROCEDURE fire_emp(IN id ANCHOR TO data.emp.name)
  10. BEGIN
  11. DELETE FROM data.emp WHERE fire_emp.id = id;
  12. END;
  13. /

Install App 1.1

点击(此处)折叠或打开

  1. SET SCHEMA = APP_1_1;
  2. SET PATH = SYSTEM PATH, APP_1_1;
  3. @@app_v1.1.sql
  4. CALL version.register('App 1.1', 'DATA', CURRENT PATH);
  5. COMMIT;

To ensure that the new application version doesn't "cross talk" I have provided a small function that flags any dependencies of the application outside of an approved list:
We run that to ensure the new application version is well behaved:

点击(此处)折叠或打开

  1. SET VERSION.SOURCE = ARRAY['APP_1_1'];
  2. SET VERSION.EXCEPT = ARRAY['DATA'];
  3. SELECT COUNT(*) FROM UNNEST(VERSION.LIST_EXTERNAL_DEPENDENCIES());
  4. 1
  5. -----------
  6. 0

Production grade rolling upgrade

Normally you now perform the following steps:

1.    Drain an application server by disallowing new client connections

2.    Update the application server

3.    Update the application version for the application server's host name using the SERVER.UPDATE procedure

4.    re-integrate the application server.

 

"Laptop grade" rolling upgrade

 Since I'm doing this experiment on my laptop I only have one application server.
So I will update the version while my one CLPPlus shell is still executing App 1.0.
Then I fire up a new CLPPlus shell with a new connection to demonstrate both application versions working in parallel.

 

Upgrade application server

点击(此处)折叠或打开

  1. CALL server.update('srielau', 'App 1.1');
  2. COMMIT;

Run App 1.1

点击(此处)折叠或打开

  1. BEGIN
  2. DECLARE id ANCHOR TO data.emp.id;
  3. LOOP
  4. CALL hire_emp(id, 'Jeremy', 30000);
  5. COMMIT;
  6. CALL fire_emp(id);
  7. COMMIT;
  8. END LOOP;
  9. END;
  10. /

When I disconnect the original CLPPlus application I need to run the new client application of course.

The snapshot below shows:

1.    Top-Left the first connection running App.1.0

2.    Top-Right the second connection running App 1.1

3.    Bottom left the console used for all administration

4.    Both apps are firing away concurrently saturating the laptop.


Rolling upgrade rules

What is important to note here is that:

·        We deploy a completely new version of the application even though we just changed one of the two procedures. 

·        The deployment was 100% online. It did not affect the running of the first version beyond CPU and IO resources to compile and deploy

·        If you want to subdivide the application into multiple schemas that is fine, but keep in mind that the application of a given version will always use the same PATH.
Assume HR_1_3 invokes schema FINANCE_3_0.
 Upgrading FINANCE_3_0 to FINANCE_3_1 will only be visible to HR if you add a new version HR_1_4 invoking FINANCE_3_1 even if nothing changed in HR.

Both applications can work at the same time here. But you must design for that.
If one application, for example produces data which the other cannot ingest then there will be failures.
This implies that for a successful online "rolling upgrade" you may need to use two steps:

1.    One preparation step which merely enhances the application to cope with unexpected data.

2.    Once that preparation step is completely rolled out the extra function can be added. 
But again the new application version has to be tolerant of the previous version's effects on the database.

 

Version Management System

 I have written a simple set of routines in a VERSION module which manage the association of different PATH and SCHEMA settings to application versions. 
A second set of routines picks up these versions and associates them with host names which represent application servers. 
A connection procedure ensures the settings are applied accordingly based on a connection's host name. 
You can of course add additional properties to versions, such as isolation levels or user defined variables. 
You may also want to replace HOSTNAME with another identifier for the application server.
Lastly the code below supplies a routine which double checks that application source code is "pure". 
The goal is that there is no cross talk across versions. 
Here is a quick overview:

·        VERSION.REGISTER(VERSION VARCHAR(10), SCHEMA VARCHAR(128), PATH VARCHAR(2048))
 This procedure defines a version and sets its properties. The schema will be used by dynamic queries to resolve unqualified table, view and sequence names. The PATH will be used to resolve any routines, modules, types and variables.
 

·        VERSION.DEREGISTER(VERSION VARCHAR(10))
 This procedure removes a version. The version must not be used by any server. Note that removal of the version does not drop any objects.
 

·        VERSION.UPDATE(VERSION VARCHAR(10), SCHEMA VARCHAR(128), PATH VARCHAR(2048))
 This procedure updates an existing version to new properties. If SCHEMA is not specified or NULL it will remain unchanged. The same is true for PATH. The update will be visible to any new connection coming from an associated server.
 

·        VERSION.SOURCE SCHEMA_LIST
 This variable needs to be set to an array of schemata which make up an application version before invoking VERSION.LIST_EXTERNAL_DEPENDENCIES().
 

·        VERSION.EXCEPT SCHEMA_LIST
 This variable needs to be set to an array of schemata to be ignored by VERSION.LIST_EXTERNAL_DEPENDENCIES(). Typically this will be the list of schemata holding tables, views and sequences referenced within the application.
 

·        VERSION.LIST_EXTERNAL_DEPENDENCIES() RETURNS DEPENDENCY_LIST
 This scalar function returns and array of rows representing objects referenced by any object in a schema set in VERSION.SOURCE which is neither listed in VERSION.SOURCE nor VERSION.EXCEPT. That is the function lists any reference to an object outside of the application version. It should return an empty array. 
 Example:

点击(此处)折叠或打开

  1. · SET VERSION.SOURCE = ARRAY['HR_1_5', 'FINANCE_2_1'];
  2. · SET VERSION.EXCEPT = ARRAY['ARCHIVE', 'ACTIVE'];
  3. SELECT * FROM UNNEST(VERSION.LIST_EXTERNAL_REFERENCES());

·        SERVER.REGISTER(SERVERNAME VARCHAR(32), VERSION VARCHAR(10))
 This procedure registers an application server identified by the hostname and associates it with an application version.
 

·        SERVER.DEREGISTER(SERVERNAME VARCHAR(32))
 This procedure removes a server name for the list of known servers.
 

·        SERVER.UPDATE(SERVERNAME VARCHAR(32), VERSION VARCHAR(10))
 This procedure updates an application server identified by the hostname to a new application version. 
The next connection from this SERVERNAME will use the PATH and SCHEMA of the associated VERSION.

 

vms.sql

1.  SET SCHEMA = CONFIG; 
SET PATH = SYSTEM PATH, CONFIG; 
BEGIN 
  DECLARE CONTINUE HANDLER FOR SQLSTATE '42704' BEGIN END; 
  EXECUTE IMMEDIATE 'DROP TABLE APPSERVER'; 
  EXECUTE IMMEDIATE 'DROP TABLE APPVERSION'; 
END; 


CREATE TABLE APPSERVER(SERVERNAME VARCHAR(32) NOT NULL PRIMARY KEY, 
                       VERSION    VARCHAR(10) NOT NULL); 

CREATE TABLE APPVERSION(VERSION VARCHAR(10)   NOT NULL PRIMARY KEY, 
                        SCHEMA  VARCHAR(128)  NOT NULL, 
                        PATH    VARCHAR(2048) NOT NULL); 

ALTER TABLE APPSERVER ADD CONSTRAINT APPVERSION FOREIGN KEY (VERSION) 
                          REFERENCES APPVERSION ON DELETE RESTRICT ON UPDATE RESTRICT; 

CREATE OR REPLACE MODULE VERSION; 
/ 

CREATE OR REPLACE PUBLIC SYNONYM VERSION FOR MODULE VERSION;


ALTER MODULE VERSION PUBLISH PROCEDURE REGISTER(VERSION ANCHOR TO APPVERSION.VERSION, 
                                                SCHEMA  ANCHOR TO APPVERSION.SCHEMA, 
                                                PATH    ANCHOR TO APPVERSION.PATH); 
/ 

ALTER MODULE VERSION PUBLISH PROCEDURE DEREGISTER(VERSION ANCHOR TO APPVERSION.VERSION); 
/ 

ALTER MODULE VERSION PUBLISH PROCEDURE UPDATE(VERSION ANCHOR TO APPVERSION.VERSION, 
                                              SCHEMA  ANCHOR TO APPVERSION.SCHEMA DEFAULT (NULL), 
                                              PATH    ANCHOR TO APPVERSION.PATH   DEFAULT (NULL)); 
/ 

ALTER MODULE VERSION PUBLISH TYPE SCHEMA_LIST AS VARCHAR(128) ARRAY[]; 
/ 

ALTER MODULE VERSION PUBLISH TYPE DEPENDENCY AS ROW(FROM_SCHEMA VARCHAR(128), 
                                                    FROM_MODULE VARCHAR(128), 
                                                    FROM_OBJECT VARCHAR(128), 
                                                    FROM_TYPE   VARCHAR(20), 
                                                    TO_SCHEMA   VARCHAR(128), 
                                                    TO_MODULE   VARCHAR(128), 
                                                    TO_OBJECT   VARCHAR(128), 
                                                    TO_TYPE VARCHAR(20)); 
/ 

ALTER MODULE VERSION PUBLISH TYPE DEPENDENCY_LIST AS DEPENDENCY ARRAY[]; 
/ 

ALTER MODULE VERSION PUBLISH VARIABLE SOURCE SCHEMA_LIST; 


ALTER MODULE VERSION PUBLISH VARIABLE EXCEPT SCHEMA_LIST; 
/ 

ALTER MODULE VERSION PUBLISH FUNCTION LIST_EXTERNAL_DEPENDENCIES() RETURNS DEPENDENCY_LIST; 


CREATE OR REPLACE MODULE SERVER; 
/ 

CREATE OR REPLACE PUBLIC SYNONYM SERVER FOR MODULE SERVER; 


ALTER MODULE SERVER PUBLISH PROCEDURE REGISTER(SERVERNAME ANCHOR TO APPSERVER.SERVERNAME, 
                                               VERSION    ANCHOR TO APPSERVER.VERSION); 
/ 

ALTER MODULE SERVER PUBLISH PROCEDURE DEREGISTER(SERVERNAME ANCHOR TO APPSERVER.SERVERNAME); 
/ 

ALTER MODULE SERVER PUBLISH PROCEDURE UPDATE(SERVERNAME ANCHOR TO APPSERVER.SERVERNAME, 
                                             VERSION    ANCHOR TO APPSERVER.VERSION); 
/ 

ALTER MODULE VERSION ADD PROCEDURE REGISTER(VERSION ANCHOR TO APPVERSION.VERSION, 
                                            SCHEMA  ANCHOR TO APPVERSION.SCHEMA, 
                                            PATH    ANCHOR TO APPVERSION.PATH DEFAULT(NULL)) 
BEGIN 
  DECLARE EXIT HANDLER FOR SQLSTATE '23505' 
  BEGIN 
    DECLARE txt VARCHAR(70); 
    SET txt = 'Version: ''' || VERSION || ' already exists.'; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  INSERT INTO APPVERSION VALUES(VERSION, SCHEMA, COALESCE(PATH, 'SYSTEM PATH, "' || SCHEMA || '"')); 
END; 


ALTER MODULE VERSION ADD PROCEDURE DEREGISTER(VERSION ANCHOR TO APPVERSION.VERSION) 
BEGIN 
  DECLARE EXIT HANDLER FOR SQLSTATE '02000' 
  BEGIN 
    DECLARE txt VARCHAR(1000); 
    SET txt = 'Version: ''' || VERSION || ''' not found.'; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  DECLARE EXIT HANDLER FOR SQLSTATE '23001' 
  BEGIN 
    DECLARE txt VARCHAR(4000); 
    SELECT '''' || VERSION || ''' used by ' || '''' || LISTAGG(SERVERNAME) WITHIN GROUP (ORDER BY SERVERNAME) || '''' INTO txt 
      FROM APPSERVER WHERE VERSION = DEREGISTER.VERSION GROUP BY VERSION; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  DELETE FROM APPVERSION WHERE VERSION = DEREGISTER.VERSION; 
END; 
/ 

ALTER MODULE VERSION ADD PROCEDURE UPDATE(VERSION ANCHOR TO APPVERSION.VERSION, 
                                          SCHEMA  ANCHOR TO APPVERSION.SCHEMA  DEFAULT (NULL), 
                                          PATH    ANCHOR TO APPVERSION.PATH    DEFAULT (NULL)) 
BEGIN
  DECLARE EXIT HANDLER FOR SQLSTATE '02000'
  BEGIN
    DECLARE txt VARCHAR(70);
    SET txt = 'Version: ''' || VERSION || ''' not found.';
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt;
  END;
  UPDATE APPVERSION SET SCHEMA = COALESCE(UPDATE.SCHEMA, SCHEMA), 
                        PATH   = COALESCE(UPDATE.PATH, PATH) 
   WHERE VERSION = UPDATE.VERSION; 
END; 


ALTER MODULE SERVER ADD PROCEDURE REGISTER(SERVERNAME ANCHOR TO APPSERVER.SERVERNAME, 
                                           VERSION    ANCHOR TO APPSERVER.VERSION) 
BEGIN 
  DECLARE EXIT HANDLER FOR SQLSTATE '23505' 
  BEGIN 
    DECLARE txt VARCHAR(70); 
    SET txt = 'Server: ''' || SERVERNAME || ' already exists.'; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  DECLARE EXIT HANDLER FOR SQLSTATE '23503' 
  BEGIN
    DECLARE txt VARCHAR(70); 
    SET txt = 'Version: ''' || VERSION || ''' does not exist.'; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  INSERT INTO APPSERVER VALUES(SERVERNAME, VERSION); 
END; 
/ 

ALTER MODULE SERVER ADD PROCEDURE DEREGISTER(SERVERNAME ANCHOR TO APPSERVER.SERVERNAME) 
BEGIN 
  DECLARE EXIT HANDLER FOR SQLSTATE '02000' 
  BEGIN 
    DECLARE txt VARCHAR(70); 
    SET txt = 'Server: ''' || SERVERNAME || ''' not found.'; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  DELETE FROM APPSERVER WHERE SERVERNAME = DEREGISTER.SERVERNAME; 
END; 


ALTER MODULE SERVER ADD PROCEDURE UPDATE(SERVERNAME ANCHOR TO APPSERVER.SERVERNAME, 
                                         VERSION    ANCHOR TO APPSERVER.VERSION) 
BEGIN 
  DECLARE EXIT HANDLER FOR SQLSTATE '02000' 
  BEGIN 
    DECLARE txt VARCHAR(70); 
    SET txt = 'Server: ''' || SERVERNAME || ''' not found.'; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  DECLARE EXIT HANDLER FOR SQLSTATE '23503' 
  BEGIN 
    DECLARE txt VARCHAR(70); 
    SET txt = 'Version: ''' || VERSION || ''' does not exist.'; 
    SIGNAL SQLSTATE '78000' SET MESSAGE_TEXT = txt; 
  END; 
  UPDATE APPSERVER SET VERSION = UPDATE.VERSION WHERE SERVERNAME = UPDATE.SERVERNAME; 
END; 
/ 

SET ENVVARSUBSTITUTION OFF; 
CREATE OR REPLACE PROCEDURE CONNECT_PROC() SPECIFIC CONNECT_PROC 
BEGIN 
  DECLARE STMTTXT VARCHAR(4000); 
  DECLARE CLIENT_HOSTNAME ANCHOR TO APPSERVER.SERVERNAME; 
  SELECT DETMETRICS.CLIENT_HOSTNAME INTO CLIENT_HOSTNAME 
    FROM TABLE(MON_GET_CONNECTION_DETAILS(MON_GET_APPLICATION_HANDLE(), -1)) AS CONNMETRICS, 
         XMLTABLE (XMLNAMESPACES( DEFAULT 'http://www.ibm.com/xmlns/prod/db2/mon'), 
                   '$detmetric/db2_connection' PASSING XMLPARSE(DOCUMENT CONNMETRICS.DETAILS) 
      as "detmetric" COLUMNS "CLIENT_HOSTNAME" VARCHAR(255) PATH 'client_hostname' ) AS DETMETRICS; 
  FOR VERSION AS SELECT SCHEMA, PATH FROM APPSERVER AS S JOIN APPVERSION AS V ON S.VERSION = V.VERSION 
                  WHERE CLIENT_HOSTNAME = S.SERVERNAME 
  DO 
    SET STMTTXT = 'SET SCHEMA = ' || VERSION.SCHEMA; 
    EXECUTE IMMEDIATE STMTTXT; 
    SET STMTTXT = 'SET PATH = ' || VERSION.PATH; 
    EXECUTE IMMEDIATE STMTTXT; 
  END FOR; 
END; 

SET ENVVARSUBSTITUTION ON; 

UPDATE DB CFG USING CONNECT_PROC CONFIG.CONNECT_PROC; 

ALTER MODULE VERSION ADD FUNCTION LIST_EXTERNAL_DEPENDENCIES() RETURNS DEPENDENCY_LIST 
BEGIN 
  DECLARE I INTEGER DEFAULT 1; 
  DECLARE DEPENDENCY_LIST DEPENDENCY_LIST; 
  FOR dep AS SELECT DISTINCT * 
               FROM ( SELECT FROM_SCHEMA, FROM_MODULE, 
                             DECODE(FROM_TYPE, 'F', (SELECT ROUTINENAME FROM SYSCAT.ROUTINES 
                                                      WHERE FROM_SCHEMA = ROUTINESCHEMA 
                                                        AND (FROM_MODULE = ROUTINEMODULENAME OR (FROM_MODULE IS NULL AND ROUTINEMODULENAME IS NULL)) 
                                                        AND FROM_OBJECT = SPECIFICNAME), FROM_OBJECT) AS FROM_OBJECT, 
                             DECODE(FROM_TYPE, 'A', 'TABLE ALIAS', 'B', 'TRIGGER', 'F', 'ROUTINE', 'G', 'TABLE', 'H', 'TABLE', 'K', 'PACKAGE', 
                                               'L', 'TABLE', 'N', 'NICKNAME', 'O', 'TABLE', 'Q', 'SEQUENCE', 'R', 'TYPE', 'S', 'TABLE', 'T', 'TABLE', 
                                               'U', 'TABLE', 'V', 'VIEW', 'W', 'VIEW', 'X', 'INDEX EXT', 'Z', 'XSR OBJECT', 'm', 'MODULE', 
                                               'q', 'SEQUENCE ALIAS', 'u', 'MODULE ALIAS', 'v', 'VARIABLE', '*', 'TABLE', 'UNKNOWN: ' || FROM_TYPE) AS FROM_TYPE,
                             TO_SCHEMA, TO_MODULE, 
                             DECODE(TO_TYPE, 'F', (SELECT ROUTINENAME FROM SYSCAT.ROUTINES 
                                                    WHERE TO_SCHEMA = ROUTINESCHEMA 
                                                      AND (TO_MODULE = ROUTINEMODULENAME OR (TO_MODULE IS NULL AND ROUTINEMODULENAME IS NULL)) 
                                                      AND TO_OBJECT = SPECIFICNAME), TO_OBJECT) AS TO_OBJECT, 
                             DECODE(TO_TYPE, 'A', 'TABLE ALIAS', 'B', 'TRIGGER', 'F', 'ROUTINE', 'G', 'TABLE', 'H', 'TABLE', 'K', 'PACKAGE', 
                                             'L', 'TABLE', 'N', 'NICKNAME', 'O', 'TABLE', 'Q', 'SEQUENCE', 'R', 'TYPE', 'S', 'TABLE', 'T', 'TABLE', 
                                             'U', 'TABLE', 'V', 'VIEW', 'W', 'VIEW', 'X', 'INDEX EXT', 'Z', 'XSR OBJECT', 'm', 'MODULE', 
                                             'q', 'SEQUENCE ALIAS', 'u', 'MODULE ALIAS', 'v', 'VARIABLE', '*', 'TABLE', 'UNKNOWN: ' || TO_TYPE) AS TO_TYPE 
                        FROM (SELECT TYPESCHEMA AS FROM_SCHEMA, 
                                     TYPEMODULENAME AS FROM_MODULE, 
                                     TYPENAME AS FROM_OBJECT, 
                                     'R' AS FROM_TYPE, 
                                     BSCHEMA AS TO_SCHEMA, 
                                     BMODULENAME AS TO_MODULE, 
                                     BNAME AS TO_OBJECT, 
                                     BTYPE AS TO_TYPE 
                                FROM SYSCAT.DATATYPEDEP 
                              UNION ALL 
                              SELECT TABSCHEMA, 
                                     NULL, 
                                     TABNAME, 
                                     'T', 
                                     TYPESCHEMA, 
                                     NULL, 
                                     TYPENAME, 
                                     'R' 
                                FROM SYSCAT.COLUMNS 
                              UNION ALL 
                              SELECT TABSCHEMA, 
                                     NULL, 
                                     TABNAME, 
                                     'T', 
                                     BSCHEMA,
                                     BMODULENAME,
                                     BNAME, 
                                     BTYPE 
                                FROM SYSCAT.CONSTDEP 
                              UNION ALL 
                              SELECT MODULESCHEMA, 
                                     MODULENAME, 
                                     NULL,
                                     'u',
                                     BASE_MODULESCHEMA, 
                                     BASE_MODULENAME,
                                     NULL,
                                     'm'
                                FROM SYSCAT.MODULES 
                               WHERE MODULETYPE = 'A' 
                              UNION ALL 
                              SELECT P.PKGSCHEMA, 
                                     NULL, 
                                     P.PKGNAME, 
                                     'K', 
                                     P.BSCHEMA, 
                                     P.BMODULENAME, 
                                     P.BNAME, 
                                     P.BTYPE 
                                FROM SYSCAT.PACKAGEDEP AS P 
                                LEFT OUTER JOIN SYSCAT.ROUTINEDEP AS RD 
                                  ON P.PKGSCHEMA = RD.BSCHEMA AND P.PKGNAME = RD.BNAME AND RD.BTYPE = 'K' 
                                LEFT OUTER JOIN SYSCAT.ROUTINES AS R 
                                  ON RD.ROUTINESCHEMA = R.ROUTINESCHEMA 
                                  AND (RD.ROUTINEMODULENAME = R.ROUTINEMODULENAME OR (RD.ROUTINEMODULENAME IS NULL AND R.ROUTINEMODULENAME IS NULL)) 
                                  AND RD.SPECIFICNAME = R.SPECIFICNAME 
                               WHERE R.ORIGIN <> 'Q' OR R.ORIGIN IS NULL 
                              UNION ALL 
                              SELECT ROUTINESCHEMA, 
                                     ROUTINEMODULENAME, 
                                     SPECIFICNAME, 
                                     'F', 
                                     BSCHEMA, 
                                     BMODULENAME, 
                                     BNAME,
                                     BTYPE
                                FROM SYSCAT.ROUTINEDEP 
                                WHERE BTYPE <> 'K' 
                              UNION ALL 
                              SELECT ROUTINESCHEMA, 
                                     ROUTINEMODULENAME, 
                                     SPECIFICNAME, 
                                     'F', 
                                     TYPESCHEMA, 
                                     TYPEMODULENAME, 
                                     TYPENAME, 
                                     'R' 
                                FROM SYSCAT.ROUTINEPARMS 
                              UNION ALL 
                              SELECT TYPESCHEMA, 
                                     TYPEMODULENAME, 
                                     TYPENAME, 
                                     'R', 
                                     FIELDTYPESCHEMA, 
                                     FIELDTYPEMODULENAME,  
                                     FIELDTYPENAME, 
                                     'R' 
                                FROM SYSCAT.ROWFIELDS 
                              UNION ALL 
                              SELECT SEQSCHEMA, 
                                     NULL, 
                                     SEQNAME, 
                                     'q', 
                                     BASE_SEQSCHEMA, 
                                     NULL, 
                                     BASE_SEQNAME, 
                                     'Q' 
                                FROM SYSCAT.SEQUENCES 
                                WHERE SEQTYPE = 'A' 
                              UNION ALL 
                              (SELECT SEQSCHEMA, 
                                      NULL, 
                                      SEQNAME, 
                                      'Q', 
                                      TYPESCHEMA, 
                                      TYPEMODULENAME, 
                                      TYPENAME, 
                                      'R' 
                                 FROM SYSCAT.SEQUENCES AS S 
                                 JOIN SYSCAT.DATATYPES AS T 
                                   ON T.TYPEID = S.DATATYPEID) 
                              UNION ALL 
                              SELECT TABSCHEMA, 
                                     NULL, 
                                     TABNAME, 
                                     'T', 
                                     BSCHEMA, 
                                     BMODULENAME, 
                                     BNAME, 
                                     BTYPE
                                FROM SYSCAT.TABDEP 
                              UNION ALL 
                              SELECT TRIGSCHEMA, 
                                     NULL, 
                                     TRIGNAME, 
                                     'B', 
                                     BSCHEMA, 
                                     BMODULENAME, 
                                     BNAME, 
                                     BTYPE 
                                FROM SYSCAT.TRIGDEP 
                              UNION ALL 
                              SELECT TRIGSCHEMA, 
                                     NULL, 
                                     TRIGNAME, 
                                     'B',
                                     TABSCHEMA, 
                                     NULL,
                                     TABNAME, 
                                     'T' 
                                FROM SYSCAT.TRIGGERS 
                              UNION ALL 
                              SELECT VARSCHEMA, 
                                     VARMODULENAME, 
                                     VARNAME, 
                                     'v', 
                                     BSCHEMA, 
                                     BMODULENAME, 
                                     BNAME, 
                                     BTYPE 
                                FROM SYSCAT.VARIABLEDEP 
                              UNION ALL 
                              SELECT VARSCHEMA, 
                                     VARMODULENAME, 
                                     VARNAME, 
                                     'v', 
                                     TYPESCHEMA, 
                                     TYPEMODULENAME, 
                                     TYPENAME, 
                                     'R' 
                                FROM SYSCAT.VARIABLES 
                              UNION ALL 
                              SELECT OBJECTSCHEMA,  
                                     NULL, 
                                     OBJECTNAME, 
                                     'Z', 
                                     BSCHEMA, 
                                     BMODULENAME, 
                                     BNAME, 
                                     BTYPE 
                                FROM SYSCAT.XSROBJECTDEP) AS DEP, 
                             UNNEST(SOURCE) AS S(SCHEMA), 
                             UNNEST(EXCEPT) AS E(SCHEMA) 
                         WHERE FROM_SCHEMA = S.SCHEMA 
                           AND TO_SCHEMA <> E.SCHEMA AND TO_SCHEMA <> S.SCHEMA AND TO_SCHEMA NOT LIKE 'SYS%') 
                     ORDER BY FROM_SCHEMA, FROM_MODULE, FROM_TYPE, FROM_OBJECT, TO_SCHEMA, TO_MODULE, TO_TYPE, TO_OBJECT 
  DO 
    SET DEPENDENCY_LIST[I] = (FROM_SCHEMA, FROM_MODULE, FROM_OBJECT, FROM_TYPE, TO_SCHEMA, TO_MODULE, TO_OBJECT, TO_TYPE); 
    SET I = I + 1; 
  END FOR; 
  RETURN DEPENDENCY_LIST; 
END; 
/

 

阅读(1605) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~