Rem
Rem $Header: rdbms/admin/catfusrg.sql /st_rdbms_12.2.0.1.0dbbp/5 2017/07/28 16:38:55 saratho Exp $
Rem
Rem catfusrg.sql
Rem
Rem Copyright (c) 2002, 2017, Oracle and/or its affiliates. 
Rem All rights reserved.
Rem
Rem    NAME
Rem      catfusrg.sql - Catalog registration file for DB Feature Usage clients
Rem
Rem    DESCRIPTION
Rem      Clients of the DB Feature Usage infrastructure can register their
Rem      features and high water marks in this file.
Rem
Rem      It is important to register the following 8 features:
Rem        RAC, Partitioning, OLAP, Data Mining, Oracle Label Security,
Rem        Oracle Advanced Security, Oracle Programmer(?), Oracle Spatial.
Rem
Rem    NOTES
Rem      The tracking for the following advisors is currently disabled:
Rem         Tablespace Advisor - smuthuli
Rem         SGA/Memory Advisor - tlahiri
Rem
Rem
Rem BEGIN SQL_FILE_METADATA
Rem SQL_SOURCE_FILE: rdbms/admin/catfusrg.sql
Rem SQL_SHIPPED_FILE: rdbms/admin/catfusrg.sql
Rem SQL_PHASE: CATFUSRG
Rem SQL_STARTUP_MODE: NORMAL
Rem SQL_IGNORABLE_ERRORS: NONE
Rem SQL_CALLING_FILE: rdbms/admin/catpprvt.sql
Rem END SQL_FILE_METADATA
Rem
Rem    MODIFIED   (MM/DD/YY)
Rem    apfwkr      03/28/17 - Backport saratho_shdlcns from main
Rem    saratho     02/07/17 - add high-watermark for number of primary shards
Rem    dcolello    10/18/16 - Add high water mark for shards
Rem    amylavar    09/02/16 - Feature tracking for In-Memory Distribute 
Rem                           For Service
Rem    aumishra    09/01/16 - IME feature tracking
Rem    amylavar    08/29/16 - Feature tracking for join groups
Rem    dmaniyan    07/20/16 - XbranchMerge dmaniyan_ftnewtrn from main
Rem    prthiaga    06/22/16 - Bug 23572368: Sql Injection vulnerability 
Rem                           in DBMS_FEATURE_JSON 
Rem    dmaniyan    05/19/16 - Add feature tracking for sharding
Rem    schakkap    05/11/16 - #(22652097): _optimizer_adaptive_plans =>
Rem                           optimizer_adaptive_plans
Rem    prthiaga    05/05/16 - Bug 23491595: Add 12.2 JSON features & 
Rem                           add XDB non-index table/view stats
Rem    msingal     04/17/16 - Lrg/18936592 statistics changed for HCC in cloud
Rem    anujgg      03/18/16 - Bug 20907094: Feature usage HCC conventional load
Rem    jtomass     03/03/16 - Bug 20898668: density_factor to dbms_feature_asm
Rem    msakayed    03/01/16 - RTI #16412598: fix ext table usage
Rem    jtomass     02/29/16 - 22911644: add ACFS Encryption support on ASM
Rem    jftorres    02/11/16 - #(22449281): add new dba_sql_plan_baselines
Rem                           origins
Rem    qyu         02/01/16 - #22642521: track xmlindex usage
Rem    aksshah     01/29/16 - Bug 22620507: PDB I/O Rate Limits feature usage
Rem    kdnguyen    01/15/16 - Bug 22542622: Feat Tracking for IMC FS 
Rem    jtomass     01/05/16 - 22498132: ACFS Snapshot support
Rem    hbaer       01/04/16 - tracking of json partitioning
Rem    rmacnico    12/08/15 - Rename Cellcache to Cellmemory
Rem    lzheng      11/19/15 - procedural replication feature
Rem    soumadat    10/20/15 - Bug 21932167 : HCC ARCHIVE HIGH Not Showing In
Rem                           Feature Tracking
Rem    alui        10/06/15 - Bug 20674123: fix feature usage for QoS
Rem    rmacnico    10/01/15 - bug 21931792: cellcache as IMC feature
Rem    molagapp    09/17/15 - bug 21848513
Rem    jtomass     08/25/15 - bug 21693658: ACFS support on ASM with #FS
Rem                           Management
Rem    molagapp    08/17/15 - lrg 14294933
Rem    yinlu       08/05/15 - bug 21562376 exclude securefile compression
Rem                           for xml search index D table
Rem    pkapil      07/03/15 - Bug 21248059 : Correcting feature_boolean for
Rem                           Hybrid Columnar Compression case
Rem    carlorti    06/05/15 - Bug 21059266: Rewrote query in terms of Data
Rem                           Dictionary base tables
Rem    hbaer       05/27/15 - bug 21151384, overload zonemap tracking with
Rem                           attribute clustering
Rem    jtomass     05/21/15 - 20877328: FT for ASM Filter Driver
Rem    hbaer       05/08/15 - bug 21052077: enhanced partition usage tracking
Rem    mfallen     05/01/15 - bug 20746293: long identifiers
Rem    prshanth    04/23/15 - Bug 20718081 Rename Oracle Pluggable Databases to
Rem                           Oracle Multitenant
Rem    dblui       03/27/15 - Bug 20347656 exclude compression advisor tmp idxs
Rem    msingal     02/05/15 - Bug/19665921 add support for HCC over cloud DB
Rem                           installations
Rem    jtomass     01/08/15 - 20312104: FT for ThinProvisioning & FlexASM, add
Rem                           high water mark for FlexASM
Rem    jstraub     12/08/14 - Bug 20147092: Fix logic for APEX feature usage
Rem    hbaer       12/05/14 - fix bug 12573239, additional filtering for
Rem                           internal partitioning use cases
Rem    snechand    11/24/14 - Bug 16532277 Excluding schemas - SYSMAN_MDS,
Rem                           SYSMAN_OPSS, SYSMAN_BIPLATFORM
Rem    zhimchen    10/30/14 - Fix bug#12970736, exclude SYSEXT and UD1
Rem    yhu         10/12/14 - LRG# 13361756: deal with MDSYS lob, from 
Rem                           zhimchen_lrg-13361756
Rem    rjanders    08/13/14 - LRG# 12953957, qualify dba_registry queries
Rem    suelee      08/07/14 - Bug 19351050: fix usage for Resource Manager on
Rem                           CDBs
Rem    molagapp    08/06/14 - rename BA to RA
Rem    amylavar    07/29/14 - Bug 19308780: Dont enable tracking for inmemory
Rem                           if inmemory_size = 0
Rem    zqiu        07/11/14 - PARTIAL state added to v$KEY_VECTOR
Rem    jinjche     06/09/14 - Add Backup Appliance NZDL feature tracking
Rem    lexuxu      06/04/14 - Backport lexuxu_bug-18756350 from main
Rem    apfwkr      05/26/14 - Backport prthiaga_bug-18805145 from main
Rem    prthiaga    05/21/14 - Bug 18805145: JSON feature usage
Rem    apfwkr      05/01/14 - Backport pmojjada_lrg-11571892 from main
Rem    prthiaga    05/21/14 - Bug 18805145: JSON feature usage
Rem    lexuxu      04/30/14 - DBLRA feature usage
Rem    pmojjada    04/23/14 - LRG# 11571892, Bug# 18645104 : Exclude SYSTEM
Rem                           schema from VPD feature usage
Rem    akociube    02/26/14 - DBMS_FEATURE_IMA support
Rem    kdnguyen    02/20/14 - Change ADVANCED to Advanced for Index Compression
Rem    hbaer       02/14/14 - add zonemap feature tracking
Rem    yanchuan    01/13/14 - lrg 9586866: add DVSYS in the NOT IN USER list
Rem    surman      12/29/13 - 13922626: Update SQL metadata
Rem    chinkris    12/12/13 - Rename in-memory views
Rem    hbaer       12/18/12 - bug 14666795, plus partition collection 
Rem                           enhancements for 12.1.0.2 and other bug fixes
Rem    jiayan      12/09/13 - lrg 9549959: fix feature_boolean value for 
Rem                           Automatic Reoptimization and Adaptive Plans
Rem    amylavar    12/05/13 - Feature tracking for IOTs
Rem    zhimchen    11/17/13 - Fix lrg#10052943: exclude user FLOWS_FILES for
Rem                           XDB feature usage
Rem    yxie        11/08/13 - get emx usage info for all instances
Rem    amylavar    11/01/13 - Feature tracking for IMC
Rem    youyang     10/30/13 - lrg9922394: change pa feature usage cursor
Rem    amylavar    10/22/13 - Change tracking for ROW STORE COMPRESS ADVANCED
Rem    yxie        07/30/13 - add feature usage for em express
Rem    smuthuli    07/22/13 - Fix heatmap feature usage query
Rem    hsivrama    07/15/13 - Bug 15854162 : Feature tracking for adaptive
Rem                           plans(project 31793)
Rem    gkulkarn    06/13/13 - bug-16874207: OGG ALLKEY SUPLOG feature tracking
Rem    esmendoz    05/22/13 - BUG 16564880 - error on gateways usage.
Rem    kamotwan    05/20/13 - 16830636 : Added feature tracking for GGSESSION
Rem                           and DELCASCADEHINT in goldengate
Rem    kamotwan    05/20/13 - Bug 16874207 : Added feature tracking for
Rem                           DBENCRYPTION in goldengate 
Rem    pmojjada    03/29/13 - Bug# 16532243 & 16555784: Exclude MDSYS and
Rem                           SYSMAN_MDS from showing in feature usage.
Rem    suelee      03/28/13 - Bug 16557838: Resource Manager on CDBs
Rem    dagagne     03/27/13 - BUG 16563848: ADG not required for global
Rem                           sequences
Rem    smuthuli    02/24/13 - XbranchMerge smuthuli_bug-14803927 from
Rem                           st_rdbms_12.1.0.1
Rem    smuthuli    12/27/12 - Track Heatmap
Rem    yhuang      12/13/12 - XbranchMerge yhuang_idmr_feature_tracking from
Rem                           main
Rem    surman      12/10/12 - XbranchMerge surman_bug-12876907 from main
Rem    banand      12/06/12 - bug 15844932: 12c RMAN features tracking
Rem    ratiwary    11/28/12 - Add In-Database Hadoop Feature tracking
Rem    snadhika    11/28/12 - lrg 7256825
Rem    panzho      11/27/12 - lrg-7270756
Rem    jiayan      11/13/12 - Add SQL Plan Directives and Concurrent Stats
Rem                           Gathering
Rem    sasounda    11/09/12 - 15841236: add feature compress online
Rem    chinkris    11/08/12 - Bug-15846613: Add HCC row level locking feature
Rem    talliu      11/05/12 - Adding tracking for pluggable database
Rem    jerrede     11/05/12 - Use Common Routine to calculate cells
Rem    xbarr       10/05/12 - Add feature usage support for Data Mining
Rem    smcgee      10/12/12 - Rename far sync standby to far sync instance
Rem    xihua       10/12/12 - 12c Index Compression factoring change
Rem    vpantele    09/28/12 - bug 14672752: add online move datafile tracking
Rem    fuli        09/24/12 - bug 13966908
Rem    jkaloger    09/20/12 - Feature tracking for Data Pump Full Transportable
Rem    paestrad    09/12/12 - Adding information from NEW JOB TYPES project for
Rem                           DBMS_FEATURE_JOB_SCHEDULER
Rem    esmendoz    09/11/12 - adding DBMS_FEATURE_HS to query gateway features.
Rem    teclee      09/06/12 - Add tracking for HCC row level locking
Rem    amunnoli    08/31/12 - Bug 14568283:Intuitive Audit Feature names
Rem    panzho      08/22/12 - bug 14514644
Rem    liding      08/16/12 - bug 14500587: tracking for out-of-place refresh
Rem    minwei      08/14/12 - Add tracking for online redefinition
Rem    kmohan      08/10/12 - Track Database Resident Connection Pooling (DRCP)
Rem                           usage.
Rem    hbaer       08/01/12 - fix bug 14369338, plus adjustments for 12c
Rem    ysarig      07/23/12 - Remove DB control
Rem    sslim       07/11/12 - Bug 14158354: Add tracking of DBMS_ROLLING
Rem    prgaharw    07/05/12 - 13895208 - Proj-30966: ILM feature tracking
Rem    kdnguyen    06/05/12 - register oltp compressed index feature
Rem    sdball      05/24/12 - Exclude GSM schema from feature usage checks
Rem    dgraj       05/21/12 - Bug 13887494: Add feature usage tracking for TSDP
Rem    ddas        05/16/12 - add evolve advisor origin to SPM tracking
Rem    jheng       05/14/12 - Bug 14050142: add privilege capture usage tracking
Rem    cqi         04/17/12 - Add feature tracking for RACONENODE
Rem    aramappa    04/10/12 - bug 13921755:Improve label security feature usage
Rem    dagagne     04/16/12 - Add more Data Guard features
Rem    lzheng      04/13/12 - bug 13917054: Enhance Stream/XStream/GoldenGate
Rem                           feature tracking
Rem    snadhika    04/12/12 - Add feature tracking for Real Application Security
Rem    sdball      04/05/12 - Add GSM usage checking
Rem    siravic     03/30/12 - Bug# 13888340: Data redaction feature 
Rem                           usage tracking
Rem    surman      03/27/12 - 13615447: Add SQL patching tags
Rem    ssonawan    03/26/12 - Bug 8518997: Improve 'Audit Options',
Rem                           add 'Fine Grained Audit' & 'Unified Audit'
Rem    alui        02/21/12 - enhance QoSM to track measure only mode
Rem    kamotwan    12/22/11 - Add feature tracking for GoldenGate
Rem    qyu         11/22/11 - lrg 5834576 fix 
Rem    alhollow    10/28/11 - Pillar/ ZFS support
Rem    spamu       09/13/11 - Add feature tracking for Advanced Network
Rem                           Compression
Rem    alhollow    08/01/11 - Add HCC stats
Rem    rjanders    09/30/11 - #11707598: differentiate "spatial" from "locator"
Rem    srtata      08/29/11 - renamed OLS policy table
Rem    ddas        08/22/11 - enhance SPM feature usage tracking
Rem    alui        06/30/11 - Add QoS Management tracking
Rem    vraja       06/03/11 - add DBFS feature-usage checks
Rem    jkaloger    01/04/11 - PROJ:27450 - Update Utilities tracking for 12g.
Rem    hosu        05/07/10 - register incremental maintenance
Rem    mtozawa     11/17/10 - bug 10280821: add DMU feature usage tracking
Rem    jjlee       07/19/10 - bug 9903993: remove compat check from ADG query
Rem    fsanchez    05/06/10 - bug 9689580
Rem    bpwang      05/30/10 - LRG 4646114: Handle GoldenGate Capture change
Rem    rmao        05/19/10 - change to dba_capture/apply.purpose
Rem    evoss       05/07/10 - better dbms_scheduler queries
Rem    bpwang      04/23/10 - Add XStream In, XStream Out, GoldenGate
Rem    jheng       04/06/10 - Fix bug 9256867: use dba_policies for OLS
Rem    yemeng      03/03/10 - change the name for diag/tuning pack
Rem    hbaer       02/26/10 - bug 9404352: fix Partition feature usage tracking
Rem                           for 11gR2
Rem    ysarig      02/10/10 - fixing bug 8940905
Rem    bvaranas    02/03/10 - 8803049: Feature usage tracking for deferred
Rem                           segment creation
Rem    pbelknap    09/23/09 - correct advisor fusg for reports
Rem    suelee      08/26/09 - Add tracking for dNFS and instance caging
Rem    fsanchez    07/22/09 - XbranchMerge fsanchez_bug-8657554 from
Rem                           st_rdbms_11.2.0.1.0
Rem    baleti      06/29/09 - split SF into user and system tracking
Rem    jcarey      06/30/09 - better olap query
Rem    qyu         06/30/09 - fix bug 8643026
Rem    pbelknap    06/22/09 - #8618452 - feature usage for reports
Rem    bsthanik    06/30/09 - 8643032: Exclude user APEX for XDB feature usage
Rem                           reporting
Rem    pbelknap    06/22/09 - #8618452 - feature usage for reports
Rem    cchui       06/17/09 - Register Database Vault
Rem    baleti      06/16/09 - Add SF compression, encryption and deduplication
Rem                           feature tracking
Rem    etucker     06/19/09 - break up OJVM stats
Rem    ychan       06/18/09 - Fix bug 8607966
Rem    xbarr       06/18/09 - Bug 8610599: update feature usage tracking for Data Mining option
Rem    mfallen     06/16/09 - add awr reports feature
Rem    mhho        06/17/09 - update ASO tracking queries
Rem    baleti      06/16/09 - Add SF compression, encryption and deduplication
Rem                           feature tracking
Rem    bsthanik    06/09/09 - Exclude user OE for XDB feature tracking
Rem    fsanchez    06/05/09 - change compression name DEFAULT -> BASIC
Rem    sugpande    06/24/09 - Change xml in HWM for exadata to a simple sql
Rem    sugpande    06/19/09 - Change HWM name
Rem    suelee      06/03/09 - Bug 8544790: gather feature info for Resource
Rem                           Manager
Rem    spsundar    05/29/09 - bug 8540405
Rem    alexsanc    05/26/09 - bug 7012409
Rem    weizhang    05/22/09 - bug 7026782: segment advisor (user)
Rem    sravada     05/22/09 - fix Spatial usage tracking so that only Spatial
Rem                           usage is counted and Locator is not counted
Rem    wbattist    05/26/09 - bug 7009390 - properly ignore XDB service and any
Rem                           streams services for feature usage
Rem    sravada     05/22/09 - fix Spatial usage tracking so that only Spatial
Rem                           usage is counted and Locator is not counted
Rem    qyu         05/14/09 - bug 7012411 and 7012412
Rem    vmarwah     05/14/09 - feature tracking for hybrid columnar compression
Rem    bsthanik    05/13/09 - 7009367: report xdb usage correctly
Rem    vgokhale    05/12/09 - Add feature usage for server flash cache
Rem    fsanchez    04/06/09 - bug 8411943
Rem    mkeihl      03/12/09 - Bug 5074668: active_instance_count is deprecated
Rem    ataracha    01/29/09 - enhance dbms_feature_xdb
Rem    etucker     12/02/08 - add javavm registration
Rem    sugpande    01/06/09 - Add db feature usage and high water mark for
Rem                           exadata
Rem    ysarig      09/25/08 - Fix bug# 7425224
Rem    fsanchez    08/28/08 - bug 6623413
Rem    jberesni    07/10/08 - add 7-day dbtime and dbcpu to AWR feature_info
Rem    lgalanis    06/25/08 - fix date logic in capture and replay procs
Rem    msakayed    04/17/08 - compression/encryption feature tracking for 11.2
Rem    ssamaran    02/13/08 - Add RMAN tracking
Rem    jiashi      03/19/08 - Remove dest_id from ADG RTQ feature tracking
Rem    jiashi      02/28/08 - Update ADG RTQ feature name
Rem    achoi       01/22/08 - track Edition feature
Rem    dolin       01/15/08 - Update Multimedia to Oracle Multimedia, DICOM to
Rem                           Oracle Multimedia DICOM
Rem    rkgautam    08/17/07 - bug-5475037 Using the feature 
Rem                         - Externally authenticated users 
Rem    evoss       06/08/07 - Add scheduler feature usage support
Rem    mlfeng      05/22/07 - more information for tablespaces
Rem    siroych     05/22/07 - fix errors in Auto SGA/MEM procedures
Rem    sdizdar     05/13/07 - bug-6040046: add tracking for backup compression
Rem    soye        04/25/07 - #5599389: add failgroup info to ASM tracking
Rem    dolin       04/10/07 - Update feature usage for interMedia->Multimedia
Rem                         - interMedia DICOM->DICOM
Rem    rmir        11/19/06 - Bug 5570546, VPD feature usage query correction
Rem    gstredie    02/19/07 - Add tracking for heap compression
Rem    siroych     04/13/07 - bug 5868103: fix feature usage for ASMM
Rem    vakrishn    02/27/07 - Flashback Data Archive feature usage
Rem    mlfeng      03/28/07 - add feature usage capture for baselines
Rem    veeve       02/26/07 - add db usage for workload capture and replay
Rem    hchatter    03/09/07 - 5868117: correctly report IPQ usage
Rem    amadan      02/19/07 - bug 5570961: fix db feature usage for stream
Rem    pbelknap    02/24/07 - lrg 2875206 - add nvl to asta query
Rem    ychan       02/13/07 - Support em feature usage
Rem    jsoule      01/25/07 - add db usage for metric baselines
Rem    pbelknap    02/12/07 - add projected db time saved for auto sta
Rem    ilistvin    01/24/07 - add autotask clients
Rem    weizhang    01/29/07 - add tracking for auto segadv and shrink
Rem    pbelknap    01/12/07 - split STS usage into system and user
Rem    sackulka    01/22/07 - Usage tracking for securefiles
Rem    kyagoub     12/28/06 - add db usage for SQL replay advisor
Rem    suelee      01/02/07 - Disable IORM
Rem    ilistvin    11/15/06 - move procedure invokations to execsvrm.sql
Rem    mannamal    12/21/06 - Fix the problems caused by merge (lrg 2750790)
Rem    shsong      11/01/06 - Add tracking for recovery layer
Rem    achaudhr    12/05/06 - Result_Cache: Add feature tracking
Rem    yohu        12/06/06 - use sysstat instead of inststat 
Rem    sltam       11/13/06 - count service with goal = null
Rem    sltam       10/30/06 - dbms_feature_services - Handle if db_domain 
Rem                           is not set
Rem    rvenkate    10/25/06 - enhance service usage tracking
Rem    mannamal    10/31/06 - Add tracking for semantics/RDF
Rem    yohu        11/21/06 - add tracking for XA/RAC (clusterwide global txn)
Rem    ddas        10/27/06 - rename OPM to SPM
Rem    msakayed    10/17/06 - add tracking for loader/datapump/metadata api
Rem    jdavison    10/19/06 - Add more Data Guard feature info
Rem    mbrey       10/09/06 - add support for CDC
Rem    sbodagal    10/03/06 - add support for Materialized Views (user)
Rem    soye        10/05/06 - #5582564: add more ASM usage tracking
Rem    jdavison    10/10/06 - Modify Data Guard features
Rem    kigoyal     10/11/06 - add cache features
Rem    suelee      10/02/06 - Track IORM
Rem    molagapp    10/06/06 - track usage of BMR and rollforward
Rem    dolin       10/10/06 - add interMedia feature
Rem    rmir        09/27/06 - 5566035,add Transparent Database Encryption
Rem                           feature
Rem    jstraub     10/04/06 - Changed registering of Application Express per
Rem                           mfeng comments
Rem    jstraub     10/02/06 - add Application Express
Rem    bspeckha    09/26/06 - add workspace manager feature
Rem    ayalaman    09/26/06 - tracking for RUL and EXF components
Rem    oshiowat    09/18/06 - bug5385695 - add oracle text
Rem    amozes      09/25/06 - add support for data mining
Rem    molagapp    09/25/06 - add data repair advisor
Rem    ddas        09/07/06 - register optimizer plan management feature
Rem    xbarr       06/06/06 - remove DMSYS entries for Data Mining 
Rem    qyu         05/11/06 - add more xml in xdb 
Rem    mrafiq      03/22/06 - number of resources changed 
Rem    mlfeng      01/18/06 - add flag to USER_TABLES highwater mark for 
Rem                           recycle bin 
Rem    vkapoor     12/23/05 - Number of resources changed 
Rem    qyu         12/15/05 - add xml, lob, object and extensibility feature 
Rem    mrafiq      08/18/05 - adding XDB feature 
Rem    swerthei    08/15/05 - add backup encryption
Rem    swerthei    08/15/05 - add Oracle Secure Backup 
Rem    yuli        07/21/05 - remove standby unprotected mode feature 
Rem    mlfeng      05/16/05 - upper to values 
Rem    mlfeng      05/09/05 - fix spatial query 
Rem    rpang       02/18/05 - 4148642: long report in dbms_feature_plsql_native
Rem    pokumar     08/11/04 - change query for Dynamic SGA feature usage
Rem    fayang      08/02/04 - add CSSCAN features usage detection 
Rem    bpwang      08/03/04 - lrg 1726108:  disregard wmsys in streams query
Rem    jywang      08/02/04 - Add temp tbs into DBFUS_LOCALLY_MANAGED_USER_STR 
Rem    ckearney    07/29/04 - fix Olap Cube SQL to match how it is populated 
Rem    pokumar     05/20/04 - change query for Dynamic SGA feature usage 
Rem    veeve       04/28/04 - Populate CLOB column for ADDM
Rem    mrhodes     02/25/04 - OSM->ASM 
Rem    mlfeng      01/14/04 - tune high water mark queries 
Rem    mkeihl      11/10/03 - Bug 3238893: Fix RAC feature usage tracking 
Rem    mlfeng      11/05/03 - add tracking for SQL Tuning Set, AWR 
Rem    gmulagun    10/28/03 - improve performance of audit query 
Rem    mlfeng      10/30/03 - add ASM tracking, services HWM
Rem    mlfeng      10/30/03 - track system/user
Rem    jwlee       10/16/03 - add flashback database feature 
Rem    ckearney    10/08/03 - fix owner of DBA_OLAP2_CUBES
Rem    hbaer       09/30/03 - lrg1578529 
Rem    esoyleme    09/22/03 - change analytic workspace query 
Rem    mlfeng      09/05/03 - change HDM -> ADDM, OMF logic 
Rem    rpang       08/15/03 - Tune SQL for PL/SQL NCOMP sampling
Rem    bpwang      08/08/03 - bug 2993461:  updating streams query
Rem    hbaer       07/31/03 - fix bug 3074607 
Rem    rsahani     07/29/03 - enable SQL TUNING ADVISOR
Rem    myechuri    07/10/03 - change file mapping query
Rem    gngai       07/15/03 - seed db register
Rem    mlfeng      07/02/03 - change high water mark statistics logic
Rem    sbalaram    06/19/03 - Bug 2993464: fix usage query for adv. replication
Rem    tbosman     05/13/03 - add cpu count tracking
Rem    rpang       05/21/03 - Fixed PL/SQL native compilation
Rem    mlfeng      05/02/03 - change unused aux_count from 0 to null
Rem    xcao        05/22/03 - modify Messaging Gateway usage registration
Rem    aime        04/25/03 - aime_going_to_main
Rem    rjanders    03/11/03 - Correct 'standby archival' query for beta1
Rem    mpoladia    03/11/03 - Change audit options query
Rem    dwildfog    03/10/03 - Enable tracking for several advisors
Rem    swerthei    03/07/03 - fix RMAN usage queries
Rem    hbaer       03/07/03 - adjust dbms_feature_part for cdc tables
Rem    mlfeng      02/20/03 - Change name of oracle label security
Rem    wyang       03/06/03 - enable tracking undo advisor
Rem    mlfeng      02/07/03 - Add PL/SQL native and interpreted tracking
Rem    mlfeng      01/31/03 - Add test flag to test features and hwm
Rem    mlfeng      01/23/03 - Updating Feature Names and Descriptions
Rem    mlfeng      01/13/03 - DB Feature Usage
Rem    mlfeng      01/08/03 - Comments for registering DB Features and HWM
Rem    mlfeng      01/08/03 - Added Partitioning procedure and test procs
Rem    mlfeng      11/07/02 - Registering more features
Rem    mlfeng      11/05/02 - Created
Rem

@@?/rdbms/admin/sqlsessstart.sql


  -- ******************************************************** 
  --  To register a database feature, the following procedure 
  --  is used (A more detailed description of the input
  --  parameters is given in the dbmsfus.sql file):
  --
  --    procedure REGISTER_DB_FEATURE 
  --       ( feature_name           IN VARCHAR2,
  --         install_check_method   IN INTEGER,
  --         install_check_logic    IN VARCHAR2,
  --         usage_detection_method IN INTEGER,
  --         usage_detection_logic  IN VARCHAR2,
  --         feature_description    IN VARCHAR2);
  --
  --  Input arguments:
  --   feature_name           - name of feature
  --   install_check_method   - how to check if the feature is installed.
  --                            currently support the values:
  --                            DBU_INST_ALWAYS_INSTALLED, DBU_INST_OBJECT
  --   install_check_logic    - logic used to check feature installation.
  --                            if method is DBU_INST_ALWAYS_INSTALLED, 
  --                            this argument will take the NULL value.
  --                            if method is DBU_INST_OBJECT, this argument 
  --                            will take the owner and object name for
  --                            an object that must exist if the feature has 
  --                            been installed.
  --   usage_detection_method - how to capture the feature usage, either
  --                            DBU_DETECT_BY_SQL, DBU_DETECT_BY_PROCEDURE, 
  --                            DBU_DETECT_NULL
  --   usage_detection_logic  - logic used to detect usage.  
  --                            If method is DBU_DETECT_BY_SQL, logic will 
  --                            SQL statement used to detect usage.
  --                            If method is DBU_DETECT_BY_PROCEDURE, logic
  --                            will be PL/SQL procedure used to detect usage.
  --                            If method is DBU_DETECT_NULL, this argument
  --                            will not be used. Usage is not tracked.
  --   feature_description    - Description of feature
  --
  --
  --  Examples:
  --
  --  To register the Label Security feature (an install check is not 
  --  required and the detection method is to use a PL/SQL procedure), 
  --  the following is used:
  -- 
  --  declare 
  --   DBFUS_LABEL_SECURITY_PROC CONSTANT VARCHAR2(1000) :=
  --    'dbms_feature_label_security';
  --
  --  begin
  --   dbms_feature_usage.register_db_feature
  --     ('Label Security',
  --      dbms_feature_usage.DBU_INST_ALWAYS_INSTALLED, 
  --      NULL,
  --      dbms_feature_usage.DBU_DETECT_BY_PROCEDURE,
  --      DBFUS_LABEL_SECURITY_PROC,
  --      'Oracle Label Security is being used');
  --  end; 
  --
  --  To register the Partitioning feature (an install check is not
  --  required and the detection method is to use a PL/SQL procedure),
  --  the following is used:
  --
  --  declare
  --   DBFUS_PARTN_USER_PROC CONSTANT VARCHAR2(1000) :=
  --       'DBMS_FEATURE_PARTITION_USER';
  --
  --  begin
  --   dbms_feature_usage.register_db_feature
  --      ('Partitioning (user)',
  --       dbms_feature_usage.DBU_INST_ALWAYS_INSTALLED,
  --       NULL,
  --       dbms_feature_usage.DBU_DETECT_BY_PROCEDURE,
  --       DBFUS_PARTN_USER_PROC,
  --       'Partitioning');
  --  end;
  -- ******************************************************** 


  -- ******************************************************** 
  --  To register a high water mark, the following procedure 
  --  is used (A more detailed description of the input
  --  parameters is given in the dbmsfus.sql file):
  -- 
  --    procedure REGISTER_HIGH_WATER_MARK
  --       ( hwm_name   IN VARCHAR2,
  --         hwm_method IN INTEGER,
  --         hwm_logic  IN VARCHAR2,
  --         hwm_desc   IN VARCHAR2);
  --
  --  Input arguments:
  --   hwm_name   - name of high water mark
  --   hwm_method - how to compute the high water mark, either
  --                DBU_HWM_BY_SQL, DBU_HWM_BY_PROCEDURE, or DBU_HWM_NULL
  --   hwm_logic  - logic used for high water mark.
  --                If method is DBU_HWM_BY_SQL, this argument will be SQL 
  --                statement used to compute hwm.
  --                If method is DBU_HWM_BY_PROCEDURE, this argument will be
  --                PL/SQL procedure used to compute hwm.
  --                If method is DBU_HWM_NULL, this argument will not be
  --                used. The high water mark will not be tracked.
  --   hwm_desc   - Description of high water mark
  --
  -- 
  --  Example:
  --
  --  To register the number of user tables (method is SQL), the 
  --  following is used:
  --
  --  declare
  --   HWM_USER_TABLES_STR CONST VARCHAR2(1000) :=
  --       'select count(*) from dba_tables where owner not in ' ||
  --       '(''SYS'', ''SYSTEM'')';
  --
  --  begin
  --   dbms_feature_usage.register_high_water_mark
  --      ('USER_TABLES',
  --       dbms_feature_usage.DBU_HWM_BY_SQL,
  --       HWM_USER_TABLES_STR,
  --       'Number of User Tables');
  --  end;
  -- ******************************************************** 




Rem *********************************************************
Rem Procedures used by the Features to Track Usage
Rem *********************************************************

/***************************************************************
 * DBMS_FEATURE_BA_OWNER
 *  The procedure to detect usage for Recovery Appliance
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_ba_owner
      (feature_boolean OUT number, 
       aux_count       OUT number, 
       info            OUT clob)
AS
   owner     VARCHAR2(100);

BEGIN

   EXECUTE IMMEDIATE 
   'BEGIN ' ||
   'SELECT dbms_rai_owner INTO :owner FROM DUAL; ' ||
   'EXCEPTION ' ||
   '   WHEN OTHERS THEN ' ||
   '   :owner := NULL; ' ||
   'END;' USING OUT owner;

   IF (owner IS NOT NULL) THEN
      feature_boolean := 1;
   ELSE
      feature_boolean := 0;
   END IF;

   aux_count := NULL;
   info := NULL;

END dbms_feature_ba_owner;
/
show errors

/***************************************************************
 * DBMS_FEATURE_ASM
 *  The procedure to detect usage for ASM
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_asm
      (is_used OUT number, total_diskgroup_size OUT number, summary OUT clob)
AS
   redundancy_type    clob;
   max_diskgroup_size number;
   min_diskgroup_size number;
   num_disk           number;
   num_diskgroup      number;
   min_disk_size      number;
   max_disk_size      number;
   num_failgroup      number;
   min_failgroup_size number;
   max_failgroup_size number;
   au_size            clob;
   density_factor     number;

BEGIN
  -- initialize
  redundancy_type      := 'Redundancy';
  max_diskgroup_size   := NULL;
  min_diskgroup_size   := NULL;
  total_diskgroup_size := NULL;
  num_disk             := NULL;
  num_diskgroup        := NULL;
  min_disk_size        := NULL;  
  max_disk_size        := NULL;
  num_failgroup        := NULL;
  min_failgroup_size   := NULL;
  max_failgroup_size   := NULL;
  au_size              := ':au_size';
  density_factor        := NULL;

  select count(*) into is_used from v$asm_client; 
  
  -- if asm is used 
  if (is_used >= 1) then

       select max(total_mb), min(total_mb), sum(total_mb), count(*)
         into max_diskgroup_size, min_diskgroup_size, 
              total_diskgroup_size, num_diskgroup
         from v$asm_diskgroup;

       select max(total_mb), min(total_mb), count(*)
         into max_disk_size, min_disk_size, num_disk
         from v$asm_disk;

       select max(total_fg_mb), min(total_fg_mb), count(*)
         into max_failgroup_size, min_failgroup_size, num_failgroup
         from (select sum(total_mb) as total_fg_mb 
                 from v$asm_disk 
                 group by failgroup);

                               
                        
       for item in (select type, count(*) as rcount from v$asm_diskgroup group by type)
       loop
         redundancy_type:=redundancy_type||':'||item.type||'='||item.rcount;
       end loop;

       for item in (select allocation_unit_size as ausz from v$asm_diskgroup )
       loop
         au_size:=au_size||':'||item.ausz;
       end loop;

       select sys_context('SYS_CLUSTER_PROPERTIES','ASM_DENSITYFACT')
         into density_factor
         from dual; 

       summary := redundancy_type||':total_diskgroup_size:'||total_diskgroup_size
                ||':max_diskgroup_size:'||max_diskgroup_size
                ||':min_diskgroup_size:'||min_diskgroup_size
                ||':num_diskgroup:'||num_diskgroup
                ||':max_disk_size:'||max_disk_size
                ||':min_disk_size:'||min_disk_size
                ||':num_disk:'||num_disk
                ||':max_failgroup_size:'||max_failgroup_size
                ||':min_failgroup_size:'||min_failgroup_size
                ||':num_failgroup:'||num_failgroup
                ||au_size
                ||':density_factor:'||density_factor;

  end if;

END;
/


/***************************************************************
 * DBMS_FEATURE_THP - ASM Thin Provisioning
 *  The procedure to detect usage for Thin Provisioning
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_thp
      (feature_boolean OUT number, aux_count OUT number, feature_info OUT clob)
AS
BEGIN
  select count(*) into feature_boolean from v$asm_attribute 
  where name='thin_provisioned' AND value='true';
  -- compose the CLOB
   
END;
/

/***************************************************************
 * DBMS_FEATURE_FLEX_ASM
 *  The procedure to detect usage for Flex ASM
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_flex_asm
      (is_used OUT number, aux_count OUT number, feature_info OUT clob)
AS
   flxasmstat         VARCHAR2(30);   
   numfailovr         VARCHAR2(30);
BEGIN
  select sys_context('SYS_CLUSTER_PROPERTIES', 'FLEXASM_STATE') 
         into flxasmstat from dual;
  
  if (flxasmstat = 'FLEXASM_ENABLED') then
    is_used := 1;
  else
    is_used := 0;
  end if;

  if (is_used >= 1) then
    select sys_context('SYS_CLUSTER_PROPERTIES', 'FLEXASM_STATE_HW') 
           into numfailovr from dual;  

    -- compose the CLOB
    feature_info := ':num_failovers:'||numfailovr; 
  end if;

END;
/

/***************************************************************
 * DBMS_FEATURE_AFD
 *  The procedure to detect usage for ASM Filter Driver
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_afd
      (is_used OUT number, aux_count OUT number, feature_info OUT clob)
AS
BEGIN
  select count(*) into is_used
         from v$asm_disk where path LIKE 'AFD:%' AND
                               state = 'NORMAL' AND
                               library LIKE '%AFD Library%' AND
                               mount_status = 'CACHED' AND
                               group_number != '0';
END;
/

/***************************************************************
 * DBMS_FEATURE_ACFS
 *  The procedure to detect usage for ACFS -
 *  Automatic Storage Management Cluster File System
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_acfs
      (is_used OUT number, aux_count OUT number, feature_info OUT clob)
AS
  numfs    NUMBER;                            -- # of file systems
BEGIN
  /* Query to detect if ACFS drivers are loaded */
  select sys_context('SYS_CLUSTER_PROPERTIES','ACFS_SUPPORTED')
         into is_used from dual; 
  
  /* Query to retrieve the number of file systems across the cluster */
  select count(*) into numfs from gv$asm_filesystem;

   -- compose the CLOB
   feature_info := ':num_filesystems:'||numfs;
END;
/

/***************************************************************
 * DBMS_FEATURE_ACFS_SNAPSHOT
 *  The procedure to detect usage for ACFS SNAPSHOT -
 *  Automatic Storage Management Cluster File System Snapshot
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_acfs_snapshot
      (is_used OUT number, aux_count OUT number, feature_info OUT clob)
AS
BEGIN
  /* Query to detect if ACFS snapshot are being used */
  select count(*) into is_used from gv$asm_acfssnapshots;

   -- compose the CLOB
   feature_info := ':num_snapshots: '||is_used;
END;
/

/***************************************************************
 * DBMS_FEATURE_ACFS_ENCR
 *  The procedure to detect usage for ACFS Encryption -
 *  Automatic Storage Management Cluster File System
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_acfs_encr
      (is_used OUT number, aux_count OUT number, feature_info OUT clob)
AS
BEGIN
  /* Query to detect if there is at least one acfs file system
   * with encryption enabled. */
  select count(*) into is_used from V$ASM_ACFS_ENCRYPTION_INFO
         where set_status like 'YES' and
         enabled_status like 'ENABLED'; 
END;
/

/***************************************************************
 * DBMS_FEATURE_AUTOSTA
 *  The procedure to detect usage for Automatic SQL Tuning
 ***************************************************************/

CREATE OR REPLACE PROCEDURE dbms_feature_autosta
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  asqlt_task_name    dbms_id := 'SYS_AUTO_SQL_TUNING_TASK';

  execs_since_sample NUMBER;                  -- # of execs since last sample
  total_execs        NUMBER;                  -- number of task executions 
  w_auto_impl        NUMBER;                  -- execs with AUTO implement on
  profs_rec          NUMBER;                  -- total profiles in task
  savedsecs          NUMBER;                  -- db time saved (s)
  tmp_buf            VARCHAR2(32767);         -- temp buffer
BEGIN

  /*
   * We compute the following stats for db feature usage:
   *   Number of executions since last sample (execs_since_sample)
   *   Total number of executions in the task (total_execs)
   *   Total number of executions with auto-implement ON (w_auto_impl)
   *   Total number of SQL profiles recommended in the task (profs_rec)
   *   Projected DB Time Saved through Auto Implementation (savedsecs)
   *
   * Note that these stats are only computed through looking at the task,
   * which, by default, stores results from the last month of history only.
   */

  -- execs since last sample
  SELECT count(*)
  INTO   execs_since_sample 
  FROM   dba_advisor_executions 
  WHERE  task_name = asqlt_task_name AND 
         execution_last_modified >= (SELECT nvl(max(last_sample_date),
                                                sysdate-7) 
                                     FROM   dba_feature_usage_statistics);
  
  -- total # of executions
  SELECT count(*) 
  INTO   total_execs
  FROM   dba_advisor_executions 
  WHERE  task_name = asqlt_task_name;

  -- #execs with auto implement ON
  SELECT count(*) 
  INTO   w_auto_impl
  FROM   dba_advisor_exec_parameters 
  WHERE  task_name = asqlt_task_name AND 
         parameter_name = 'ACCEPT_SQL_PROFILES' AND 
         parameter_value = 'TRUE';

  -- total profiles recommended so far
  SELECT count(*) 
  INTO   profs_rec
  FROM   dba_advisor_recommendations r 
  WHERE  r.task_name = asqlt_task_name AND
         r.type = 'SQL PROFILE';

  -- db time saved by AUTO impl profiles
  SELECT round(nvl(sum(before_usec - after_usec)/1000000, 0))
  INTO   savedsecs 
  FROM   (SELECT nvl(o.attr8, 0) before_usec, 
                 nvl(o.attr8, 0) * (1 - r.benefit/10000) after_usec
          FROM   dba_sql_profiles sp,
                 dba_advisor_objects o,
                 dba_advisor_findings f,
                 dba_advisor_recommendations r
          WHERE  o.task_name = asqlt_task_name AND
                 o.type = 'SQL' AND
                 sp.task_id = o.task_id AND
                 sp.task_obj_id = o.object_id AND
                 sp.task_exec_name = o.execution_name AND
                 o.task_id = f.task_id AND 
                 o.execution_name = f.execution_name AND
                 o.object_id = f.object_id AND
                 f.finding_id = sp.task_fnd_id AND
                 r.task_id = f.task_id AND
                 r.execution_name = f.execution_name AND
                 r.finding_id = f.finding_id AND
                 r.rec_id = sp.task_rec_id AND
                 sp.type = 'AUTO');

  -- the used boolean and aux count we set to the number of execs since last
  -- sample
  feature_boolean := execs_since_sample;
  aux_count := execs_since_sample;

  -- compose the CLOB
  tmp_buf := 'Execution count so far: '          || total_execs || ', ' ||
             'Executions with auto-implement: '  || w_auto_impl || ', ' ||
             'SQL profiles recommended so far: ' || profs_rec   || ', ' ||
             'Projected DB Time Saved Automatically (s): ' || savedsecs;

  dbms_lob.createtemporary(feature_info, TRUE);
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);

END dbms_feature_autosta;
/

/************************************************************************
 * DBMS_FEATURE_SPD - DBMS FEATURE Sql Plan Directive
 *  The procedure to detect usage for statistics incremental maintenance
 ***********************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_spd
  (feature_boolean    OUT  NUMBER,
   aux_count          OUT  NUMBER,
   feature_info       OUT  CLOB)
AS
  NEW_LINE      CONSTANT  VARCHAR2(8) := '
';
  num_dirs                NUMBER;   -- number of directives
  plan_dir_mgmt_control   NUMBER;
  dsdir_usage_control     NUMBER;
  spd_retention_weeks     NUMBER;
  num_dir_obj             NUMBER;
  num_dir_subobj          NUMBER;
  tmp_buf                 VARCHAR2(32767);
  CURSOR spd_reason_cursor IS
    select reason c1, count(*) c2 from dba_sql_plan_directives group by reason;
  CURSOR spd_state_cursor IS
    select state c1, count(*) c2 from dba_sql_plan_directives group by state;
  CURSOR spd_type_cursor IS
    select type c1, count(*) c2 from dba_sql_plan_directives group by type;
BEGIN
  -- get total number of rows in dba_sql_plan_directives
  SELECT count(*)
  INTO num_dirs
  FROM dba_sql_plan_directives;

  dbms_lob.createtemporary(feature_info, TRUE);  
  
  -- # of directives with each type
  for spd_type_iter in spd_type_cursor
  loop
    tmp_buf := 'Number of directives with type, '||spd_type_iter.c1||': '||
               spd_type_iter.c2 || NEW_LINE;
    dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);
  end loop;

  -- # of directives with each reason
  for spd_reason_iter in spd_reason_cursor
  loop
    tmp_buf := 'Number of Directives with reason, '||spd_reason_iter.c1||': '||
               spd_reason_iter.c2||NEW_LINE;
    dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);
  end loop;  

  -- # of directives with each state
  for spd_state_iter in spd_state_cursor
  loop
    tmp_buf := 'Number of Directives with state, '||spd_state_iter.c1||': '||
               spd_state_iter.c2 || NEW_LINE;
    dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);
  end loop;

  -- # of directive objects and subobjects
  select count(object_name), count(subobject_name)
  into num_dir_obj, num_dir_subobj
  from dba_sql_plan_dir_objects;

  tmp_buf := 'Number of Directive objects: '|| num_dir_obj || 
             ', subobjects: ' || num_dir_subobj || NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  

  -- # of retention weeks 
  select dbms_spd.get_prefs('SPD_RETENTION_WEEKS')
  into spd_retention_weeks
  from dual;

  tmp_buf := 'spd_retention_weeks: '||spd_retention_weeks|| NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  

  -- get value of _sql_plan_directive_mgmt_control
  select ksppstvl value
  into plan_dir_mgmt_control
  from x$ksppi x, x$ksppcv y where (x.indx = y.indx) and
  ksppinm = '_sql_plan_directive_mgmt_control';

  tmp_buf := '_sql_plan_directive_mgmt_control: ' || plan_dir_mgmt_control
             || NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  

  -- get value of _optimizer_dsdir_usage_control
  select ksppstvl value
  into dsdir_usage_control
  from x$ksppi x, x$ksppcv y where (x.indx = y.indx) and
  ksppinm = '_optimizer_dsdir_usage_control';
  
  tmp_buf := '_optimizer_dsdir_usage_control: ' || dsdir_usage_control || 
             NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  

  -- populate the outputs if some directive mgmt operation is enabled or
  -- if there is atleast one directive.
  if (plan_dir_mgmt_control > 0 or num_dirs > 0) then
    feature_boolean := plan_dir_mgmt_control; 
  else
    feature_boolean := 0;
  end if;

  aux_count := num_dirs;

END dbms_feature_spd;
/
show errors

/**************************************************************************
 * DBMS_FEATURE_ADAPTIVE_PLANS - DBMS FEATURE Adaptive Plans
 *  The procedure is to track the usage of adaptive execution plans.
 *************************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_adaptive_plans
  (feature_boolean    OUT  NUMBER,
   aux_count          OUT  NUMBER,
   feature_info       OUT  CLOB)
AS
  NEW_LINE      CONSTANT  VARCHAR2(8) := '
';

  adaptive_plan_param     VARCHAR2(10);   -- adaptive plans parameter
  reporting_param         VARCHAR2(10);   -- reporting mode parameter 
  num_all_queries         NUMBER;         -- number of queries
  num_adaptive_queries    NUMBER;         -- number of adaptive queries
  reporting_param_value   VARCHAR2(10);   -- reporting mode param value
  tmp_buf                 VARCHAR2(32767);

BEGIN
  dbms_lob.createtemporary(feature_info, TRUE);  

  select ksppstvl
  into adaptive_plan_param
  from x$ksppi x, x$ksppcv y where (x.indx = y.indx) and
  ksppinm = 'optimizer_adaptive_plans';

  select ksppstvl
  into reporting_param
  from x$ksppi x, x$ksppcv y where (x.indx = y.indx) and
  ksppinm = 'optimizer_adaptive_reporting_only';

  if (reporting_param = 'FALSE' AND adaptive_plan_param = 'TRUE') then
    feature_boolean := 1; 
  else
    feature_boolean := 0;
  end if;

  if (reporting_param = 'FALSE') then
    reporting_param_value := 'No';
  else
    reporting_param_value := 'Yes';
  end if; 


  -- Find # of sqls in v$sql
  select count(*)
  into   num_all_queries
  from   v$sql vs, v$sqlcommand vsc 
  where vs.command_type = vsc.command_type and
  vsc.command_name in ('INSERT', 'SELECT', 'UPDATE', 'DELETE', 'UPSERT');
 
  -- Find # of sqls which are adaptive
  select count(*)
  into   num_adaptive_queries
  from   v$sql vs, v$sqlcommand vsc
  where vs.command_type = vsc.command_type and 
  vs.is_resolved_adaptive_plan is NOT NULL and 
  vsc.command_name in ('INSERT', 'SELECT', 'UPDATE', 'DELETE', 'UPSERT'); 

  tmp_buf := 'Total number of queries: ' || num_all_queries || 
             NEW_LINE ;

  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf); 

  tmp_buf := 'Number of queries with an adaptive plan: ' || 
              num_adaptive_queries || 
             NEW_LINE ;

  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf); 

  tmp_buf := 'Percentage of queries with an adaptive plan: ' || 
             100*num_adaptive_queries/num_all_queries || 
             NEW_LINE ;

  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf); 

  tmp_buf := 'Are the queries running in reporting mode ? : ' || 
             reporting_param_value || 
             NEW_LINE ;

  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf); 

END dbms_feature_adaptive_plans;
/
show errors

/**************************************************************************
 * DBMS_FEATURE_AUTO_REOPT - DBMS FEATURE Auto reoptimization
 *  The procedure is to track the usage of adaptive reoptimization.
 *************************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_auto_reopt
  (feature_boolean    OUT  NUMBER,
   aux_count          OUT  NUMBER,
   feature_info       OUT  CLOB)
AS
  NEW_LINE      CONSTANT  VARCHAR2(8) := '
';

  reporting_param         VARCHAR2(10);
  use_feedback_param      VARCHAR2(10);
  num_all_queries         NUMBER;   -- number of queries
  num_reopt_queries       NUMBER;   -- number of reoptimizable queries
  tmp_buf                 VARCHAR2(32767);

BEGIN
  dbms_lob.createtemporary(feature_info, TRUE);  

  select ksppstvl
  into reporting_param
  from x$ksppi x, x$ksppcv y where (x.indx = y.indx) and
  ksppinm = 'optimizer_adaptive_reporting_only';

  select ksppstvl
  into use_feedback_param
  from x$ksppi x, x$ksppcv y where (x.indx = y.indx) and
  ksppinm = '_optimizer_use_feedback';
 
  if (reporting_param = 'FALSE' AND use_feedback_param = 'TRUE') then
    feature_boolean := 1; 
  else
    feature_boolean := 0;
  end if; 

  -- Find # of sqls in v$sql
  select count(*)
  into   num_all_queries
  from   v$sql vs, v$sqlcommand vsc 
  where vs.command_type = vsc.command_type and
  vsc.command_name in ('INSERT', 'SELECT', 'UPDATE', 'DELETE', 'UPSERT'); 
 
  -- Find # of sqls which are reoptimizable
  select count(*)
  into   num_reopt_queries
  from   v$sql vs, v$sqlcommand vsc 
  where vs.command_type = vsc.command_type and vs.is_reoptimizable = 'Y' 
  and vsc.command_name in ('INSERT', 'SELECT', 'UPDATE', 'DELETE', 'UPSERT');    

  tmp_buf := 'Total number of queries: ' || num_all_queries || 
             NEW_LINE ;

  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf); 

  tmp_buf := 'Number of reoptimizable queries: ' || num_reopt_queries || 
             NEW_LINE ;

  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf); 

  tmp_buf := 'Percentage of reoptimizable queries: ' || 
             100*num_reopt_queries/num_all_queries || 
             NEW_LINE ;

  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf); 

END dbms_feature_auto_reopt;
/
show errors

/**************************************************************************
 * DBMS_FEATURE_CONCURRENT_STATS - DBMS FEATURE Concurrent Stats Gathering
 *  The procedure is to track the usage of concurrent stats gathering.
 *************************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_concurrent_stats
  (feature_boolean    OUT  NUMBER,
   aux_count          OUT  NUMBER,
   feature_info       OUT  CLOB)
AS
  NEW_LINE      CONSTANT  VARCHAR2(8) := '
';
  num_all_ops             NUMBER;   -- number of all stats operations
  num_gath_ops            NUMBER;   -- number of gather operations
  num_conc_ops            NUMBER;   -- number of concurrent stats operations
  num_auto_ops            NUMBER;   -- number of auto stats gathering jobs
  interval_start          VARCHAR(20); -- minimum start time in operations view
  interval_end            VARCHAR(20); -- maximum start time in operations view
  conc_pref               VARCHAR2(10); -- value of "concurrent" preference
  tmp_buf                 VARCHAR2(32767);

  -- cursor to get the types of stats gathering operations which were
  -- were performed concurrently.
  CURSOR op_type_cursor IS
    select operation op, count(*) cnt from dba_optstat_operations
    where  extractvalue(xmltype(notes), 
                       '/params/param[@name="concurrent"]/@val') = 'TRUE'
    group by operation
    order by 2 desc;

BEGIN
  dbms_lob.createtemporary(feature_info, TRUE);  

  -- get the time interval reported in dba_optstat_operations
  select to_char(min(start_time), 'MM/DD/YYYY'),
         to_char(max(start_time), 'MM/DD/YYYY') 
         into interval_start, interval_end
  from dba_optstat_operations;

  tmp_buf := 'Time interval covered by dba_optstat_operations: '|| 
             interval_start || ' - ' || interval_end || NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  

  -- get the total number of rows in dba_optstat_operations
  SELECT count(*)
  INTO num_all_ops
  FROM dba_optstat_operations;

  tmp_buf := 'Total Number of All Stats Operations: '|| num_all_ops 
             || NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  

  -- get the number of gather stats operations which is eligible to be run
  -- concurrently
  SELECT count(*)
  INTO num_gath_ops
  FROM dba_optstat_operations
  WHERE operation like 'gather%';

  tmp_buf := 'Total Number of Gather Stats Operations: '|| num_gath_ops 
             || NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  

  -- get the number of concurrent stats gathering operations
  SELECT count(*)
  INTO num_conc_ops
  FROM dba_optstat_operations
  WHERE extractvalue(xmltype(notes), 
                       '/params/param[@name="concurrent"]/@val') = 'TRUE';
 
  tmp_buf := 'Total Number of Concurrent Operations: ' || num_conc_ops || '.'
              || NEW_LINE || 
             'Types of concurrent operations with their frequencies:'
              || NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);

  -- # of concurrent operations group by the kind of operation
  for op_type_iter in op_type_cursor
  loop
    tmp_buf := '  ' || op_type_iter.op ||': '||
                      op_type_iter.cnt || NEW_LINE;
    dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);
  end loop; 

  -- value of "concurrent" preference
  select dbms_stats.get_prefs('CONCURRENT')
  into conc_pref
  from dual;

  tmp_buf := 'Current value of CONCURRENT preference: '|| conc_pref || NEW_LINE;
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);  
  
  -- populate the outputs if concurrent stats gathering is enabled
  if (num_conc_ops > 0 OR
      upper(conc_pref) not in ('OFF', 'FALSE')) then
    feature_boolean := 1; 
  else
    feature_boolean := 0;
  end if;

  if (num_gath_ops > 0) then
    aux_count := num_conc_ops/num_gath_ops;
  else
    aux_count := -1;
  end if;

END dbms_feature_concurrent_stats;
/
show errors

/************************************************************************
 * DBMS_FEATURE_STATS_INCREMENTAL
 *  The procedure to detect usage for statistics incremental maintenance
 ***********************************************************************/

CREATE OR REPLACE PROCEDURE dbms_feature_stats_incremental
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  im_preference      VARCHAR2(30) := 'INCREMENTAL';
  global_on          VARCHAR2(20);
  table_im_on        NUMBER;
  table_im_off       NUMBER;
  stats_gathered_im  NUMBER;
  tmp_buf            VARCHAR2(32767);   
BEGIN

  /*
   * We compute the following stats for db feature usage:
   *   whether global preference of incremental maintenance turned on
   *   # of tables with table level incremental maintenance preference 
   *     turned on
   *   # of tables with table level incremental maintenance preference 
   *     turned off
   *   # of tables that have had stats gathered in incremental mode
   */

  --whether global preference of incremental maintenance turned on
  SELECT decode(count(*), 0, 'FALSE', 'TRUE')
  INTO   global_on  
  FROM   dual
  WHERE  dbms_stats.get_prefs(im_preference) = 'TRUE';

  --# of tables with table level incremental maintenance preference 
  -- turned on
  SELECT count(*)
  INTO   table_im_on
  FROM   all_tab_stat_prefs
  WHERE  PREFERENCE_NAME = im_preference and PREFERENCE_VALUE = 'TRUE';

  -- # of tables with table level incremental maintenance preference 
  -- turned off
  SELECT count(*)
  INTO   table_im_off
  FROM   all_tab_stat_prefs
  WHERE  PREFERENCE_NAME = im_preference and PREFERENCE_VALUE = 'FALSE';

  -- # of tables that have had stats gathered in incremental mode
  SELECT distinct count(bo#)
  INTO   stats_gathered_im
  FROM   sys.wri$_optstat_synopsis_head$
  WHERE  analyzetime is not null;

  -- the used boolean and aux count we set to the number of execs since last
  -- sample
  feature_boolean := stats_gathered_im;
  aux_count := stats_gathered_im;

  -- compose the CLOB
  tmp_buf := 'Incremental global preference on : ' || global_on || ', ' ||
    'Number of tables with table level incremental maintenance preference ' ||
      'turned on: ' || table_im_on || ', ' ||
    'Number of tables with table level incremental maintenance preference ' ||
      'turned off: ' || table_im_off || ', ' ||
    'Number of tables that have had statistics gathered in incremental mode: ' || 
      stats_gathered_im;

  dbms_lob.createtemporary(feature_info, TRUE);
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);

END dbms_feature_stats_incremental;
/

/***************************************************************
 * DBMS_FEATURE_WCR_CAPTURE
 *  The procedure to detect usage for Workload Capture
 ***************************************************************/

CREATE OR REPLACE PROCEDURE dbms_feature_wcr_capture
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  prev_sample_count     NUMBER;
  prev_sample_date      DATE;
  prev_sample_date_dbtz DATE;
  date_format           CONSTANT VARCHAR2(64) := 'YYYY:MM:DD HH24:MI:SS';

  captures_since     NUMBER;             -- # of captures since last sample
BEGIN

  /*
   * We compute the total number of captures done on the 
   * current database by finding the number of captures done
   * since the last sample and adding it to the current aux_count.
   */

  -- Find prev_sample_count and prev_sample_date first
  select nvl(max(aux_count), 0), nvl(max(last_sample_date), sysdate-7)
  into   prev_sample_count, prev_sample_date
  from   dba_feature_usage_statistics
  where  name = 'Database Replay: Workload Capture';

  -- convert date to db timezone
  select to_date(to_char(from_tz(cast(prev_sample_date as timestamp), 
         sessiontimezone) at time zone dbtimezone, date_format), 
         date_format) into prev_sample_date_dbtz from dual;

  -- Find # of workload captures since last sample in current DB
  select count(*)
  into   captures_since
  from   dba_workload_captures
  where  (prev_sample_date_dbtz is null OR start_time > prev_sample_date_dbtz)
   and   dbid = (select dbid from v$database);

  -- Mark boolean to be captures_since
  feature_boolean := captures_since;
  -- Add current aux_count with captures_since for new value
  aux_count       := prev_sample_count + captures_since;
  -- Feature_info not used
  feature_info    := NULL;

END dbms_feature_wcr_capture;
/

show errors;
/

/***************************************************************
 * DBMS_FEATURE_WCR_REPLAY
 *  The procedure to detect usage for Workload Replay
 *  Almost Verbatim to DBMS_FEATURE_WCR_CAPTURE
 ***************************************************************/

CREATE OR REPLACE PROCEDURE dbms_feature_wcr_replay
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  prev_sample_count     NUMBER;
  prev_sample_date      DATE;
  prev_sample_date_dbtz DATE;
  date_format           CONSTANT VARCHAR2(64) := 'YYYY:MM:DD HH24:MI:SS';

  replays_since      NUMBER;             -- # of replays since last sample
BEGIN

  /*
   * We compute the total number of replays done on the 
   * current database by finding the number of replays done
   * since the last sample and adding it to the current aux_count.
   */

  -- Find prev_sample_count and prev_sample_date first
  select nvl(max(aux_count), 0), nvl(max(last_sample_date), sysdate-7)
  into   prev_sample_count, prev_sample_date
  from   dba_feature_usage_statistics
  where  name = 'Database Replay: Workload Replay';

  -- convert date to db timezone
  select to_date(to_char(from_tz(cast(prev_sample_date as timestamp), 
         sessiontimezone) at time zone dbtimezone, date_format), 
         date_format) into prev_sample_date_dbtz from dual;

  -- Find # of workload replays since last sample in current DB
  select count(*)
  into   replays_since
  from   dba_workload_replays
  where  (prev_sample_date_dbtz is null OR start_time > prev_sample_date_dbtz)
    and  dbid = (select dbid from v$database);

  -- Mark boolean to be replays_since
  feature_boolean := replays_since;
  -- Add current aux_count with replays_since for new value
  aux_count       := prev_sample_count + replays_since;
  -- Feature_info not used
  feature_info    := NULL;

END dbms_feature_wcr_replay;
/

show errors;
/


/***************************************************************
 * DBMS_FEATURE_PARTITION_USER
 *  The procedure to detect usage for Partitioning (user)
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_partition_user
      (is_used OUT number, data_ratio OUT number, clob_rest OUT clob)
AS  
  c1            SYS_REFCURSOR;
  stmt          varchar2(32767) default null;
  inc_result    varchar2(32767) default null;
  result        clob            default null;
BEGIN
  -- initialize
  is_used       := 0;
  data_ratio    := 0;
  clob_rest     := NULL;

  -- switch to dynamic sql because mdsys catalog not 
  -- available when catfusrg.sql is run
  stmt := q'!select num||':'||idx_or_tab||':'||ptype||':'||subptype||':'||
                    pcnt||':'||subpcnt||':'||pcols||':'||subpcols||':'||
                    idx_flags||':'||idx_type||':'||idx_uk||':'||rpcnt||':'||
                    rsubpcnt||':'|| def_segment_creation||':'||
                    partial_idx||':'||orphaned_entries ||':'||zonemap||':'||
                    attrcluster||':'||subpartemp 
                    -- read only partitions and subpartitions
                    ||':'|| numrodeftab||':'||numrodefpart
                    ||':'|| numropart||':'||numrosubpart
                    -- partitioned external tables
                    ||':'|| exttab ||':'||exttype
                    -- some debug info for customer-specific analysis,
                    -- if required. Uncommenting this line will provide the
                    -- tracking of object owner and object name
--                      ||  ':'|| owner ||'.'||name
                    || '|' my_string
               from (select dense_rank() over (order by  decode(bo#,null,pobj#,bo#)) NUM,
                            idx_or_tab, 
                            ptype, pcols, pcnt, rpcnt, 
                            subptype, subpcols, subpartemp, subpcnt, rsubpcnt,
                            idx_flags, idx_type, idx_uk, orphaned_entries,
                            def_segment_creation, partial_idx,            
                            zonemap, attrcluster
                            -- read only
                            , numrodeftab, numrodefpart
                            , numropart, numrosubpart
                            -- partitioned external tables
                            , exttab, exttype
                            -- some debug info
                            , owner, name
                     from
                     ( select /*+ full(o) */ o.obj#, i.bo#, p.obj# pobj#, 
                       u.name owner, o.name name,
                       decode(o.type#,1,'I',2,'T',null) IDX_OR_TAB, 
                       is_xml ||
                       -- tracking of JSON partitioned tables, similar to XML tracking
                       -- e.g. prefixing the partitioning type
                       case when bitand(p.flags,4194304)=4194304 then 'JSON-' end ||
                       -- introducing abbreviations
                       -- I: interval, R: range, H: hash, L: list, S: system
                       -- RF: reference, IR: interval-ref, AL: auto list
                       -- (P): parent of reference partitioned table
                       decode(p.parttype, 1, case when bitand(p.flags,64)=64 then 
                                                 -- INTERVAL-REF, 12c
                                                 case when bitand(p.flags,32)=32 then 'IR'
                                                      else 'I' end 
                                                 else 'R' end 
                                         ,2, 'H', 3, 'S' 
                                                 -- AUTO LIST, 12.2
                                                 ,4, case when bitand(p.flags,64)=64 
                                                          then 'AL'
                                                          else 'L' end
                                                ,5, 'RF'
                                         ,p.parttype||'-?') ||
                      decode(bitand(p.flags,32),32,' (P)') PTYPE,
                       -- introducing abbreviations
                       -- I: interval, R: range, H: hash, L: list, S: system
                       -- RF: reference, IR: interval-ref, AL: auto list
                       -- (P): parent of reference partitioned table 
                      decode(mod(p.spare2, 256), 0, null
                                                   , 1, case when bitand(p.flags, 32768) = 32768
                                                             then 'I'
                                                             else 'R' end
                                                    , 2, 'H', 3,'S' 
                                                    -- AUTO LIST, 12.2
                                                    ,4, case when bitand(p.flags,32768)=32768 
                                                             then 'AL'
                                                             else 'L' end
                                                    , 5, 'RF' 
                                                    , p.spare2||'-?') SUBPTYPE,
                      p.partcnt PCNT, 
                      -- interval subpartitioning
                      -- overloading default subpartitioning count with max number of
                      -- subpartitions. default subpartition count does not really make sense
                      -- for interval subpartition since subpartitions will be created on demand
                      case when (mod(p.spare2, 256) = 1 and bitand(p.flags, 32768) = 32768)
                           then 1048575
                      else
                           mod(trunc(p.spare2/65536), 65536) 
                      end SUBPCNT,
                      to_char(p.partkeycols) || vc.vc_p PCOLS, 
                      to_char(case mod(trunc(p.spare2/256), 256) 
                           when 0 then null 
                           else mod(trunc(p.spare2/256), 256) end)||vc.vc_sp SUBPCOLS,
                      case when bitand(p.flags,1) = 1 then 
                                case when bitand(p.flags,2) = 2 then 'LP'
                                      else 'L' end
                           when bitand(p.flags,2) = 2 then 'GP' 
                      end IDX_FLAGS,
                      -- introducing abbreviation
                      -- N: normal, /R: reverse, B: bitmap, C: cluster, IT: iot - top
                      -- IN: iot nested, S: secondary, A: ansi, L: lob, -F: function-based
                      -- D: domain
                      decode(i.type#, 1, 'N'||
                                          decode(bitand(i.property, 4), 0, '', 4, '/R'),
                                      2, 'B', 3, 'C', 4, 'IT',
                                      5, 'IN', 6, 'S', 7, 'A', 8, 'L',
                                      9, 'D')  || 
                                      case when bitand(i.property,16) = 16 
                                           then '-F' end IDX_TYPE,
                      decode(i.property, null,null,
                                         decode(bitand(i.property, 1), 0, 'NU', 
                                                                       1, 'U', '?')) IDX_UK,
                      -- real partition and subpartition count
                      case when bitand(p.flags,64)=64 then op.xnumpart else p.partcnt end RPCNT,
                      osp.numsubpart RSUBPCNT,
                      -- deferred segments
                      case o.type# 
                      when 1 then --index
                        decode(ip_seg_off,null,isp_seg_off,ip_seg_off)   
                      when 2 then --table
                        decode(tp_seg_off,null,tsp_seg_off,tp_seg_off)
                      else null end DEF_SEGMENT_CREATION,
                      -- partial indexing
                      -- this is overloaded functionality, showing different values for tables
                      -- and indexes
                      -- tables:   ON | OFF
                      -- indexes:  P: partial, F: full index
                      -- in addition it is overloaded with the number of [subb]partitions
                      -- with indexing off FOR PARTITIONED INDEXES. Null for nonpartitioned ones
                      case o.type# 
                      when 1 then --index
                         decode(bitand(i.flags, 8388608), 8388608, 'P', 'F')||'-'||
                        -- overload field with count of all [sub]partitions with indexing off
                        decode(ip_idx_off,null,isp_idx_off,ip_idx_off)
                      when 2 then --table
                        decode(bitand(p.flags,8192),8192,'OFF','ON')||'-'||
                        -- overload field with count of all [sub]partitions with indexing off
                        decode(tp_idx_off,null,tsp_idx_off,tp_idx_off)
                      else null end PARTIAL_IDX,
                      null ORPHANED_ENTRIES,
                      decode(zonemap,null,'N',zonemap) ZONEMAP, 
                      decode(attrcluster,null,'N',attrcluster) ATTRCLUSTER,
                      st_part SUBPARTEMP
                      -- 12.2 read only partition counts
                      , decode(bitand(p.flags, 65536), 65536, 1, 0) NUMRODEFTAB -- table default read only
                      , numrodefpart -- partitions default read only
                      , numropart, numrosubpart -- real counts of read only [sub]partitions
                      , exttab 
                      , decode(exttype,'ORACLE_LOADER','LD','ORACLE_HIVE','HI',
                                       'ORACLE_HDFS','HD','ORACLE_DATAPUMP','DP',exttype) as exttype 
                      from partobj$ p, obj$ o, user$ u, ind$ i, 
                           ( select distinct obj#, 'XML-' as is_xml from opqtype$ where type=1) xml,
                           -- real subpartition count for tables and indexes
                           ( select /* NO_MERGE FULL(tsp) FULL(tcp) */ tcp.bo#, count(*) numsubpart
                             -- default read only partitions (none counts as no)
                             , max(tcp.numrodefpart) numrodefpart
                             -- read only subpartition count
                             , sum(decode(bitand(tsp.flags, 67108864), 67108864, 1, 0)) numrosubpart
                             from tabsubpart$ tsp, 
                                  (select obj#, bo#, spare2,
                                          sum(decode(bitand(spare2, 12288), 4096, 1, 0))
                                          over (partition by bo#)  numrodefpart
                                   from tabcompart$) tcp 
                             where tcp.obj# = tsp.pobj# 
                             group by tcp.bo#
                             union all
                             select /* NO_MERGE FULL(isp) FULL(icp) */ icp.bo#, count(*) numsubpart
                             -- dummy for read only info
                             , null, null 
                             from indsubpart$ isp, indcompart$ icp 
                             where icp.obj# = isp.pobj# 
                             group by icp.bo#) osp,
                           -- real partition count for tables and indexes
                           ( select tp.bo#, count(*) xnumpart
                             -- read only partitions count
                             , sum(decode(bitand(tp.flags, 67108864), 67108864, 1, 0)) numropart            
                             from tabpart$ tp
                             group by tp.bo#
                             union all
                             select ip.bo#, count(*) xnumpart
                             -- dummy for read only partitions
                             , 0
                             from indpart$ ip
                             group by ip.bo#) op,                            
                           -- details table partitions: partial indexing and deferred segments
                           ( select tp.bo#,
                                    -- number or partitions with indexing off 
                                    sum(decode(bitand(tp.flags, 2097152), 2097152, 1, 0))  tp_idx_off,
                                    -- number or partitions with deferred segment creation
                                    sum(decode(bitand(tp.flags, 65536), 65536, 1, 0))  tp_seg_off
                             from tabpart$ tp
                             group by tp.bo#) pxd,
                           -- details table subpartitions: partial indexing and deferred segments
                           ( select tcp.bo#, 
                                    -- number or subpartitions with indexing off 
                                    sum(decode(bitand(tsp.flags, 2097152), 2097152, 1, 0))  tsp_idx_off,
                                    -- number or subpartitions with deferred segment creation
                                    sum(decode(bitand(tsp.flags, 65536), 65536, 1, 0))  tsp_seg_off
                             from tabsubpart$ tsp, tabcompart$ tcp 
                             where tcp.obj# = tsp.pobj#
                             group by tcp.bo#) spxd,
                           -- details index partitions: partial indexing and deferred segments
                           ( select ip.bo#,
                                    -- number or partitions with indexing off 
                                    sum(decode(bitand(ip.flags, 1), 1, 1, 0))  ip_idx_off,
                                    -- number or partitions with deferred segment creation
                                    sum(decode(bitand(ip.flags, 65536), 65536, 1, 0))  ip_seg_off
                             from indpart$ ip
                             group by ip.bo#) ipd,
                           -- details index subpartitions: partial indexing and deferred segments
                           ( select icp.bo#, 
                                    -- number or subpartitions with indexing off 
                                    sum(decode(bitand(isp.flags, 1), 1, 1, 0))  isp_idx_off,
                                    -- number or subpartitions with deferred segment creation
                                    sum(decode(bitand(isp.flags, 65536), 65536, 1, 0))  isp_seg_off
                             from indsubpart$ isp, indcompart$ icp 
                             where icp.obj# = isp.pobj#
                             group by icp.bo#) ispd,
                           -- attribute clustering
                           ( select c.clstobj#, 'Y-'||
                                    -- kind of attribute clustering  
                                    case when ctable is not null 
                                         then 'MT-' else 'ST-' end
                                    ||
                                    case when clstfunc = 1 then 'I-'     -- interleaved
                                         else 'L-' end     -- linear
                                    ||to_char(decode(ctable, null,0,ctable)+1)||'-'||ccol as ATTRCLUSTER
                             from sys.clst$ c, 
                                  -- count of tables and columns used for attribute clustering
                                  -- no detailed breakdown of columns per row
                                  -- table count does not include fact table for hierarchical attr. clustering
                                  ( select clstobj#, count(intcol#) ccol 
                                    from sys.clstkey$
                                    group by clstobj#) k,
                                  ( select clstobj#, count(*) ctable
                                    from sys.clstjoin$
                                    group by clstobj#) kt
                             where c.clstobj# = k.clstobj#
                             and   c.clstobj# = kt.clstobj#(+)) attrcl,
                            -- zone maps
                            (select detailobj#, zonemap from
                                (select sd.detailobj#, flags, 'Y-'||        
                                        -- single table zonemap or hierarchical zonemap
                                        decode(bitand(sn.flag3, 1024),
                                               0, 'ST', 'MT') ||
                                               -- number of tables and columns in zonemap (aggr, no detailed breakdown)
                                               -- table count does not include fact table for hierarch. zonemap
                                               '-'||  count(distinct sd.detailobj#) over (partition by sd.sumobj#) ||
                                               '-'||  sa.zmcol as ZONEMAP 
                                 from sys.sumdetail$ sd, sys.sum$ s, sys.snap$ sn,
                                      ( select sumobj#, count(*) zmcol
                                        from sys.sumagg$ 
                                        where aggfunction = 18
                                        group by sumobj#) sa
                                 where s.obj# = sd.sumobj# 
                                 and   s.obj# = sa.sumobj#
                                 and s.containernam(+) = sn.vname) v
                             where bitand(v.flags, 2) = 2      /* zonemap fact table */
                           ) zm,
                           ( select bo#, count(*) st_part 
                             from defsubpart$ 
                             group by bo# ) spt,
                           -- partitioned external tables
                           ( select obj#, 'Y' exttab, type$ exttype
                             from external_tab$ ) xt,
                           -- virtual column detection
                           (select obj#, case when p_cnt is not null 
                                         then '-VC('||p_cnt||')' 
                                         else null end as vc_p, 
                                         case when sp_cnt is not null 
                                         then '-VC('||sp_cnt||')' 
                                         else null end as vc_sp
                                         from (select pc.obj#, lvl, count(*) cnt 
                                               from (select obj#, 'P' lvl, intcol#  
                                                     from partcol$ pc
                                                     union all 
                                                     select obj#, 'SP' lvl, intcol#
                                                     from subpartcol$) pc,
                                                    (select obj#, intcol# from col$
                                                     where bitand(property, 8) = 8) c
                                               where pc. obj# = c.obj# and pc.intcol# =c.intcol#
                                              group by pc.obj#, lvl)
                                         pivot (max(cnt) cnt 
                                                for lvl in ('P' as P,'SP' as SP))) vc
                      where o.obj# = i.obj#(+)
                      and   o.owner# = u.user# 
                      and   p.obj# = o.obj#
                      and   p.obj# = xml.obj#(+)
                      and   p.obj# = osp.bo#(+) 
                      and   p.obj# = op.bo#(+)
                      and   p.obj# = pxd.bo#(+)
                      and   p.obj# = spxd.bo#(+)
                      and   p.obj# = ipd.bo#(+)
                      and   p.obj# = ispd.bo#(+)
                      and   p.obj# = spt.bo#(+)
                      and   o.obj# = attrcl.clstobj#(+)
                      and   o.obj# = zm.detailobj#(+)
                      and   o.obj# = xt.obj#(+)
                      and   o.obj# = vc.obj#(+)
                      -- bug 14369338, exclude AUDSYS
                      and   u.name not in ('SYS','SYSTEM','SH','SYSMAN','AUDSYS')
                      -- bug 12573239
                      -- exclude RMAN catalog usage, identified by schema 
                      -- (introduced with ZDLRA, 12.1.0.2)
                      and u.name not in (select u.name 
                                         from sysauth$ sa join user$ u 
                                         on (sa.grantee# = u.user#) join user$ u2 
                                         on (sa.privilege# = u2.user#) 
                                         where u2.name = 'RECOVERY_CATALOG_OWNER')
                      -- bug 12573239
                      -- exclude Spatial topology tables and indexes
                      -- SDO metadata does not have any unique identifier like obj# 
                      -- but only owner and table name. This would make a very expensive 
                      -- query joing back to obj$ and user$ to uniquely identify 
                      -- partitioned indexes through i.bo#, so we are using 
                      -- an approximation by filtering on the prefix of the 
                      -- auto-generated index names ... 
                      and not exists (select 1 
                                      from mdsys.sdo_topo_metadata_table sdo 
                                      where u.name = sdo.sdo_owner 
                                      and o.name like topology||'%')
                      -- fix bug 3074607 - filter on obj$
                      and o.type# in (1,2,19,20,25,34,35)
                      -- exclude flashback data archive tables
                      -- fix bug 14666795
                      -- crystal clear identification of FBA tables deemed as too expensive
                      -- would require a probe against tab$             
                      -- e,g. o.obj# not in (select obj# from tab$ where bitand(property,8589934592)=8589934592)
                      and o.name not like 'SYS_FBA%'
                      -- exclude change tables
                      and o.obj# not in ( select obj# from cdc_change_tables$)
                      -- exclude local partitioned indexes on change tables
                      and i.bo# not in  ( select obj# from cdc_change_tables$)
                union all
                -- global nonpartitioned indexes on partitioned tables
                select o.obj#, i.bo#, p.obj# pobj#,
                       u.name owner, o.name name,
                       'I' IDX_OR_TAB,
                        null,null,null,null,
                        to_char(case cols when 0 then null
                                  else cols end) PCOLS,
                        null SUBPCOLS,
                       'GNP' IDX_FLAGS, 
                      -- introducing abbreviation
                      -- N: normal, /R: reverse, B: bitmap, C: cluster, IT: iot - top
                      -- IN: iot nested, S: secondary, A: ansi, L: lob, -F: function-based
                      -- D: domain
                       decode(i.type#, 1, 'N'||
                                      decode(bitand(i.property, 4), 0, '', 4, '/R'),
                                      2, 'B', 3, 'C', 4, 'IT',
                                      5, 'IN', 6, 'S', 7, 'A', 8, 'L',
                                      9, 'D') ||
                       case when bitand(i.property,16) = 16 then '-F' end IDX_TYPE,
                       decode(i.property, null,null,
                                          decode(bitand(i.property, 1), 0, 'NU', 
                                          1, 'U', '?')) IDX_UK,
                       null, null, 
                       null DEF_SEGMENT_CREATION,
                       -- P: partial, F: full
                       decode(bitand(i.flags, 8388608), 8388608, 'P', 'F') PARTIAL_IDX,
                       decode(bitand(i.flags, 268435456), 268435456, 'YES', 'NO') ORPHANED_ENTRIES,
                       NULL ZONEMAP, NULL ATTRCLUSTER,
                       NULL SUBPARTEMP
                       , NULL NUMDEFROTAB
                       , NULL NUMDEFROPART
                       , NULL NUMROPART
                       , NULL NUMROSUBPART
                       , NULL EXTTAB
                       , NULL EXTTYPE
                from partobj$ p, user$ u, obj$ o, ind$ i
                where p.obj# = i.bo#
                -- exclude global nonpartitioned indexes on change tables
                and   i.bo# not in  ( select obj# from cdc_change_tables$)
                -- exclude flashback data archive tables
                and   o.name not like 'SYS_FBA%'
                -- bug 12573239
                -- exclude global indexes on Spatial topology tables
                and o.name not in (select topology||'_RELATION$' 
                                   from mdsys.sdo_topo_metadata_table sdo 
                                   where u.name = sdo.sdo_owner)
                and   o.owner# = u.user# 
                and   p.obj# = o.obj# 
                -- nonpartitioned index
                and   bitand(i.property, 2) <>2
                -- bug 14369338, exclude AUDSYS
                and   u.name not in ('SYS','SYSTEM','SH','SYSMAN','AUDSYS')
                -- bug 12573239
                -- exclude RMAN catalog usage, identified by schema (introduced with ZDLRA, 12.1.0.2)
                and u.name not in (select u.name 
                                   from sysauth$ sa join user$ u 
                                   on (sa.grantee# = u.user#) join user$ u2 on (sa.privilege# = u2.user#) 
                                   where u2.name like 'RECOVERY_CATALOG_OWNER')
                )
                order by num, idx_or_tab desc)!';

     -- usage of spatial catalog made rewrite to dynamic sql necessary
     open c1 for stmt;

     loop
        fetch c1 into inc_result;
        exit when c1%notfound;

     if (is_used = 0) then
       is_used:=1;
     end if;  

     clob_rest := clob_rest||inc_result;
   end loop;
   
   close c1;

     select pcnt into data_ratio
     from
     (
       SELECT c1, TRUNC((ratio_to_report(sum_blocks) over())*100,2) pcnt  
       FROM
       (
        select decode(p.obj#,null,'REST','PARTTAB') c1, sum(s.blocks) sum_blocks
        from tabpart$ p, seg$ s
        where s.file#=p.file#(+)
        and s.block#=p.block#(+)
        and s.type#=5
        group by  decode(p.obj#,null,'REST','PARTTAB')
        )
      )
      where c1 = 'PARTTAB';

   exception when others then
     if c1%isopen then
        close c1;
     end if;

     is_used    := 0;
     data_ratio := 9999;
     clob_rest  := 'an error occured at processing time ... : '|| sqlerrm;
end;
/

/***************************************************************
 * DBMS_FEATURE_PARTITION_SYSTEM
 *  The procedure to detect usage for Partitioning (system)
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_partition_system
      (is_used OUT number, data_ratio OUT number, clob_rest OUT clob)
AS  
BEGIN
  -- initialize
  is_used := 0;
  data_ratio := 0;
  clob_rest := NULL;

  -- SQL is synchronized with Partitioning user tracking to simplify development
  -- and enhancement of code
  FOR crec IN (select num||':'||idx_or_tab||':'||ptype||':'||subptype||':'||
                    pcnt||':'||subpcnt||':'||pcols||':'||subpcols||':'||
                    idx_flags||':'||idx_type||':'||idx_uk||':'||rpcnt||':'||
                    rsubpcnt||':'|| def_segment_creation||':'||
                    partial_idx||':'||orphaned_entries ||':'||zonemap||':'||
                    attrcluster||':'||subpartemp 
                    -- read only partitions and subpartitions
                    ||':'|| numrodeftab||':'||numrodefpart
                    ||':'|| numropart||':'||numrosubpart
                    -- partitioned external tables
                    ||':'|| exttab ||':'||exttype
                    -- some debug info for customer-specific analysis,
                    -- if required. Uncommenting this line will provide the
                    -- tracking of object owner and object name
--                      ||  ':'|| owner ||'.'||name
                    || '|' my_string
               from (select dense_rank() over (order by  decode(bo#,null,pobj#,bo#)) NUM,
                            idx_or_tab, 
                            ptype, pcols, pcnt, rpcnt, 
                            subptype, subpcols, subpartemp, subpcnt, rsubpcnt,
                            idx_flags, idx_type, idx_uk, orphaned_entries,
                            def_segment_creation, partial_idx,            
                            zonemap, attrcluster
                            -- read only
                            , numrodeftab, numrodefpart
                            , numropart, numrosubpart
                            -- partitioned external tables
                            , exttab, exttype
                            -- some debug info
                            , owner, name
                     from
                     ( select /*+ full(o) */ o.obj#, i.bo#, p.obj# pobj#, 
                       u.name owner, o.name name,
                       decode(o.type#,1,'I',2,'T',null) IDX_OR_TAB, 
                       is_xml ||
                       -- tracking of JSON partitioned tables, similar to XML tracking
                       -- e.g. prefixing the partitioning type
                       case when bitand(p.flags,4194304)=4194304 then 'JSON-' end ||
                       -- introducing abbreviations
                       -- I: interval, R: range, H: hash, L: list, S: system
                       -- RF: reference, IR: interval-ref, AL: auto list
                       -- (P): parent of reference partitioned table
                       decode(p.parttype, 1, case when bitand(p.flags,64)=64 then 
                                                 -- INTERVAL-REF, 12c
                                                 case when bitand(p.flags,32)=32 then 'IR'
                                                      else 'I' end 
                                                 else 'R' end 
                                         ,2, 'H', 3, 'S' 
                                                 -- AUTO LIST, 12.2
                                                 ,4, case when bitand(p.flags,64)=64 
                                                          then 'AL'
                                                          else 'L' end
                                                ,5, 'RF'
                                         ,p.parttype||'-?') ||
                      decode(bitand(p.flags,32),32,' (P)') PTYPE,
                       -- introducing abbreviations
                       -- I: interval, R: range, H: hash, L: list, S: system
                       -- RF: reference, IR: interval-ref, AL: auto list
                       -- (P): parent of reference partitioned table 
                      decode(mod(p.spare2, 256), 0, null
                                                   , 1, case when bitand(p.flags, 32768) = 32768
                                                             then 'I'
                                                             else 'R' end
                                                    , 2, 'H', 3,'S' 
                                                    -- AUTO LIST, 12.2
                                                    ,4, case when bitand(p.flags,32768)=32768 
                                                             then 'AL'
                                                             else 'L' end
                                                    , 5, 'RF' 
                                                    , p.spare2||'-?') SUBPTYPE,
                      p.partcnt PCNT, 
                      -- interval subpartitioning
                      -- overloading default subpartitioning count with max number of
                      -- subpartitions. default subpartition count does not really make sense
                      -- for interval subpartition since subpartitions will be created on demand
                      case when (mod(p.spare2, 256) = 1 and bitand(p.flags, 32768) = 32768)
                           then 1048575
                      else
                           mod(trunc(p.spare2/65536), 65536) 
                      end SUBPCNT,
                      to_char(p.partkeycols) || vc.vc_p PCOLS, 
                      to_char(case mod(trunc(p.spare2/256), 256) 
                           when 0 then null 
                           else mod(trunc(p.spare2/256), 256) end)||vc.vc_sp SUBPCOLS,
                      case when bitand(p.flags,1) = 1 then 
                                case when bitand(p.flags,2) = 2 then 'LP'
                                      else 'L' end
                           when bitand(p.flags,2) = 2 then 'GP' 
                      end IDX_FLAGS,
                      -- introducing abbreviation
                      -- N: normal, /R: reverse, B: bitmap, C: cluster, IT: iot - top
                      -- IN: iot nested, S: secondary, A: ansi, L: lob, -F: function-based
                      -- D: domain
                      decode(i.type#, 1, 'N'||
                                          decode(bitand(i.property, 4), 0, '', 4, '/R'),
                                      2, 'B', 3, 'C', 4, 'IT',
                                      5, 'IN', 6, 'S', 7, 'A', 8, 'L',
                                      9, 'D')  || 
                                      case when bitand(i.property,16) = 16 
                                           then '-F' end IDX_TYPE,
                      decode(i.property, null,null,
                                         decode(bitand(i.property, 1), 0, 'NU', 
                                                                       1, 'U', '?')) IDX_UK,
                      -- real partition and subpartition count
                      case when bitand(p.flags,64)=64 then op.xnumpart else p.partcnt end RPCNT,
                      osp.numsubpart RSUBPCNT,
                      -- deferred segments
                      case o.type# 
                      when 1 then --index
                        decode(ip_seg_off,null,isp_seg_off,ip_seg_off)   
                      when 2 then --table
                        decode(tp_seg_off,null,tsp_seg_off,tp_seg_off)
                      else null end DEF_SEGMENT_CREATION,
                      -- partial indexing
                      -- this is overloaded functionality, showing different values for tables
                      -- and indexes
                      -- tables:   ON | OFF
                      -- indexes:  P: partial, F: full index
                      -- in addition it is overloaded with the number of [subb]partitions
                      -- with indexing off FOR PARTITIONED INDEXES. Null for nonpartitioned ones
                      case o.type# 
                      when 1 then --index
                         decode(bitand(i.flags, 8388608), 8388608, 'P', 'F')||'-'||
                        -- overload field with count of all [sub]partitions with indexing off
                        decode(ip_idx_off,null,isp_idx_off,ip_idx_off)
                      when 2 then --table
                        decode(bitand(p.flags,8192),8192,'OFF','ON')||'-'||
                        -- overload field with count of all [sub]partitions with indexing off
                        decode(tp_idx_off,null,tsp_idx_off,tp_idx_off)
                      else null end PARTIAL_IDX,
                      null ORPHANED_ENTRIES,
                      decode(zonemap,null,'N',zonemap) ZONEMAP, 
                      decode(attrcluster,null,'N',attrcluster) ATTRCLUSTER,
                      st_part SUBPARTEMP
                      -- 12.2 read only partition counts
                      , decode(bitand(p.flags, 65536), 65536, 1, 0) NUMRODEFTAB -- table default read only
                      , numrodefpart -- partitions default read only
                      , numropart, numrosubpart -- real counts of read only [sub]partitions
                      , exttab 
                      , decode(exttype,'ORACLE_LOADER','LD','ORACLE_HIVE','HI',
                                       'ORACLE_HDFS','HD','ORACLE_DATAPUMP','DP',exttype) as exttype 
                      from partobj$ p, obj$ o, user$ u, ind$ i, 
                           ( select distinct obj#, 'XML-' as is_xml from opqtype$ where type=1) xml,
                           -- real subpartition count for tables and indexes
                           ( select /* NO_MERGE FULL(tsp) FULL(tcp) */ tcp.bo#, count(*) numsubpart
                             -- default read only partitions (none counts as no)
                             , max(tcp.numrodefpart) numrodefpart
                             -- read only subpartition count
                             , sum(decode(bitand(tsp.flags, 67108864), 67108864, 1, 0)) numrosubpart
                             from tabsubpart$ tsp, 
                                  (select obj#, bo#, spare2,
                                          sum(decode(bitand(spare2, 12288), 4096, 1, 0))
                                          over (partition by bo#)  numrodefpart
                                   from tabcompart$) tcp 
                             where tcp.obj# = tsp.pobj# 
                             group by tcp.bo#
                             union all
                             select /* NO_MERGE FULL(isp) FULL(icp) */ icp.bo#, count(*) numsubpart
                             -- dummy for read only info
                             , null, null 
                             from indsubpart$ isp, indcompart$ icp 
                             where icp.obj# = isp.pobj# 
                             group by icp.bo#) osp,
                           -- real partition count for tables and indexes
                           ( select tp.bo#, count(*) xnumpart
                             -- read only partitions count
                             , sum(decode(bitand(tp.flags, 67108864), 67108864, 1, 0)) numropart            
                             from tabpart$ tp
                             group by tp.bo#
                             union all
                             select ip.bo#, count(*) xnumpart
                             -- dummy for read only partitions
                             , 0
                             from indpart$ ip
                             group by ip.bo#) op,                            
                           -- details table partitions: partial indexing and deferred segments
                           ( select tp.bo#,
                                    -- number or partitions with indexing off 
                                    sum(decode(bitand(tp.flags, 2097152), 2097152, 1, 0))  tp_idx_off,
                                    -- number or partitions with deferred segment creation
                                    sum(decode(bitand(tp.flags, 65536), 65536, 1, 0))  tp_seg_off
                             from tabpart$ tp
                             group by tp.bo#) pxd,
                           -- details table subpartitions: partial indexing and deferred segments
                           ( select tcp.bo#, 
                                    -- number or subpartitions with indexing off 
                                    sum(decode(bitand(tsp.flags, 2097152), 2097152, 1, 0))  tsp_idx_off,
                                    -- number or subpartitions with deferred segment creation
                                    sum(decode(bitand(tsp.flags, 65536), 65536, 1, 0))  tsp_seg_off
                             from tabsubpart$ tsp, tabcompart$ tcp 
                             where tcp.obj# = tsp.pobj#
                             group by tcp.bo#) spxd,
                           -- details index partitions: partial indexing and deferred segments
                           ( select ip.bo#,
                                    -- number or partitions with indexing off 
                                    sum(decode(bitand(ip.flags, 1), 1, 1, 0))  ip_idx_off,
                                    -- number or partitions with deferred segment creation
                                    sum(decode(bitand(ip.flags, 65536), 65536, 1, 0))  ip_seg_off
                             from indpart$ ip
                             group by ip.bo#) ipd,
                           -- details index subpartitions: partial indexing and deferred segments
                           ( select icp.bo#, 
                                    -- number or subpartitions with indexing off 
                                    sum(decode(bitand(isp.flags, 1), 1, 1, 0))  isp_idx_off,
                                    -- number or subpartitions with deferred segment creation
                                    sum(decode(bitand(isp.flags, 65536), 65536, 1, 0))  isp_seg_off
                             from indsubpart$ isp, indcompart$ icp 
                             where icp.obj# = isp.pobj#
                             group by icp.bo#) ispd,
                           -- attribute clustering
                           ( select c.clstobj#, 'Y-'||
                                    -- kind of attribute clustering  
                                    case when ctable is not null 
                                         then 'MT-' else 'ST-' end
                                    ||
                                    case when clstfunc = 1 then 'I-'     -- interleaved
                                         else 'L-' end     -- linear
                                    ||to_char(decode(ctable, null,0,ctable)+1)||'-'||ccol as ATTRCLUSTER
                             from sys.clst$ c, 
                                  -- count of tables and columns used for attribute clustering
                                  -- no detailed breakdown of columns per row
                                  -- table count does not include fact table for hierarchical attr. clustering
                                  ( select clstobj#, count(intcol#) ccol 
                                    from sys.clstkey$
                                    group by clstobj#) k,
                                  ( select clstobj#, count(*) ctable
                                    from sys.clstjoin$
                                    group by clstobj#) kt
                             where c.clstobj# = k.clstobj#
                             and   c.clstobj# = kt.clstobj#(+)) attrcl,
                            -- zone maps
                            (select detailobj#, zonemap from
                                (select sd.detailobj#, flags, 'Y-'||        
                                        -- single table zonemap or hierarchical zonemap
                                        decode(bitand(sn.flag3, 1024),
                                               0, 'ST', 'MT') ||
                                               -- number of tables and columns in zonemap (aggr, no detailed breakdown)
                                               -- table count does not include fact table for hierarch. zonemap
                                               '-'||  count(distinct sd.detailobj#) over (partition by sd.sumobj#) ||
                                               '-'||  sa.zmcol as ZONEMAP 
                                 from sys.sumdetail$ sd, sys.sum$ s, sys.snap$ sn,
                                      ( select sumobj#, count(*) zmcol
                                        from sys.sumagg$ 
                                        where aggfunction = 18
                                        group by sumobj#) sa
                                 where s.obj# = sd.sumobj# 
                                 and   s.obj# = sa.sumobj#
                                 and s.containernam(+) = sn.vname) v
                             where bitand(v.flags, 2) = 2      /* zonemap fact table */
                           ) zm,
                           ( select bo#, count(*) st_part 
                             from defsubpart$ 
                             group by bo# ) spt,
                           -- partitioned external tables
                           ( select obj#, 'Y' exttab, type$ exttype
                             from external_tab$ ) xt,
                           -- virtual column detection
                           (select obj#, case when p_cnt is not null 
                                         then '-VC('||p_cnt||')' 
                                         else null end as vc_p, 
                                         case when sp_cnt is not null 
                                         then '-VC('||sp_cnt||')' 
                                         else null end as vc_sp
                                         from (select pc.obj#, lvl, count(*) cnt 
                                               from (select obj#, 'P' lvl, intcol#  
                                                     from partcol$ pc
                                                     union all 
                                                     select obj#, 'SP' lvl, intcol#
                                                     from subpartcol$) pc,
                                                    (select obj#, intcol# from col$
                                                     where bitand(property, 8) = 8) c
                                               where pc. obj# = c.obj# and pc.intcol# =c.intcol#
                                              group by pc.obj#, lvl)
                                         pivot (max(cnt) cnt 
                                                for lvl in ('P' as P,'SP' as SP))) vc
                      where o.obj# = i.obj#(+)
                      and   o.owner# = u.user# 
                      and   p.obj# = o.obj#
                      and   p.obj# = xml.obj#(+)
                      and   p.obj# = osp.bo#(+) 
                      and   p.obj# = op.bo#(+)
                      and   p.obj# = pxd.bo#(+)
                      and   p.obj# = spxd.bo#(+)
                      and   p.obj# = ipd.bo#(+)
                      and   p.obj# = ispd.bo#(+)
                      and   p.obj# = spt.bo#(+)
                      and   o.obj# = attrcl.clstobj#(+)
                      and   o.obj# = zm.detailobj#(+)
                      and   o.obj# = xt.obj#(+)
                      and   o.obj# = vc.obj#(+)
                      -- bug 14369338, exclude AUDSYS
--                      and   u.name not in ('SYS','SYSTEM','SH','SYSMAN','AUDSYS')
                      -- bug 12573239
                      -- exclude RMAN catalog usage, identified by schema 
                      -- (introduced with ZDLRA, 12.1.0.2)
--                      and u.name not in (select u.name 
--                                         from sysauth$ sa join user$ u 
--                                         on (sa.grantee# = u.user#) join user$ u2 
--                                         on (sa.privilege# = u2.user#) 
--                                         where u2.name = 'RECOVERY_CATALOG_OWNER')
                      -- bug 12573239
                      -- exclude Spatial topology tables and indexes
                      -- SDO metadata does not have any unique identifier like obj# 
                      -- but only owner and table name. This would make a very expensive 
                      -- query joing back to obj$ and user$ to uniquely identify 
                      -- partitioned indexes through i.bo#, so we are using 
                      -- an approximation by filtering on the prefix of the 
                      -- auto-generated index names ... 
--                      and not exists (select 1 
--                                      from mdsys.sdo_topo_metadata_table sdo 
--                                      where u.name = sdo.sdo_owner 
--                                      and o.name like topology||'%')
                      -- fix bug 3074607 - filter on obj$
                      and o.type# in (1,2,19,20,25,34,35)
                      -- exclude flashback data archive tables
                      -- fix bug 14666795
                      -- crystal clear identification of FBA tables deemed as too expensive
                      -- would require a probe against tab$             
                      -- e,g. o.obj# not in (select obj# from tab$ where bitand(property,8589934592)=8589934592)
--                      and o.name not like 'SYS_FBA%'
                      -- exclude change tables
--                      and o.obj# not in ( select obj# from cdc_change_tables$)
                      -- exclude local partitioned indexes on change tables
--                      and i.bo# not in  ( select obj# from cdc_change_tables$)
                union all
                -- global nonpartitioned indexes on partitioned tables
                select o.obj#, i.bo#, p.obj# pobj#,
                       u.name owner, o.name name,
                       'I' IDX_OR_TAB,
                        null,null,null,null,
                        to_char(case cols when 0 then null
                                  else cols end) PCOLS,
                        null SUBPCOLS,
                       'GNP' IDX_FLAGS, 
                      -- introducing abbreviation
                      -- N: normal, /R: reverse, B: bitmap, C: cluster, IT: iot - top
                      -- IN: iot nested, S: secondary, A: ansi, L: lob, -F: function-based
                      -- D: domain
                       decode(i.type#, 1, 'N'||
                                      decode(bitand(i.property, 4), 0, '', 4, '/R'),
                                      2, 'B', 3, 'C', 4, 'IT',
                                      5, 'IN', 6, 'S', 7, 'A', 8, 'L',
                                      9, 'D') ||
                       case when bitand(i.property,16) = 16 then '-F' end IDX_TYPE,
                       decode(i.property, null,null,
                                          decode(bitand(i.property, 1), 0, 'NU', 
                                          1, 'U', '?')) IDX_UK,
                       null, null, 
                       null DEF_SEGMENT_CREATION,
                       -- P: partial, F: full
                       decode(bitand(i.flags, 8388608), 8388608, 'P', 'F') PARTIAL_IDX,
                       decode(bitand(i.flags, 268435456), 268435456, 'YES', 'NO') ORPHANED_ENTRIES,
                       NULL ZONEMAP, NULL ATTRCLUSTER,
                       NULL SUBPARTEMP
                       , NULL NUMDEFROTAB
                       , NULL NUMDEFROPART
                       , NULL NUMROPART
                       , NULL NUMROSUBPART
                       , NULL EXTTAB
                       , NULL EXTTYPE
                from partobj$ p, user$ u, obj$ o, ind$ i
                where p.obj# = i.bo#
                -- exclude global nonpartitioned indexes on change tables
--                and   i.bo# not in  ( select obj# from cdc_change_tables$)
                -- exclude flashback data archive tables
--                and   o.name not like 'SYS_FBA%'
                -- bug 12573239
                -- exclude global indexes on Spatial topology tables
--                and o.name not in (select topology||'_RELATION$' 
--                                   from mdsys.sdo_topo_metadata_table sdo 
--                                   where u.name = sdo.sdo_owner)
                and   o.owner# = u.user# 
                and   p.obj# = o.obj# 
                -- nonpartitioned index
                and   bitand(i.property, 2) <>2
                -- bug 14369338, exclude AUDSYS
--                and   u.name not in ('SYS','SYSTEM','SH','SYSMAN','AUDSYS')
                -- bug 12573239
                -- exclude RMAN catalog usage, identified by schema (introduced with ZDLRA, 12.1.0.2)
--                and u.name not in (select u.name 
--                                   from sysauth$ sa join user$ u 
--                                   on (sa.grantee# = u.user#) join user$ u2 on (sa.privilege# = u2.user#) 
--                                   where u2.name like 'RECOVERY_CATALOG_OWNER')
                )
                order by num, idx_or_tab desc)) LOOP 

     if (is_used = 0) then
       is_used:=1;
     end if;  

     clob_rest := clob_rest||crec.my_string;
   end loop;

   if (is_used = 1) then
     select pcnt into data_ratio
     from
     (
       SELECT c1, TRUNC((ratio_to_report(sum_blocks) over())*100,2) pcnt  
       FROM
       (
        select decode(p.obj#,null,'REST','PARTTAB') c1, sum(s.blocks) sum_blocks
        from tabpart$ p, seg$ s
        where s.file#=p.file#(+)
        and s.block#=p.block#(+)
        and s.type#=5
        group by  decode(p.obj#,null,'REST','PARTTAB')
        )
      )
      where c1 = 'PARTTAB';
   end if;
end;
/


/***************************************************************
 * DBMS_FEATURE_ZMAP
 *  The procedure to detect usage for Zone maps
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_zmap
      (is_used OUT number, zmap_ratio OUT number, clob_rest OUT clob)
AS
BEGIN
  -- initialize
  is_used := 0;
  zmap_ratio := 0;
  clob_rest := '{ "data": [';

FOR crec IN (SELECT 
       -- zone map existent
       DECODE(xx.sumobj#,NULL,'N','Y') ||
       DECODE(xx.clstobj#,NULL,'N','Y') zmap_used,
       '{ ' ||
       -- zone map existent
       CASE WHEN xx.sumobj# IS NOT NULL THEN
         '"zmap": { '       || 
--         '"zmap":"'       || DECODE(xx.sumobj#,NULL,'N','Y')  ||'",' ||
         '"zm_w_attrcl":"' || DECODE(bitand(xx.cflags,8),8,'Y','N') ||'",' ||
         '"joined_zmap":"' || DECODE(bitand(xx.xpflags,137438953472),137438953472,'Y','N') || '",' ||
         '"zm_scale":'     || zmapscale     || ','||
         '"num_joins":'    || numjoins      || ','||
         '"num_tabs":'     || numdetailtab  || ','||
         '"num_cols":'     || (numaggregates - numjoins-1)/2 || ','||
         '"refresh_mode":"'|| DECODE(bitand(mflags,65536),65536,'C', 
                                     DECODE(bitand(mflags,34359738368+68719476736),34359738368+68719476736,'L+M', 
                                            34359738368,'L', 68719476736,'M','D'))    || '",'||
         '"disabled":"'    || DECODE(bitand(mflags,4),4,'Y','N')                      || '",'||
         '"unusable":"'    || DECODE(bitand(mflags,17179869184),17179869184,'Y','N')  || '",'||
         '"invalid":"'     || DECODE(bitand(mflags,64),64,'Y','N')                    || '",'||
         '"stale":"'       || DECODE(bitand(mflags,16+32),0,'Y','N')                  || '"},'
       END ||
       -- attribute clustering existent
       CASE WHEN xx.clstobj# IS NOT NULL THEN
         '"attrcl":{ '      ||
--         '"attrcl":"'      || DECODE(xx.clstobj#,NULL,'N','Y') ||'",' ||
         '"clst_type":"'    || DECODE(clstfunc,1,'I','L')                                 || '",'||
         '"clst_mode":"'    || DECODE(bitand(xx.cflags,1+2),1+2,'L+M',1,'L',2,'M','OFF')  || '",'||
         '"clst_dim":'      || (SELECT COUNT(*) FROM clstdimension$ d 
                                WHERE d.clstobj#(+)=xx.clstobj#)                          || ','||
         '"clst_grp":'     || (SELECT COUNT(DISTINCT groupid) FROM clstkey$ k
                                       WHERE k.clstobj#(+)=xx.clstobj#)                   || ','||
         '"clst_cols":'     || (SELECT COUNT(*) FROM clstkey$ k 
                                        WHERE k.clstobj#(+)=xx.clstobj#)                  || ','||
         '"clst_invalid":"' || DECODE(bitand(xx.flags,4),4,'Y','N')                       || '"},'
       END ||
       '"tab":{'    ||
       CASE 
       WHEN bitand(xx.property,32) = 32 THEN
         '"parttab":"Y"'
       -- nonpartitioned tables with existing segments
       WHEN s.block# IS NOT NULL THEN
          '"parttab":"N",' ||
           '"imc_prio":"'      ||
           DECODE(bitand(s.spare1, 4294967296), 4294967296, 
                 DECODE(bitand(s.spare1, 34359738368), 34359738368, 
                        DECODE(bitand(s.spare1, 3848290697216), 549755813888,'LOW',
                                                               1099511627776,'MEDIUM', 
                                                               2199023255552,'HIGH',
                                                               3298534883328,'CRITICAL','NONE'),'NONE'),'-')  || '",'||
          '"imc_dist":"'       ||
          DECODE(bitand(s.spare1, 4294967296), 4294967296, 
                 DECODE(bitand(s.spare1, 8589934592),0,'DUPLICATE','AUTO DIST'),'-')                          || '",'||
          '"imc_comp":"'       ||
          DECODE(bitand(s.spare1, 4294967296), 4294967296, 
                 DECODE(bitand(s.spare1, 274877906944), 0, 
                       DECODE(bitand(s.spare1,17179869184),0,'BASIC','QUERY'), 
                              DECODE(bitand(s.spare1,17179869184),0,'CAP LOW','CAP HIGH')),'-') ||'"' 
          ELSE
          '"parttab":"N"' 
      END 
      || '} }' zmap_obj
FROM (SELECT * 
      FROM (SELECT sumobj#, clstobj#, detailobj#, mflags, c.flags as cflags, clstfunc, numjoins, 
                   numaggregates, numdetailtab, zmapscale, xpflags
            FROM ( SELECT sumobj#, detailobj#, mflags, numjoins, numaggregates, numdetailtab,
                          zmapscale, xpflags        
                   FROM sum$ s 
                            RIGHT OUTER JOIN sumdetail$ sd
                            ON (s.obj# = sd.sumobj#)
                            WHERE bitand(s.xpflags,34359738368)=34359738368
                            AND bitand(sd.flags,2) = 2
                  ) zm 
            -- to accommodate either-or for zone maps or attribute clustering      
            FULL OUTER JOIN clst$ c
            ON (zm.detailobj# = c.clstobj#)
            ) x
            JOIN tab$ t
            ON (t.obj#=DECODE(x.clstobj#,NULL,x.detailobj#,x.clstobj#))
     ) xx
     -- needed for deferred segments and partitioned objects
     LEFT OUTER JOIN seg$ s
     ON (xx.file#  = s.file#
     AND xx.block# = s.block#
     AND xx.ts#    = s.ts#)) loop
     
     -- overloading of zonemap tracking with basic attribute clustering tracking
     IF (is_used = 0 AND crec.zmap_used != 'NN') THEN
       is_used:=1;
     END IF;  

 clob_rest := clob_rest || crec.zmap_obj ||', ';
 END LOOP;

 clob_rest := substr(clob_rest,1,length(clob_rest)-2) ||' ] }';

   IF (is_used = 1) THEN
      -- ratio of tables with zone maps versus total # of tables in percent
      -- if AUX_COUNT=0 then only attribute clustering is used
      SELECT (COUNT(detailobj#)/COUNT(obj#))*100 INTO zmap_ratio   
      FROM ( SELECT detailobj#                
             FROM sum$ s 
             JOIN sumdetail$ sd
       ON (s.obj# = sd.sumobj#)
       WHERE bitand(s.xpflags,34359738368)=34359738368
       AND bitand(sd.flags,2) = 2
       ) zm
      RIGHT OUTER JOIN tab$ t
      ON (t.obj#=zm.detailobj#); 
   END IF;

 END;
 /


/***************************************************************
 * DBMS_FEATURE_PLSQL_NATIVE
 *  The procedure to detect usage for PL/SQL Native
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_plsql_native (
  o_is_used     OUT           number,
  o_aux_count   OUT           number, -- not used, set to zero
  o_report      OUT           clob )

  --
  -- Find ncomp usage from ncomp_dll$
  --
  -- When >0 NATIVE units, sets "o_is_used=1". Always generates XML report,
  -- for example...
  --
  -- <plsqlNativeReport date ="04-feb-2003 14:34">
  -- <owner name="1234" native="2" interpreted="1"/>
  -- <owner name="1235" native="10" interpreted="1"/>
  -- <owner name="CTXSYS" native="118"/>
  -- ...
  -- <owner name="SYS" native="1292" interpreted="6"/>
  -- <owner name="SYSTEM" native="6"/>
  -- ...
  -- <owner name="XDB" native="176"/>
  -- </plsqlNativeReport>
  --

is
  YES      constant number := 1;
  NO       constant number := 0;
  NEWLINE  constant varchar2(2 char) := '
';
  v_date   constant varchar2(30) := to_char(sysdate, 'dd-mon-yyyy hh24:mi');
  v_report          varchar2(400); -- big enough to hold one "<owner .../>"
begin

  o_is_used   := NO;
  o_aux_count := 0;
  o_report    := '<plsqlNativeReport date ="' || v_date || '">' || NEWLINE;

  -- For security and privacy reasons, we do not collect the names of the
  -- non-Oracle schemas. In the case statement below, we filter the schema
  -- names against v$sysaux_occupants, which contains the list of Oracle
  -- schemas.
  for r in (select (case when u.name in
                              (select distinct schema_name
                                 from v$sysaux_occupants)
                         then u.name
                         else to_char(u.user#)
                    end) name,
              count(o.obj#) total, count(d.obj#) native
              from user$ u, ncomp_dll$ d, obj$ o
              where o.obj# = d.obj# (+)
                and o.type# in (7,8,9,11,12,13,14)
                and u.user# = o.owner#
              group by u.name, u.user#
              order by u.name) loop
    if (r.native > 0) then
      o_is_used := YES;
    end if;
    v_report := '<owner name="'|| r.name || '"';
    if (r.native > 0) then
      v_report := v_report || ' native="' || r.native || '"';
    end if;
    if (r.total > r.native) then
      v_report := v_report || ' interpreted="' || (r.total - r.native) || '"';
    end if;
    v_report := v_report || '/>' || NEWLINE;
    o_report := o_report || v_report;
  end loop;
  o_report := o_report || '</plsqlNativeReport>';
end dbms_feature_plsql_native;
/

/*******************************************************************
 * DBMS_FEATURE_QOSM
 *  The procedure to detect usage for Quality of Service Management
 *******************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_qosm
      (is_used OUT number, aux_count OUT number, feature_info OUT clob)
AS
  managed_cumtime     interval day(9) to second(0);
  measureonly_cumtime interval day(9) to second(0);
  monitor_cumtime     interval day(9) to second(0);
  enabled             number;
  prevmode            number;
  curmode             number;
  maxpc               number;
  curnumpc            number;
  managed             number;
  measureonly         number;
  monitor             number;
BEGIN
  -- initialize
  feature_info := NULL;
  aux_count := NULL;
  enabled := 0;

  -- get number of performance classes
  select count(*) into is_used from x$kywmpctab
    where kywmpctabsp not like ':%';

  execute immediate
  'select used,curmode,prevmode,maxpc,curnumpc,managed_cumtime,' ||
     'measureonly_cumtime,monitor_cumtime,managed,measureonly,' ||
     'monitor from appqossys.wlm_feature_usage'
   into
     enabled,curmode,prevmode,maxpc,curnumpc,managed_cumtime,
     measureonly_cumtime,monitor_cumtime,managed,measureonly,monitor;

  -- if QOSM is used
  if (is_used >= 1) then
    -- number of Performance Classes
    aux_count := is_used;
  end if;

  if (enabled > 0) then
    is_used := 1;
    feature_info := to_clob(
      'Curmode '    || to_char(curmode) ||
      ', Prevmode ' || to_char(prevmode) ||
      ', MaxNumPC ' || to_char(maxpc) ||
      ', CurNumPC ' || to_char(curnumpc) ||
      ', Managed_cumtime ' || to_char(managed_cumtime) ||
      ', MO_cumtime '  || to_char(measureonly_cumtime) ||
      ', MON_cumtime ' || to_char(monitor_cumtime) ||
      ', Managed '  || to_char(managed) ||
      ', MeasureOnly ' || to_char(measureonly) ||
      ', Monitor '  || to_char(monitor));
  end if;
END;
/
show errors;

/*******************************************************************
 * DBMS_FEATURE_RACONENODE
 *  The procedure to detect usage for RAC One Node
 *******************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_rond
      (is_used OUT number, aux_count OUT number, feature_info OUT clob)
AS
  is_rac_on number;
  d_type varchar2(15);
  q_status number;
  ops_enabled number;
BEGIN
  is_used := 0;
  aux_count := 0;
  feature_info := NULL;
  d_type := NULL;
  q_status := 0;
  ops_enabled := 0;

  select count(*) into ops_enabled from x$kjidt;

  if (ops_enabled > 0) then
    select kjidtv, kjidtqs into d_type, q_status from x$kjidt;
    if (q_status = 0) then
      feature_info := to_clob('Database is not in RAC');
    elsif (q_status = 1) then
      if (upper(d_type) = 'RACONENODE') then
        is_used := 1;
        feature_info := to_clob('Database is of type RACOneNode');
      elsif (upper(d_type) = 'RAC') then
        feature_info := to_clob('Database is of type RAC');
      elsif (upper(d_type) = 'SINGLE') then
        feature_info := to_clob('Database is of type SINGLE');
      end if;
    elsif (q_status = 2) then
      feature_info := to_clob('Database type query failed');
    elsif (q_status = 3) then
      feature_info := to_clob('Database type query returned warning');
    end if;
  else
    feature_info := to_clob('Database is not RAC One Node');
  end if;
END;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_RAC
 *  The procedure to detect usage for RAC
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_rac
      (is_used OUT number, nodes OUT number, clob_rest OUT clob)
AS
   cpu_count_current number;
   cpu_stddev_current number;
BEGIN
  -- initialize
  clob_rest := NULL;
  nodes := NULL;
  cpu_count_current := NULL;
  cpu_stddev_current := NULL;

  select count(*) into is_used from v$system_parameter where
     name='cluster_database' and value='TRUE';
   -- if RAC is used see if only active/passive or active/active
   if (is_used = 1) then
       select count(*) into nodes from gv$instance;
       select sum(cpu_count_current), round(stddev(cpu_count_current),1)
          into cpu_count_current, cpu_stddev_current from gv$license;
       -- active_instance_count init.ora has been deprecated
       --   so 'usage:Active Passive' will no longer be returned
       clob_rest:='usage:All Active:cpu_count_current:'||cpu_count_current
                ||':cpu_stddev_current:'||cpu_stddev_current;
  end if;
END;
/

/***************************************************************
 * DBMS_FEATURE_XDB
 *  The procedure to detect usage for XDB
 ***************************************************************/
/*
 * XDB is being used if user has created atleast 1 of the following
 ***** resource in XDB repositor, 
 ***** XML schema, 
 ***** table with XMLType column, or
 ***** view with XMLType column

 * Here is an example of what this procedure puts in OUT var feature_info
<xdb_feature_usage>
  <user_resources>       2 </user_resources>
  <user_schemas>         1 </user_schemas>
  <user_SB_columns>      1 </user_SB_columns>
  <user_NSB_columns>     7 </user_NSB_columns>
  <user_SB_views>        0 </user_SB_views>
  <user_NSB_views>       0 </user_NSB_views>
  <user_SB_columns_noidx> 1 </user_SB_columns_noidx>
  <user_NSB_columns_noidx>3 </user_NSB_columns_noidx>
  <user_SB_views_noidx>   0 </user_SB_views_noidx>
  <user_NSB_views_noidx>  0 </user_NSB_views_noidx>
  <user_OR_cols>         0 </user_OR_cols>
  <user_CLOB_cols>       6 </user_CLOB_cols>
  <user_BINARY_cols>     2 </user_BINARY_cols>
  <all_resconfigs>       8 </all_resconfigs>
  <all_acls>             4 </all_acls>
  <user_xml_SXIs>        1 </user_xml_SXIs>
  <user_xml_UXIs>        0 </user_xml_UXIs>
  <xml_search_idxes>     0 </xml_search_idxes>
</xdb_feature_usage>

 * Notes:
*/

create or replace procedure DBMS_FEATURE_XDB
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  num_xdb_res           number := 0;
  num_xdb_rc            number := 0;
  num_xdb_acl           number := 0;
  num_xdb_schemas       number := 0;
  num_sb_tbl            number := 0;
  num_xdb_tbl           number := 0;
  num_xdb_vw            number := 0;
  num_nsb_tbl           number := 0;
  num_sb_vw             number := 0;
  num_nsb_vw            number := 0;
  num_st_or             number := 0;
  num_st_lob            number := 0;
  num_st_clob           number := 0;
  num_st_bin            number := 0;
  num_xdb_sidx          number := 0;
  num_xdb_uidx          number := 0;
  num_xdb_tidx          number := 0;
  feature_usage         varchar2(1000);
  TYPE cursor_t         IS REF CURSOR;
  cursor_objtype        cursor_t;
  total_count           number := 0;
  flag                  number := 0;
  objtype               number := 0;
  num_sb_tbl_noidx       number := 0;
  num_nsb_tbl_noidx      number := 0;
  num_sb_vw_noidx        number := 0;
  num_nsb_vw_noidx       number := 0;
begin
    /* get number of non system resources from resource_view */
    execute immediate q'[select count(*) 
    from xdb.xdb$resource e, sys.user$ u 
    where to_number(utl_raw.cast_to_binary_integer(e.xmldata.ownerid)) = 
        u.user# and u.name not in (select distinct username from 
          dba_users where oracle_maintained = 'Y') and 
        u.name not in ('OE', 'SH', 'HR', 'SCOTT')
        and u.name not like 'APEX_%'
        and u.name not like 'FLOWS_%']'
        into num_xdb_res;

    /* get number of non system xml schemas registered */
    execute immediate q'[select count(*) 
    from dba_xml_schemas 
    where owner not in (select distinct username from dba_users 
                        where oracle_maintained = 'Y') 
     and owner not in ('OE', 'SH', 'HR', 'SCOTT') and owner not like 'APEX_%'
     and owner not like 'FLOWS_%']' into num_xdb_schemas ;
    
    /* count non system, SB and NSB xml columns */
       OPEN cursor_objtype FOR q'[
             select count(*), o.type#, bitand(p.flags, 2)
             from sys.opqtype$ p, sys.obj$ o, sys.user$ u
             where o.obj# = p.obj# and p.type = 1 and
                   (o.type# = 2 or o.type# = 4) and
                   o.owner# = u.user# and u.name not in 
                     (select distinct username from dba_users 
                      where oracle_maintained = 'Y')
                   and u.name not in ('OE', 'SH', 'HR', 'SCOTT') and
                   u.name not like 'APEX_%'
                   and u.name not like 'FLOWS_%'
             group by (bitand(p.flags, 2), o.type#)]';

        LOOP
          BEGIN
            FETCH cursor_objtype INTO total_count, objtype, flag;
            EXIT WHEN cursor_objtype%NOTFOUND;


            /* get number of non schema based tables */
            IF (flag = 0) and (objtype = 2) THEN
              num_nsb_tbl := total_count;
            END IF;

            /* get number of non schema based views */
            IF (flag = 0) and (objtype = 4) THEN
              num_nsb_vw := total_count;
            END IF;

            /* get number of schema based tables */
            IF (flag = 2) and (objtype = 2) THEN
              num_sb_tbl := total_count;
            END IF;

            /* get number of schema based views */
            IF (flag = 2) and (objtype = 4) THEN
              num_sb_vw := total_count;
            END IF;
          END;
        END LOOP;

    num_xdb_vw := num_nsb_vw + num_sb_vw;
    num_xdb_tbl := num_nsb_tbl + num_sb_tbl;

    /* count user created SB and NSB xml columns */
       OPEN cursor_objtype FOR q'[
             select count(*), o.type#, bitand(p.flags, 2)
             from sys.opqtype$ p, sys.obj$ o, sys.user$ u
             where o.obj# = p.obj# and p.type = 1 and
                   (o.type# = 2 or o.type# = 4) and
                   o.owner# = u.user# and u.name not in 
                     (select distinct username from dba_users 
                      where oracle_maintained = 'Y')
                   and u.name not in ('OE', 'SH', 'HR', 'SCOTT') and
                   u.name not like 'APEX_%'
                   and u.name not like 'FLOWS_%'
                   and (bitand(p.flags, 1) = 1 or bitand(p.flags, 4) = 4 or
                        bitand(p.flags, 68) = 68 or 
                        bitand(p.flags, 1024) = 1024)
             group by (bitand(p.flags, 2), o.type#)]';

        LOOP
          BEGIN
            FETCH cursor_objtype INTO total_count, objtype, flag;
            EXIT WHEN cursor_objtype%NOTFOUND;

            /* get number of non schema based tables */
            IF (flag = 0) and (objtype = 2) THEN
              num_nsb_tbl_noidx := total_count;
            END IF;

            /* get number of non schema based views */
            IF (flag = 0) and (objtype = 4) THEN
              num_nsb_vw_noidx := total_count;
            END IF;

            /* get number of schema based tables */
            IF (flag = 2) and (objtype = 2) THEN
              num_sb_tbl_noidx := total_count;
            END IF;

            /* get number of schema based views */
            IF (flag = 2) and (objtype = 4) THEN
              num_sb_vw_noidx := total_count;
            END IF;
          END;
        END LOOP;

    if (num_xdb_res > 0) or (num_xdb_schemas > 0) or
        (num_xdb_vw > 0) or (num_xdb_tbl > 0) then

        /* xdb is being used by user */
        OPEN cursor_objtype FOR q'[
             select count(*), bitand(p.flags, 69)
             from sys.opqtype$ p, sys.user$ u, sys.obj$ o
             where p.type = 1 and 
                  (bitand(p.flags, 1) = 1 or bitand(p.flags, 4) = 4 or 
                   bitand(p.flags, 68) = 68) and
                  p.obj# = o.obj# and
                  o.owner# = u.user# and u.name not in 
                    (select distinct username from dba_users 
                     where oracle_maintained = 'Y') 
                  and u.name not in ('OE', 'SH', 'HR', 'SCOTT') and 
                  u.name not like 'APEX_%' 
                  and u.name not like 'FLOWS_%'                
             group by (bitand(p.flags, 69))]';

        LOOP 
          BEGIN
            FETCH cursor_objtype INTO total_count, flag;
            EXIT WHEN cursor_objtype%NOTFOUND;

            /* get number of xmltype columns stored as object */
            IF flag = 1 THEN 
              num_st_or := total_count; 
            END IF;

            /* get number of xmltype columns stored as lob */
            IF flag = 4 THEN
              num_st_clob := total_count;
            END IF;

            /* get number of xmltype columns stored as binary */
            IF flag = 68 THEN
              num_st_bin := total_count;
            END IF;
          END;
        END LOOP;

        /* get number of resconfigs */ 
        execute immediate 'select count(*) from xdb.xdb$resconfig' into 
                                                        num_xdb_rc;
        /* get number of acls */ 
        execute immediate 'select count(*) from xdb.xdb$acl' into 
                                                        num_xdb_acl;

        /* get number of structured xml indexes */
        execute immediate q'[select count(1) from dba_xml_indexes 
                     where index_type = 'STRUCTURED' and 
                     index_owner not in
                     (select distinct username from dba_users 
                      where oracle_maintained = 'Y')
                     and index_owner not in ('OE', 'SH', 'HR', 'SCOTT')]'
                into num_xdb_sidx;
        
        /* get number of unstructured xml indexes */
        execute immediate q'[select count(1) from dba_xml_indexes
                     where index_type = 'UNSTRUCTURED' and 
                     index_owner not in
                     (select distinct username from dba_users 
                      where oracle_maintained = 'Y')
                     and index_owner not in ('OE', 'SH', 'HR', 'SCOTT')]'
                 into num_xdb_uidx;        

        /* get number of xml search indexes */
        execute immediate q'[select count(1) from ctxsys.dr$index i, 
                     sys.user$ u where i.idx_id in (select ixv_idx_id from
                     ctxsys.dr$index_value where IXV_OAT_ID = 50814) and 
                     i.idx_owner# = u.user# and u.name not in
                     (select distinct username from dba_users 
                      where oracle_maintained = 'Y') 
                     and u.name not in ('OE', 'SH', 'HR', 'SCOTT')]'
                     into num_xdb_tidx;

        feature_boolean := 1;
        aux_count := 0; 

        feature_usage := chr(10) ||
           '<xdb_feature_usage>'||
                chr(10)||chr(32)||chr(32)||
                '<user_resources>       '|| to_char(num_xdb_res)  || 
                ' </user_resources>'||
                chr(10) ||chr(32)||chr(32)||
                '<user_schemas>         '|| to_char(num_xdb_schemas) || 
                ' </user_schemas>'||
                chr(10)||chr(32)||chr(32)||
                '<user_SB_columns>      '|| to_char(num_sb_tbl)   || 
                ' </user_SB_columns>'||
                chr(10)||chr(32)||chr(32)||
                '<user_NSB_columns>     '|| to_char(num_nsb_tbl)  || 
                ' </user_NSB_columns>'||
                chr(10)||chr(32)||chr(32)||
                '<user_SB_views>        '|| to_char(num_sb_vw)    || 
                ' </user_SB_views>'||
                chr(10)||chr(32)||chr(32)||
                '<user_NSB_views>       '|| to_char(num_nsb_vw)   || 
                ' </user_NSB_views>'||
                chr(10)||chr(32)||chr(32)||
                '<user_SB_columns_noidx> '|| to_char(num_sb_tbl_noidx) || 
                ' </user_SB_columns_noidx>'||
                chr(10)||chr(32)||chr(32)||
                '<user_NSB_columns_noidx>'|| to_char(num_nsb_tbl_noidx) || 
                ' </user_NSB_columns_noidx>'||
                chr(10)||chr(32)||chr(32)||
                '<user_SB_views_noidx>   '|| to_char(num_sb_vw_noidx)   || 
                ' </user_SB_views_noidx>'||
                chr(10)||chr(32)||chr(32)||
                '<user_NSB_views_noidx>  '|| to_char(num_nsb_vw_noidx)  || 
                ' </user_NSB_views_noidx>'||
                chr(10)||chr(32)||chr(32)||
                '<user_OR_cols>         '|| to_char(num_st_or)    || 
                ' </user_OR_cols>'||
                chr(10)||chr(32)||chr(32)||
                '<user_CLOB_cols>       '|| to_char(num_st_clob)  || 
                ' </user_CLOB_cols>'||
                chr(10)||chr(32)||chr(32)||
                '<user_BINARY_cols>     '|| to_char(num_st_bin)   || 
                ' </user_BINARY_cols>'||
                chr(10)||chr(32)||chr(32)||
                '<all_resconfigs>       '|| to_char(num_xdb_rc)   || 
                ' </all_resconfigs>'||
                chr(10)||chr(32)||chr(32)||
                '<all_acls>             '|| to_char(num_xdb_acl)  || 
                ' </all_acls>'||
                chr(10)||chr(32)||chr(32)||
                '<user_xml_SXIs>        '|| to_char(num_xdb_sidx) ||
                ' </user_xml_SXIs>'||
                chr(10)||chr(32)||chr(32)||
                '<user_xml_UXIs>        '|| to_char(num_xdb_uidx) ||
                ' </user_xml_UXIs>'||
                chr(10)||chr(32)||chr(32)||
                '<xml_search_idxes>     '|| to_char(num_xdb_tidx) ||
                ' </xml_search_idxes>'|| 
                chr(10) ||
           '</xdb_feature_usage>';
        
        feature_info := to_clob(feature_usage);
    else
        feature_boolean := 0;
        aux_count := 0; 
        feature_info := 
            to_clob('<xdb_feature_usage>SYSTEM</xdb_feature_usage>');
    end if;
 
end;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_JSON
 *  The procedure to detect usage for JSON
 ***************************************************************/

create or replace procedure DBMS_FEATURE_JSON
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB )
AS
  num_json_cols         number := 0;
  num_clob_cols         number := 0;
  num_blob_cols         number := 0;
  num_raw_cols          number := 0;
  num_varchar2_cols     number := 0;
  num_json_view         number := 0;
  num_jv_view           number := 0;
  num_je_view           number := 0;
  num_jq_view           number := 0;
  num_jt_view           number := 0;
  num_dg_view           number := 0;
  num_min_rows          number := 0;
  num_max_rows          number := 0;
  num_avg_rows          number := 0;
  num_jv_fidx           number := 0;
  num_je_fidx           number := 0;
  num_jq_fidx           number := 0;
  num_json_cidx         number := 0;
  num_bson_cidx         number := 0;
  maxsize_jsoncol       number := 0;
  avgsize_jsoncol       number := 0;
  num_dataguides        number := 0;
  num_collections       number := 0;
  num_imcu_json         number := 0;
  num_imeu_json         number := 0;
  num_json_imc_enabled  number := 0;
  num_i_rows            number := 0;
  num_sn_rows           number := 0;
  num_dg_rows           number := 0;
  stmt                  varchar2(1000);
  table_not_found       EXCEPTION;
  PRAGMA exception_init(table_not_found, -942);
  CURSOR expr_cur IS
    SELECT COLUMN_EXPRESSION
    FROM DBA_IND_EXPRESSIONS; 
  expr   expr_cur%ROWTYPE; 
  c         clob; 
  TYPE      CurTyp IS REF CURSOR;
  cur       CurTyp;
  idxname1   dbms_id;
  uname1     dbms_id;   
  idxname2   dbms_quoted_id;
  uname2     dbms_quoted_id;   
BEGIN
  -- initialize
  feature_boolean := 0;
  aux_count := 0; 
  feature_info := '{ "version":1, ';

  /* get the number of json cols from dba_json_columns */
  execute immediate 'select count(1) from dba_json_columns
                     where owner not in 
                     (select distinct username from all_users             
                      where oracle_maintained = ''Y'')'
  into num_json_cols;

  /* Number of CLOB columns */
  execute immediate 'select count(1) from dba_json_columns
                     where data_type = ''CLOB'' and owner not in 
                     (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_clob_cols;
  
  /* Number of BLOB columns */
  execute immediate 'select count(1) from dba_json_columns
                     where data_type = ''BLOB'' and owner not in 
                     (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_blob_cols;

  /* Number of RAW columns */
  execute immediate 'select count(1) from dba_json_columns
                     where data_type = ''RAW'' and owner not in 
                     (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_raw_cols;

  /* Number of varchar columns */
  execute immediate 'select count(1) from dba_json_columns
                     where data_type = ''VARCHAR2'' and owner not in 
                     (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_varchar2_cols;

  /* Write Storage stats into clob */
  dbms_lob.append(feature_info, to_clob ('"jsonColumns": {' ||
                  '"total":' || to_char(num_json_cols) || ' , ' ||
                  '"varchar2":' || to_char(num_varchar2_cols) || ' , ' ||
                  '"clob":' || to_char(num_clob_cols) || ' , ' ||
                  '"blob":' || to_char(num_blob_cols) || ' , ' ||
                  '"raw":'  || to_char(num_raw_cols)  || ' }, '));

  /* Get the min, max and avg size of the JSON tables */
  execute immediate 'select max(num_rows) from dba_tables where 
                     table_name in (select table_name 
                     from dba_json_columns where owner not in
                     (select distinct username from all_users
                      where oracle_maintained = ''Y''))'
  into num_max_rows;

  execute immediate 'select min(num_rows) from dba_tables where 
                     table_name in (select table_name 
                     from dba_json_columns where owner not in
                     (select distinct username from all_users
                      where oracle_maintained = ''Y''))'
  into num_min_rows;
  
  execute immediate 'select avg(num_rows) from dba_tables where 
                     table_name in (select table_name 
                     from dba_json_columns where owner not in
                     (select distinct username from all_users
                      where oracle_maintained = ''Y''))'
  into num_avg_rows;

  if num_max_rows is null then
    num_max_rows := 0;
  end if;

  if num_min_rows is null then
    num_min_rows := 0;
  end if;

  if num_avg_rows is null then
    num_avg_rows := 0;
  end if;

  /* Write row count stats into clob */
  dbms_lob.append(feature_info, to_clob ('"rowCount": {' ||
                  '"maxCount":' || to_char(num_max_rows) || ' , ' ||
                  '"minCount":' || to_char(num_min_rows) || ' , ' ||
                  '"avgCount":' || to_char(num_avg_rows) || ' }, '));

  /* Get size of JSON column */
  /* Average length in bytes */
  execute immediate 'select nvl(avg(avg_col_len),0) from dba_tab_columns t, 
                     dba_json_columns j where t.owner not in
                     (select distinct username from all_users
                      where oracle_maintained = ''Y'')
                     and t.table_name = j.table_name 
                     and t.column_name = j.column_name'
  into avgsize_jsoncol;

  /* Max length in chars */
  execute immediate 'select nvl(max(char_length),0) from dba_tab_columns t, 
                     dba_json_columns j where t.owner not in
                     (select distinct username from all_users
                      where oracle_maintained = ''Y'')
                     and t.table_name = j.table_name 
                     and t.column_name = j.column_name'
  into maxsize_jsoncol;

  /* Write row size stats into clob */
  dbms_lob.append(feature_info, to_clob ('"rowSize": {' ||
                  '"avgSizeBytes":' || to_char(avgsize_jsoncol) || ' , ' ||
                  '"maxSizeChars":' || to_char(maxsize_jsoncol) || ' }, '));

  /* IMC related stats */
  /* JSON stored in IMCUs */
  execute immediate 'select count(1) from v$im_col_cu a, sys.col$ b,
                                           dba_json_columns j, obj$ o
                      where a.objd = b.obj# and 
                            b.obj# = o.obj# and
                            a.column_number = b.segcol# and 
                            b.name = j.column_name and 
                            o.name = j.table_name'
  into num_imcu_json;

  /* OSON corresponding to JSON stored in IMEUs */
  execute immediate 'select count(1) from v$im_imecol_cu a, sys.col$ b 
                     where a.objd = b.obj# and 
                           a.internal_column_number = b.intcol# and 
                           b.name like ''SYS_IME_OSON%'''
  into num_imeu_json;

  /* Get number of JSON cols/tables which have just been enabled for IMC */
  execute immediate 'select count(1) from x$kdzcolcl x, dba_json_columns j 
                    where x.column_name = j.column_name and 
                          x.table_name = j.table_name and 
                          x.owner = j.owner'
  into num_json_imc_enabled;
 
  /* Write row size stats into clob */
  dbms_lob.append(feature_info, to_clob ('"IMCStats": {' ||
                  '"IMCU#":' || to_char(num_imcu_json) || ' , ' ||
                  '"IMEU#":' || to_char(num_imeu_json) || ' , ' ||
                  '"JSONIMC#":' || to_char(num_json_imc_enabled) || ' }, '));

  /* Get the JSON view stats */
  execute immediate 'select count(1) from dba_views where 
                     upper(text_vc) like ''%JSON_VALUE%'' and owner 
                     not in (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_jv_view;

  execute immediate 'select count(1) from dba_views where 
                     upper(text_vc) like ''%JSON_EXISTS%'' and owner
                     not in (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_je_view;

  execute immediate 'select count(1) from dba_views where 
                     upper(text_vc) like ''%JSON_QUERY%'' and owner
                     not in (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_jq_view;

  execute immediate 'select count(1) from dba_views where 
                     upper(text_vc) like ''%JSON_TABLE%'' and owner
                     not in (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_jt_view;

  execute immediate 'select count(1) from dba_views where 
                     upper(text_vc) like ''%JSON_DATAGUIDE%'' and owner
                     not in (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_dg_view;

  execute immediate 'select count(1) from dba_views where 
                     upper(text_vc) like ''%JSON_%'' and owner
                     not in (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_json_view;

  /* Write JSON view stats into clob */
  dbms_lob.append(feature_info, to_clob ('"views": {' ||
                  '"total":' || to_char(num_json_view) || ' , ' ||
                  '"jsonValue":'  || to_char(num_jv_view) || ' , ' ||
                  '"jsonExists":' || to_char(num_je_view) || ' , ' ||
                  '"jsonQuery":'  || to_char(num_jq_view) || ' , ' ||
                  '"jsonTable":'  || to_char(num_jt_view) || ' , ' ||
                  '"jsonDataGuide":'  || to_char(num_dg_view) || ' }, '));

  /* Indexes */

  /* Get JSON Functional Index stats */
  OPEN expr_cur;
  LOOP
      FETCH expr_cur INTO expr;
      EXIT WHEN expr_cur%NOTFOUND;
      c := to_clob(expr.COLUMN_EXPRESSION);
      if (upper(c) like '%JSON_VALUE%') then
        num_jv_fidx := num_jv_fidx + 1;
      elsif (upper(c) like '%JSON_EXISTS%') then
        num_je_fidx := num_je_fidx + 1;
      elsif (upper(c) like '%JSON_QUERY%') then
        num_jq_fidx := num_jq_fidx + 1;
      end if;
  END LOOP;
  CLOSE expr_cur; 

  /* Write JSON view stats into clob */
  dbms_lob.append(feature_info, to_clob ('"funcIdx": {' ||
                  '"jsonValue":'  || to_char(num_jv_fidx) || ' , ' ||
                  '"jsonExists":' || to_char(num_je_fidx) || ' , ' ||
                  '"jsonQuery":'  || to_char(num_jq_fidx) || ' }, '));


  dbms_lob.append(feature_info, to_clob ('"searchIdxStats": {' ));
  /* Get JSON text indexes stats */
  stmt:= 'select i.idx_name, u.username 
          from ctxsys.dr$index i, all_users u
          where i.idx_id in ( select ixv_idx_id 
                              from ctxsys.dr$index_value 
                              where IXV_OAT_ID = 50817) and
                i.idx_owner# = u.user_id and u.username not in
                                 (select distinct username 
                                  from all_users
                                  where oracle_maintained = ''Y'')';
  open cur for stmt;
    LOOP
      fetch cur into idxname1, uname1;
      exit when cur%notfound;

      idxname2 := DBMS_ASSERT.ENQUOTE_NAME(idxname1, false);
      uname2   := DBMS_ASSERT.ENQUOTE_NAME(uname1, false);

      num_json_cidx := num_json_cidx + 1;
    begin 
      stmt := 'select count(*) from ' || uname2 || '.' || 
              dbms_assert.enquote_name('DR$'||idxname1||'$I', FALSE);
      execute immediate stmt into num_i_rows;
    exception
      when table_not_found then
        null;
    end;    

    begin
      stmt := 'select count(*) from ' || uname2 || '.' ||
              dbms_assert.enquote_name('DR$'||idxname1||'$SN', FALSE);
      execute immediate stmt into num_sn_rows;
    exception
      when table_not_found then
        null;
    end;    

    begin
      stmt := 'select count(*) from ' || uname2 || '.' ||
              dbms_assert.enquote_name('DR$'||idxname1||'$DG', FALSE);
      execute immediate stmt into num_dg_rows;
    exception
      when table_not_found then
        null;
    end;    

    dbms_lob.append(feature_info, to_clob ('"Index-Stats": {' ||
                  '"indexName":' || idxname2 || ' , ' ||
                  '"num$IRows":' || to_char(num_i_rows) || ' , ' ||
                  '"num$SNRows":' || to_char(num_sn_rows) || ' , ' ||
                  '"num$DGRows":' || to_char(num_dg_rows)  || ' }, '));
  END LOOP;  
  dbms_lob.append(feature_info, to_clob ('},'));

  begin
  execute immediate 'select count(1) from ctxsys.dr$index i, sys.user$ u 
                     where i.idx_id in (select ixv_idx_id from 
                     ctxsys.dr$index_value where IXV_OAT_ID = 50819) and
                     i.idx_owner# = u.user# and u.name not in
                     (select distinct username from all_users
                      where oracle_maintained = ''Y'')'
  into num_bson_cidx;
  exception
    when OTHERS then
       num_bson_cidx := 0;
  end;

  /* Write JSON Search index stats into clob */
  dbms_lob.append(feature_info, to_clob ('"searchIdx": {' ||
                  '"jsonSearchIdx":' || to_char(num_json_cidx) || ' , ' ||
                  '"bsonSearchIdx":' || to_char(num_bson_cidx)  || ' }, '));

  /* Get the JSON dataguide stats*/
  execute immediate 'select count(1) from ALL_JSON_DATAGUIDES'
  into num_dataguides;

  /* Write JSON dataguide stats into clob */
  dbms_lob.append(feature_info, to_clob ('"dataguideStats": {' ||
                  '"numDataguide":' || to_char(num_dataguides) || ' }, '));
        
  /* Get the SODA JSON stats*/
  execute immediate 'select count(1) from XDB.JSON$COLLECTION_METADATA'
  into num_collections;

  /* Write SODA JSON stats into clob */
  dbms_lob.append(feature_info, to_clob ('"sodaStats": {' ||
                  '"numCollections":' || to_char(num_collections) || ' } } '));
        
  /* set feature_boolean if reqd */
  IF num_json_cols > 0 THEN
    feature_boolean := 1;                    
  END IF;
END;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_APEX
 *  The procedure to detect usage for Application Express 
 ***************************************************************/

create or replace procedure DBMS_FEATURE_APEX
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
    l_apex_schema   dbms_id := null;
    l_num_apps      number := 0;
    l_num_views     number := 0;
    l_num_workspace number := 0;
    l_num_users     number := 0;
begin
    /* Determine current schema for Application Express
       Note: this will only return one row              */
    for c1 in (select schema
                 from dba_registry
                where comp_id = 'APEX' ) loop
        l_apex_schema := dbms_assert.enquote_name(c1.schema, FALSE);
    end loop;

    /* If not found, then APEX is not installed */
    if l_apex_schema is null then
        feature_boolean := 0;
        aux_count := 0;
        feature_info := to_clob('APEX usage not detected');
        return;
    end if;

       /* Determine the number of workspaces */
       execute immediate 'select count(*)
  from '||l_apex_schema||'.wwv_flow_companies
 where provisioning_company_id not in (0,10,11,12)' into l_num_workspace;

    if l_num_workspace > 0 then

       /* Determine the number of user-created applications */
       execute immediate 'select count(*)
  from '||l_apex_schema||'.wwv_flows
 where security_group_id not in (10,11,12)' into l_num_apps;

       /* Determine the number of non-internal Application Express users */
       execute immediate 'select count(*)
  from '||l_apex_schema||'.wwv_flow_fnd_user
 where security_group_id not in (10,11,12)' into l_num_users;

       /* Determine number of page views in the last 30 days */
       execute immediate 'select nvl(sum(page_views),0)
  from '||l_apex_schema||'.wwv_flow_log_history
 where log_day > sysdate -30' into l_num_views;

       feature_boolean := 1;
       aux_count := l_num_apps;
       feature_info := to_clob('Number of applications: '||to_char(l_num_apps)||
       ', '||'Number of workspaces: '||to_char(l_num_workspace)||
       ', '||'Number of users: '||to_char(l_num_users))||
       ', '||'Page views last 30 days: '||to_char(l_num_views);

    else
       feature_boolean := 0;
       aux_count := 0;
       feature_info := to_clob('APEX usage not detected');
    end if;

end DBMS_FEATURE_APEX;
/

/***************************************************************
 * DBMS_FEATURE_OBJECT
 *  The procedure to detect usage for OBJECT 
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_OBJECT
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  num_obj_types         number;
  num_obj_tables        number;
  num_obj_columns       number;
  num_obj_views         number;
  num_anydata_cols      number;
  num_nt_cols           number;
  num_varray_cols       number;
  num_octs              number;
  feature_usage         varchar2(1000);
  TYPE cursor_t         IS REF CURSOR;
  cursor_coltype        cursor_t;
  total_count           number;
  flag                  number;

BEGIN
  --initialize
  num_obj_types         :=0;
  num_obj_tables        :=0;
  num_obj_columns       :=0;
  num_obj_views         :=0;
  num_anydata_cols      :=0;
  num_nt_cols           :=0;
  num_varray_cols       :=0;
  num_octs              :=0;
  total_count           :=0;
  flag                  :=0;

  feature_boolean := 0;
  aux_count := 0;

  /* get number of object types */
  execute immediate 'select count(*) from sys.type$ t, sys.obj$ o, sys.user$ u 
          where o.owner# = u.user# and o.oid$ = t.tvoid and 
            u.name not in (select schema_name from v$sysaux_occupants) and
            u.name not in (''OE'', ''IX'', ''PM'', ''DVSYS'', 
                           ''LBACSYS'', ''GSMADMIN_INTERNAL'') and
            u.name not like ''FLOWS_%'' and
            u.name not like ''APEX_%'''
          into num_obj_types;

  /* get number of object tables */
  execute immediate 'select count(*) from  sys.tab$ t, sys.obj$ o, sys.user$ u
          where o.owner# = u.user# and o.obj# = t.obj# and
                bitand(t.property, 1) = 1 and bitand(o.flags, 128) = 0 and
                u.name not in (select schema_name from v$sysaux_occupants) and
                u.name not in (''OE'', ''PM'', ''GSMADMIN_INTERNAL'') and
                u.name not like ''FLOWS_%'' and
                u.name not like ''APEX_%'''
          into num_obj_tables;
 

  /* get number of object views */ 
  execute immediate 'select count(*) from sys.typed_view$ t, sys.obj$ o, sys.user$ u
          where o.owner# = u.user# and o.obj# = t.obj# and
                u.name not in (select schema_name from v$sysaux_occupants) and
                u.name not in (''OE'', ''DVSYS'') and
                u.name not like ''FLOWS_%'' and
                u.name not like ''APEX_%'''
          into num_obj_views;

  /* get number of object columns, nested table columns, varray columns,
   * anydata columns and OCTs
   */  
  OPEN cursor_coltype FOR '
    select /*+ index(o i_obj1) */ count(*), bitand(t.flags, 16414)
    from sys.coltype$ t, sys.obj$ o, sys.user$ u
    where o.owner# = u.user# and o.obj# = t.obj# and
          u.name not in (select schema_name from v$sysaux_occupants) and
          u.name not in (''OE'', ''IX'', ''PM'', ''DVSYS'', 
                         ''GSMADMIN_INTERNAL'') and
          u.name not like ''FLOWS_%'' and
          u.name not like ''APEX_%'' and
          ((bitand(t.flags, 30) != 0) OR
           (bitand(t.flags, 16384) = 16384 and
            t.toid = ''00000000000000000000000000020011''))
    group by (bitand(t.flags, 16414))';


  LOOP
    BEGIN
      FETCH cursor_coltype INTO total_count, flag;
      EXIT WHEN cursor_coltype%NOTFOUND;

      /* number of nested table columns */
      IF flag = 4 THEN
        num_nt_cols := total_count;
      END IF;

      /* number of varray columns */
      IF flag = 8 THEN
        num_varray_cols := total_count;
      END IF;

      /* number of OCTs */
      IF flag = 12 THEN
        num_octs := total_count;
      END IF;

      /* number of adt and ref columns */
      IF (flag = 2 or flag = 16) THEN
        num_obj_columns  := num_obj_columns + total_count;
      END IF;

      /* number of anydata columns */
      IF (flag = 16384) THEN
        num_anydata_cols := total_count;
      END IF;
    END;
  END LOOP;

  if ((num_obj_types > 0) OR (num_obj_tables > 0) OR (num_obj_columns >0)
      OR (num_obj_views > 0) OR (num_anydata_cols > 0) OR (num_nt_cols > 0)
      OR (num_varray_cols > 0) OR (num_octs > 0)) then

    feature_boolean := 1;  
    feature_usage := 'num of object types: ' || to_char(num_obj_types) ||
        ',' || 'num of object tables: ' || to_char(num_obj_tables) ||
        ',' || 'num of adt and ref columns: ' || to_char(num_obj_columns) ||
        ',' || 'num of object views: ' || to_char(num_obj_views) ||
        ',' || 'num of anydata cols: ' || to_char(num_anydata_cols) ||
        ',' || 'num of nested table cols: ' || to_char(num_nt_cols) ||
        ',' || 'num of varray cols: ' || to_char(num_varray_cols) ||
        ',' || 'num of octs: ' || to_char(num_octs);

    feature_info := to_clob(feature_usage);
  else
    feature_info := to_clob('OBJECT usage not detected');
  end if;

end;
/
 
/***************************************************************
 * DBMS_FEATURE_EXTENSIBILITY
 *  The procedure to detect usage for EXTENSIBILITY 
 ***************************************************************/
  
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_EXTENSIBILITY
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB) 
AS
  num_user_opts         number;
  num_user_aggs         number;
  num_table_funs        number;
  num_idx_types         number;
  num_domain_idxs       number;
  feature_usage         varchar2(1000);
  TYPE cursor_t         IS REF CURSOR;
  cursor_udftype        cursor_t;
  total_count           number;
  flag                  number;

begin
  --initialize
  num_user_opts         :=0;
  num_user_aggs         :=0;
  num_table_funs        :=0;
  num_idx_types         :=0;
  num_domain_idxs       :=0;
  total_count           :=0;
  flag                  :=0;


  feature_boolean := 0;
  aux_count := 0;

  /* get number of user-defined operators */
  execute immediate 'select count(*) from DBA_OPERATORS
          where owner not in (select schema_name from v$sysaux_occupants)
          and owner not in (''SH'')'
          into num_user_opts;

  /* get number of user-defined index types */
  execute immediate 'select count(*) 
          from sys.indtypes$ i, sys.user$ u, sys.obj$ o
          where i.obj# = o.obj# and o.owner# = u.user# and
                u.name not in (select schema_name from v$sysaux_occupants)
                and u.name not in (''SH'')'
          into num_idx_types;

  /* get number of user-defined domain indexes */
  execute immediate 'select count(*) from sys.user$ u, sys.ind$ i, sys.obj$ o
          where u.user# = o.owner# and o.obj# = i.obj# and
                i.type# = 9 and
                u.name not in (select schema_name from v$sysaux_occupants)
                and u.name not in (''SH'')'
          into num_domain_idxs; 

  /* get number of user-defined aggregates and user-defined 
   * pipelined table functions
   */
  OPEN cursor_udftype FOR '
    select count(*), bitand(p.properties, 24)
    from sys.obj$ o, sys.user$ u, sys.procedureinfo$ p
    where o.owner# = u.user# and o.obj# = p.obj# and
          bitand(p.properties, 24) != 0 and
          u.name not in (select schema_name from v$sysaux_occupants)
          and u.name not in (''SH'', ''DVSYS'')
    group by (bitand(p.properties, 24))';

  LOOP
    BEGIN
      FETCH cursor_udftype INTO total_count, flag;
      EXIT WHEN cursor_udftype%NOTFOUND;

      IF flag = 8 THEN
        num_user_aggs := total_count;
      END IF;

      IF flag = 16 THEN
        num_table_funs := total_count;
      END IF;
    END;
  END LOOP; 
   
  if ((num_user_opts > 0) OR (num_user_aggs > 0) OR (num_table_funs > 0)
      OR (num_idx_types > 0) OR (num_domain_idxs > 0)) then
    feature_boolean := 1;
    feature_usage := 'num of user-defined operators: ' || to_char(num_user_opts) ||
        ',' || 'num of user-defined aggregates: ' || to_char(num_user_aggs) ||
        ',' || 'num of table functions: ' || to_char(num_table_funs) ||
        ',' || 'num of index types: ' || to_char(num_idx_types) ||
        ',' || 'num of domain indexes: ' || to_char(num_domain_idxs);

    feature_info := to_clob(feature_usage);
  else
    feature_info := to_clob('EXTENSIBILITY usage not detected');
  end if;
 
end;
/

/***************************************************************
 * DBMS_FEATURE_RULESMANAGER
 *  The procedure to detect usage for RULES MANAGER & EXPRESSION FILTER
 ***************************************************************/
  
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RULESMANAGER
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB) 
AS
  num_rule_clss        number := 0; 
  num_comp_rulcls       number := 0; 
  max_pmevt_prcmp       number := 0; 
  avg_pmevt_prcmp       number := 0; 
  num_cllt_evts         number := 0; 
  num_pure_expcols      number := 0; 
  num_domain_idxs       number;

  feature_usage         varchar2(1000);
  TYPE cursor_t         IS REF CURSOR;
  cursor_udftype        cursor_t;
  total_count           number;
  flag                  number;

begin
  --initialize
  feature_boolean := 0;
  aux_count := 0;

  /* get the number of rule classes */ 
  begin  
    execute immediate 'select count(*) from exfsys.adm_rlmgr_rule_classes'
                          into num_rule_clss; 
  exception 
    when others then 
       num_rule_clss := 0; 
  end; 

  if (num_rule_clss > 0) then 
    /* get the numbers on rule classes with composite events */ 
    execute immediate 'select count(*), avg(prmevtprc), max(prmevtprc) 
     from (select count(*) as prmevtprc from 
           exfsys.adm_rlmgr_comprcls_properties
           group by rule_class_owner, rule_class_name) ' into 
         num_comp_rulcls, avg_pmevt_prcmp, max_pmevt_prcmp;

    /* rule class with collection events */
    execute immediate 'select count(*) from 
            exfsys.adm_rlmgr_comprcls_properties
              where collection_enb = ''Y''' into num_cllt_evts;
  end if; 

  /* expression columns outside the context of rule classes */ 
  execute immediate 'select count(*) from exfsys.adm_expfil_expression_sets
     where not(expr_column like ''RLM$%'')' into num_pure_expcols;
   
  if ((num_rule_clss > 0) OR (num_comp_rulcls > 0) OR (avg_pmevt_prcmp > 0)
      OR (max_pmevt_prcmp > 0) OR (num_pure_expcols > 0)) then
    feature_boolean := 1; 
    feature_usage :=
       'num of rule classes: '||to_char(num_rule_clss) ||', '||
       'num of rule classes with composite events: '||
                          to_char(num_comp_rulcls) ||', '||
       'avg num of primitive events per composite: '||
                          to_char(avg_pmevt_prcmp) ||', '||
       'max num of primitive events for a rule class: '||
                          to_char(max_pmevt_prcmp) ||', '||
       'num expression columns(user): '||
                          to_char(num_pure_expcols); 
     feature_info := to_clob(feature_usage);
  else
     feature_info := to_clob(
              'Rules Manager/Expression Filter usage not detected');  
  end if;
 
end;
/

/***************************************************************
 * DBMS_FEATURE_CDC
 *  The procedure to detect usage for Change Data Capture (CDC)
 ***************************************************************/
  
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_CDC
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB) 
AS
  num_autolog           number := 0; 
  num_hotlog            number := 0; 
  num_sync              number := 0; 
  num_dist              number := 0; 
  num_hotmine           number := 0;

  num_auto_sets         number := 0;
  num_hot_sets          number := 0;
  num_sync_sets         number := 0;
  num_dist_sets         number := 0;
  num_mine_sets         number := 0;

  num_auto_tabs         number := 0;
  num_hot_tabs          number := 0;
  num_sync_tabs         number := 0;
  num_dist_tabs         number := 0;
  num_mine_tabs         number := 0;

  num_auto_subs         number := 0;
  num_hot_subs          number := 0;
  num_sync_subs         number := 0;
  num_dist_subs         number := 0;
  num_mine_subs         number := 0;

  feature_usage         varchar2(2000);

begin
  --initialize
  aux_count := 0;

  /* get the number of total change tables and dump in aux_count */
  begin
    execute immediate 'select count(*) from sys.cdc_change_Tables$'
                        into aux_count;
  exception
    when others then
      aux_count := 0;
  end;

  if (aux_count > 0) then
    feature_boolean := 1;
  else
    feature_boolean := 0;
    feature_info := to_clob('CDC usage not detected');
    return;
  end if;

  /* get data for AUTOLOG */
  begin
    execute immediate 'select count(*) from sys.cdc_change_sources$
                         where BITAND(source_type, 2) = 2'
                        into num_autolog;
  exception
    when others then
      num_autolog := 0;
  end;
  
  if (num_autolog > 0 ) then

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b
                           where BITAND(a.source_type, 2) = 2 AND
                              b.change_source_name = a.source_name'
                          into num_auto_sets;
    exception
      when others then
        num_auto_sets := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_change_tables$ c
                           where BITAND(a.source_type, 2) = 2 AND
                              b.change_source_name = a.source_name AND
                              c.change_set_name = b.set_name'
                          into num_auto_tabs;
    exception
      when others then
        num_auto_tabs := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_subscribers$ c
                           where BITAND(a.source_type, 2) = 2 AND
                              b.change_source_name = a.source_name AND
                              c.set_name = b.set_name'
                          into num_auto_subs;
    exception
      when others then
        num_auto_subs := 0;
    end;

  end if;

  /* get data for HOTLOG */
  begin
    execute immediate 'select count(*) from sys.cdc_change_sources$
                         where BITAND(source_type, 4) = 4'
                        into num_hotlog;
  exception
    when others then
      num_hotlog := 0;
  end;
  
  if (num_hotlog > 0 ) then

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b
                           where BITAND(a.source_type, 4) = 4 AND
                              b.change_source_name = a.source_name'
                          into num_hot_sets;
    exception
      when others then
        num_hot_sets := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_change_tables$ c
                           where BITAND(a.source_type, 4) = 4 AND
                              b.change_source_name = a.source_name AND
                              c.change_set_name = b.set_name'
                          into num_hot_tabs;
    exception
      when others then
        num_hot_tabs := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_subscribers$ c
                           where BITAND(a.source_type, 4) = 4 AND
                              b.change_source_name = a.source_name AND
                              c.set_name = b.set_name'
                          into num_hot_subs;
    exception
      when others then
        num_hot_subs := 0;
    end;

  end if;

  /* get data for SYNCHRONOUS */
  begin
    execute immediate 'select count(*) from sys.cdc_change_sources$
                         where BITAND(source_type, 8) = 8'
                        into num_sync;
  exception
    when others then
      num_sync := 0;
  end;
  
  if (num_sync > 0 ) then

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b
                           where BITAND(a.source_type, 8) = 8 AND
                              b.change_source_name = a.source_name'
                          into num_sync_sets;
    exception
      when others then
        num_sync_sets := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_change_tables$ c
                           where BITAND(a.source_type, 8) = 8 AND
                              b.change_source_name = a.source_name AND
                              c.change_set_name = b.set_name'
                          into num_sync_tabs;
    exception
      when others then
        num_sync_tabs := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_subscribers$ c
                           where BITAND(a.source_type, 8) = 8 AND
                              b.change_source_name = a.source_name AND
                              c.set_name = b.set_name'
                          into num_sync_subs;
    exception
      when others then
        num_sync_subs := 0;
    end;

  end if;

  /* get data for DISTRIBUTED HOTLOG */
  begin
    execute immediate 'select count(*) from sys.cdc_change_sources$
                         where BITAND(source_type, 64) = 64'
                        into num_dist;
  exception
    when others then
      num_dist := 0;
  end;
  
  if (num_dist > 0 ) then

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b
                           where BITAND(a.source_type, 64) = 64 AND
                              b.change_source_name = a.source_name'
                          into num_dist_sets;
    exception
      when others then
        num_dist_sets := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_change_tables$ c
                           where BITAND(a.source_type, 64) = 64 AND
                              b.change_source_name = a.source_name AND
                              c.change_set_name = b.set_name'
                          into num_dist_tabs;
    exception
      when others then
        num_dist_tabs := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_subscribers$ c
                           where BITAND(a.source_type, 64) = 64 AND
                              b.change_source_name = a.source_name AND
                              c.set_name = b.set_name'
                          into num_dist_subs;
    exception
      when others then
        num_dist_subs := 0;
    end;

  end if;

  /* get data for DISTRIBUTED HOTMINING */
  begin
    execute immediate 'select count(*) from sys.cdc_change_sources$
                         where BITAND(source_type, 128) = 128'
                        into num_hotmine;
  exception
    when others then
      num_hotmine := 0;
  end;
  
  if (num_hotmine > 0 ) then

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b
                           where BITAND(a.source_type, 128) = 128 AND
                              b.change_source_name = a.source_name'
                          into num_mine_sets;
    exception
      when others then
        num_mine_sets := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_change_tables$ c
                           where BITAND(a.source_type, 128) = 128 AND
                              b.change_source_name = a.source_name AND
                              c.change_set_name = b.set_name'
                          into num_mine_tabs;
    exception
      when others then
        num_mine_tabs := 0;
    end;

    begin
      execute immediate 'select count(*) from sys.cdc_change_sources$ a,
                           sys.cdc_change_sets$ b, sys.cdc_subscribers$ c
                           where BITAND(a.source_type, 128) = 128 AND
                              b.change_source_name = a.source_name AND
                              c.set_name = b.set_name'
                          into num_mine_subs;
    exception
      when others then
        num_mine_subs := 0;
    end;

  end if;


  feature_usage := 'autolog - source: ' || to_char(num_autolog) ||', '||
                  'sets: '  || to_char(num_auto_sets) ||', '||
                  'tables: ' || to_char(num_auto_tabs) ||', '||
                  'subscriptions: ' || to_char(num_auto_subs) ||', '||
                  'hotlog - source: ' || to_char(num_hotlog) ||', '||
                  'sets: '  || to_char(num_hot_sets) ||', '||
                  'tables: ' || to_char(num_hot_tabs) ||', '||
                  'subscriptions: ' || to_char(num_hot_subs) ||', '||
                  'sync - source: ' || to_char(num_sync) ||', '||
                  'sets: '  || to_char(num_sync_sets) ||', '||
                  'tables: ' || to_char(num_sync_tabs) ||', '||
                  'subscriptions: ' || to_char(num_sync_subs) ||', '||
                  'distributed - source: ' || to_char(num_dist) ||', '||
                  'sets: '  || to_char(num_dist_sets) ||', '||
                  'tables: ' || to_char(num_dist_tabs) ||', '||
                  'subscriptions: ' || to_char(num_dist_subs) ||', '||
                  'HotMine - source: ' || to_char(num_hotmine) ||', '||
                  'sets: '  || to_char(num_mine_sets) ||', '||
                  'tables: ' || to_char(num_mine_tabs) ||', '||
                  'subscriptions: ' || to_char(num_mine_subs);

  feature_info := to_clob(feature_usage);

end;
/


/***************************************************************
 * DBMS_FEATURE_SERVICES
 *  The procedure to detect usage for Services
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_services
      (is_used OUT number, hwm OUT number, feature_info OUT clob)
AS
  -- Based off dba_services
  num_clb_long                            NUMBER := 0;
  num_clb_short                           NUMBER := 0;
  num_goal_service_time                   NUMBER := 0;
  num_goal_throughput                     NUMBER := 0;
  num_goal_none                           NUMBER := 0;
  num_goal_null                           NUMBER := 0;
  num_aq_notifications                    NUMBER := 0;

  -- Based off gv$active_services
  num_active_svcs                         NUMBER := 0;
  num_active_svcs_wo_distinct             NUMBER := 0;
  avg_active_cardinality                  NUMBER := 0;

  default_service_name                    varchar2(1000);
  default_xdb_service_name                varchar2(1000);
  db_domain                               varchar2(1000);

BEGIN
  -- initialize
  is_used      := 0;
  hwm          := 0;
  feature_info := 'Services usage not detected';

  -- get default service name - db_unique_name[.db_domain]

  SELECT value INTO default_service_name FROM v$parameter WHERE
        lower(name) = 'db_unique_name';

  SELECT value INTO db_domain FROM v$parameter WHERE
        lower(name) = 'db_domain';

  -- create default XDB service name
  default_xdb_service_name := default_service_name || 'XDB';

  -- append db_domain if it is set
  IF db_domain IS NOT NULL then
    default_service_name := default_service_name || '.' || db_domain;
  END IF;

  SELECT count(*) INTO hwm
  FROM dba_services
  WHERE 
      NAME NOT LIKE 'SYS$%'
  AND NETWORK_NAME NOT LIKE 'SYS$%'
  AND NAME <> default_xdb_service_name
  AND NAME <> default_service_name;

  IF hwm > 0 THEN
    is_used := 1;
  END IF;

  -- if services is used 
  IF (is_used = 1) THEN

    -- Get the counts for CLB_GOAL variations
    FOR item IN (
      SELECT clb_goal, count(*) cg_count
      FROM dba_services
      where 
          NAME NOT LIKE 'SYS$%'
      AND NETWORK_NAME NOT LIKE 'SYS$%'
      AND NAME <> default_xdb_service_name
      AND NAME <> default_service_name
      GROUP BY clb_goal) 

    LOOP

      IF item.clb_goal = 'SHORT' THEN
        num_clb_short := item.cg_count;
      ELSIF item.clb_goal = 'LONG' THEN
        num_clb_long  := item.cg_count;
      END IF;

    END LOOP;

    
    -- Get the counts for GOAL variations
    FOR item IN (
      SELECT goal, count(*) g_count
      FROM dba_services
      where 
          NAME NOT LIKE 'SYS$%'
      AND NETWORK_NAME NOT LIKE 'SYS$%'
      AND NAME <> default_xdb_service_name
      AND NAME <> default_service_name
      GROUP BY goal) 

    LOOP

      IF item.goal = 'SERVICE_TIME' THEN
        num_goal_service_time := item.g_count;
      ELSIF item.goal = 'THROUGHPUT' THEN
        num_goal_throughput  := item.g_count;
      ELSIF item.goal = 'NONE' THEN
        num_goal_none := item.g_count;
      ELSIF item.goal is NULL THEN
        num_goal_null := item.g_count;
      END IF;

    END LOOP;

    -- count goal is NULL as goal = NONE
    num_goal_none := num_goal_none + num_goal_null;

    -- Get the count for aq_ha_notifications
    SELECT count(*) into num_aq_notifications
    FROM dba_services
    where 
        NAME NOT LIKE 'SYS$%'
    AND NETWORK_NAME NOT LIKE 'SYS$%'
    AND NAME <> default_xdb_service_name
    AND NAME <> default_service_name
    AND AQ_HA_NOTIFICATIONS = 'YES';


    SELECT count(distinct name), count(*)
      INTO num_active_svcs, num_active_svcs_wo_distinct
    FROM gv$active_services
    WHERE 
        NAME NOT LIKE 'SYS$%'
    AND NETWORK_NAME NOT LIKE 'SYS$%'
    AND NAME <> default_xdb_service_name
    AND NAME <> default_service_name;

    IF num_active_svcs > 0 THEN

      avg_active_cardinality := 
        round(num_active_svcs_wo_distinct / num_active_svcs);

    END IF;

    feature_info := 
        ' num_clb_long: '          || num_clb_long
      ||' num_clb_short: '         || num_clb_short
      ||' num_goal_service_time: ' || num_goal_service_time
      ||' num_goal_throughput: '   || num_goal_throughput
      ||' num_goal_none: '         || num_goal_none
      ||' num_aq_notifications: '  || num_aq_notifications
      ||' num_active_services: '   || num_active_svcs
      ||' avg_active_cardinality: '|| avg_active_cardinality;

  END IF;

END;
/

/***************************************************************
 * DBMS_FEATURE_STREAMS(system)
 *  The procedure to detect usage for Services
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_streams
      (feature_boolean  OUT  NUMBER, 
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB)
AS
  num_capture                             NUMBER;
  num_ds_capture                          NUMBER;
  num_apply                               NUMBER;
  num_prop                                NUMBER;
  feature_usage                           VARCHAR2(2000);
  total_feature_usage                     NUMBER;
BEGIN
  -- initialize
  feature_boolean                  := 0;
  aux_count                        := 0;
  feature_info                     := NULL;
  num_capture                      := 0;
  num_ds_capture                   := 0;
  num_apply                        := 0;
  num_prop                         := 0;
  feature_usage                    := NULL;
  total_feature_usage              := 0;

  select decode (count(*), 0, 0, 1) into num_capture 
     from dba_capture 
     where UPPER(purpose) NOT IN ('GOLDENGATE CAPTURE','XSTREAM_OUT');

  select decode (count(*), 0, 0, 1) into num_ds_capture 
     from dba_capture 
     where UPPER(purpose) NOT IN ('GOLDENGATE CAPTURE','XSTREAM_OUT')
       and UPPER(capture_type) = 'DOWNSTREAM';

  select decode (count(*), 0, 0, 1) into num_apply 
     from dba_apply 
     where UPPER(purpose) NOT IN ('GOLDENGATE CAPTURE','GOLDENGATE APPLY',
                                  'XSTREAM IN', 'XSTREAM OUT');

  select decode (count(*), 0, 0, 1) into num_prop from dba_propagation;

  total_feature_usage := num_capture + num_apply + num_prop; 

  feature_usage := feature_usage ||
        'tcap:'                  || num_capture
      ||' dscap:'                || num_ds_capture
      ||' app:'                  || num_apply
      ||' prop:'                 || num_prop;

  feature_info   := to_clob(feature_usage);
  if (total_feature_usage > 0) THEN
      feature_boolean := 1;
  end if;
  if(num_capture > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_apply > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_prop > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;

END dbms_feature_streams;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_XSTREAM_OUT
 *  The procedure to detect usage for Services
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_xstream_out
      (feature_boolean  OUT  NUMBER, 
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB)
AS
  num_capture                             NUMBER;
  num_ds_capture                          NUMBER;
  num_apply                               NUMBER;
  feature_usage                           VARCHAR2(2000);
  total_feature_usage                     NUMBER;
BEGIN
  -- initialize
  feature_boolean                  := 0;
  aux_count                        := 0;
  feature_info                     := NULL;
  num_capture                      := 0;
  num_ds_capture                   := 0;
  num_apply                        := 0;
  feature_usage                    := NULL;
  total_feature_usage              := 0;

  select decode (count(*), 0, 0, 1) into num_capture 
     from dba_capture where UPPER(purpose) = 'XSTREAM OUT';

  select decode (count(*), 0, 0, 1) into num_ds_capture 
     from dba_capture where UPPER(purpose) = 'XSTREAM OUT' and
                            UPPER(capture_type) = 'DOWNSTREAM';

  select decode (count(*), 0, 0, 1) into num_apply 
     from dba_apply where UPPER(purpose) = 'XSTREAM OUT';


  total_feature_usage := num_capture + num_apply; 

  feature_usage := feature_usage ||
        'tcap:'                  || num_capture
      ||' dscap:'                || num_ds_capture
      ||' app:'                  || num_apply;

  feature_info   := to_clob(feature_usage);
  if (total_feature_usage > 0) THEN
      feature_boolean := 1;
  end if;
  if(num_capture > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_apply > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;

END dbms_feature_xstream_out;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_XSTREAM_IN
 *  The procedure to detect usage for Services
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_xstream_in
      (feature_boolean  OUT  NUMBER, 
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB)
AS
  num_apply                               NUMBER;
  feature_usage                           VARCHAR2(2000);
BEGIN
  -- initialize
  feature_boolean                  := 0;
  aux_count                        := 0;
  feature_info                     := NULL;
  num_apply                        := 0;
  feature_usage                    := NULL;

  select decode (count(*), 0, 0, 1) into num_apply 
     from dba_apply where UPPER(purpose) = 'XSTREAM IN';

  feature_usage := feature_usage ||
        'app:'                   || num_apply;

  feature_info   := to_clob(feature_usage);
  if (num_apply > 0) THEN
      feature_boolean := 1;
      aux_count      :=  aux_count+1;
  end if;

END dbms_feature_xstream_in;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_XSTREAM_STREAMS
 *  The procedure to detect usage for Services
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_xstream_streams
      (feature_boolean  OUT  NUMBER, 
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB)
AS
  num_capture                             NUMBER;
  num_ds_capture                          NUMBER;
  num_apply                               NUMBER;
  num_prop                                NUMBER;
  feature_usage                           VARCHAR2(2000);
  total_feature_usage                     NUMBER;
BEGIN
  -- initialize
  feature_boolean                  := 0;
  aux_count                        := 0;
  feature_info                     := NULL;
  num_capture                      := 0;
  num_ds_capture                   := 0;
  num_apply                        := 0;
  num_prop                         := 0;
  feature_usage                    := NULL;
  total_feature_usage              := 0;

  select decode (count(*), 0, 0, 1) into num_capture 
     from dba_capture where UPPER(purpose) = 'XSTREAM STREAMS';

  select decode (count(*), 0, 0, 1) into num_ds_capture 
     from dba_capture where UPPER(purpose) = 'XSTREAM STREAMS' and
                            UPPER(capture_type) = 'DOWNSTREAM';

  select decode (count(*), 0, 0, 1) into num_apply 
     from dba_apply where UPPER(purpose) = 'XSTREAM STREAMS';

  select decode (count(*), 0, 0, 1) into num_prop from dba_propagation;

  total_feature_usage := num_capture + num_apply + num_prop; 

  feature_usage := feature_usage ||
        'tcap:'                  || num_capture
      ||' dscap:'                || num_ds_capture
      ||' app:'                  || num_apply
      ||' prop:'                 || num_prop;

  feature_info   := to_clob(feature_usage);
  if (total_feature_usage > 0) THEN
      feature_boolean := 1;
  end if;
  if(num_capture > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_apply > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_prop > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;

END dbms_feature_xstream_streams;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_GOLDENGATE
 *  The procedure to detect usage for Services
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_goldengate
      (feature_boolean  OUT  NUMBER, 
       aux_count        OUT  NUMBER, 
       feature_info     OUT  CLOB)
AS
  -- Based on goldengate usage
  num_capture                             NUMBER;
  num_ds_capture                          NUMBER;
  num_apply                               NUMBER;
  num_trigger_suppression                 NUMBER;
  num_transient_duplicate                 NUMBER;
  num_dblogreader                         NUMBER;
  num_ggsddltrigopt                       NUMBER;
  feature_usage                           VARCHAR2(4000);
  total_feature_usage                     NUMBER;
  num_dbencryption                        NUMBER;
  num_ggsession                           NUMBER;
  num_delcascadehint                      NUMBER;
  num_suplog                              NUMBER;
  num_procrepl                            NUMBER;
BEGIN
  -- initialize
  feature_boolean                  := 0;
  aux_count                        := 0;
  feature_info                     := NULL;
  num_capture                      := 0;
  num_ds_capture                   := 0;
  num_apply                        := 0;
  num_trigger_suppression          := 0;
  num_transient_duplicate          := 0;
  num_dblogreader                  := 0;
  num_ggsddltrigopt                := 0;
  feature_usage                    := NULL;
  total_feature_usage              := 0;
  num_dbencryption                 := 0;
  num_ggsession                    := 0;
  num_delcascadehint               := 0;
  num_suplog                       := 0;
  num_procrepl                     := 0;

  select decode (count(*), 0, 0, 1) into num_capture 
     from dba_capture where UPPER(purpose) = 'GOLDENGATE CAPTURE';

  select decode (count(*), 0, 0, 1) into num_ds_capture 
     from dba_capture where UPPER(purpose) = 'GOLDENGATE CAPTURE' and
                            UPPER(capture_type) = 'DOWNSTREAM';

  select decode (count(*), 0, 0, 1) into num_apply from dba_apply 
     where UPPER(purpose) IN ('GOLDENGATE APPLY', 'GOLDENGATE CAPTURE');

  select sum(count) into num_dblogreader 
     from GV$GOLDENGATE_CAPABILITIES where name like 'DBLOGREADER';
  
  select sum(count) into num_transient_duplicate
     from GV$GOLDENGATE_CAPABILITIES where name like 'TRANSIENTDUPLICATE';

  select sum(count) into num_trigger_suppression
     from GV$GOLDENGATE_CAPABILITIES where name like 'TRIGGERSUPPRESSION';

  select sum(count) into num_ggsddltrigopt 
     from GV$GOLDENGATE_CAPABILITIES where name like 'DDLTRIGGEROPTIMIZATION';
 
  select sum(count) into num_dbencryption
     from GV$GOLDENGATE_CAPABILITIES where name like 'DBENCRYPTION';

  select sum(count) into num_ggsession
     from GV$GOLDENGATE_CAPABILITIES where name like 'GGSESSION';

  select sum(count) into num_delcascadehint
     from GV$GOLDENGATE_CAPABILITIES where name like 'DELETECASCADEHINT';

  select sum(count) into num_suplog
     from GV$GOLDENGATE_CAPABILITIES where name like 'SUPPLEMENTALLOG';

  select sum(count) into num_procrepl
     from GV$GOLDENGATE_CAPABILITIES where name like 'PROCREPLICATION';

  total_feature_usage := num_capture + num_apply + num_dblogreader + 
     num_transient_duplicate + num_ggsddltrigopt + num_trigger_suppression +
     num_dbencryption + num_ggsession + num_delcascadehint + num_suplog +
     num_procrepl;

  feature_usage := feature_usage ||
        'tcap:'                  || num_capture
      ||' dscap:'                || num_ds_capture
      ||' app:'                  || num_apply
      ||' dblogread:'            || num_dblogreader
      ||' tdup:'                 || num_transient_duplicate
      ||' suptrig:'              || num_trigger_suppression
      ||' dtrigopt:'             || num_ggsddltrigopt
      ||' dbenc:'                || num_dbencryption
      ||' ggsess:'               || num_ggsession
      ||' delhint:'              || num_delcascadehint
      ||' suplog:'               || num_suplog
      ||' suplog:'               || num_procrepl;
   
  feature_info   := to_clob(feature_usage);
  if (total_feature_usage > 0) THEN
      feature_boolean := 1;
  end if;
  if(num_capture > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_apply > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_dblogreader > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_transient_duplicate > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_ggsddltrigopt > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_trigger_suppression > 0 ) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_dbencryption > 0) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_ggsession > 0) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_delcascadehint > 0) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_suplog > 0) THEN
       aux_count      :=  aux_count+1;
  end if;
  if(num_procrepl > 0) THEN
       aux_count      :=  aux_count+1;
  end if;

END dbms_feature_goldengate;
/

show errors;

/****************************************************************
 * DBMS_FEATURE_USER_MVS
 * The procedure to detect usage for MATERIALIZED VIEWS (USER)
 ****************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_USER_MVS
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
 num_mv         number;     -- total number of user mvs (user mvs of all types)
 num_ondmd      number;                                   -- on-demand user mvs
 num_cmplx      number;  -- complex user mvs (mvs that can't be fast refreshed)
 num_mav        number;                                          -- (user) mavs
 num_mjv        number;                                          -- (user) mjvs
 num_mav1       number;                                         -- (user) mav1s
 num_oncmt      number;                                   -- on-commit user mvs
 num_enqrw      number;                           -- rewrite enabled (user) mvs
 num_rmt        number;                                    -- remote (user) mvs
 num_pk         number;                                        -- pk (user) mvs
 num_rid        number;                                     -- rowid (user) mvs
 num_obj        number;                                    -- object (user) mvs
 num_ofprf      number;                                 -- out-of-place refresh
 num_sync       number;                                     -- sync refresh mvs
 feature_usage  varchar2(1000);
 user_mv_test   varchar2(100);

BEGIN
  -- initialize
  num_mv := 0;                                   
  num_ondmd := 0;
  num_cmplx := 0;
  num_mav := 0;
  num_mjv := 0;
  num_mav1 := 0;
  num_oncmt := 0;
  num_enqrw := 0;
  num_rmt := 0;
  num_pk := 0;
  num_rid := 0;
  num_obj := 0;
  num_ofprf := 0;
  num_sync := 0;
  user_mv_test := ' s.sowner not in (''SYS'', ''SYSTEM'', ''SH'', ''SYSMAN'')';

  feature_boolean := 0;
  aux_count := 0;

  /* get the user mv count (user mvs of all types) */
  execute immediate 'select count(*) from dba_mviews
                     where owner not in (''SYS'', ''SYSTEM'', ''SH'', ''SYSMAN'')'
  into num_mv;

  if (num_mv > 0)
  then

    /* get number of rowid (user) mvs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 16) = 16 and' || user_mv_test
    into num_rid;

    /* get number of pk (user) mvs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 32) = 32 and' || user_mv_test
    into num_pk;

    /* get number of on-demand user mvs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 64) = 64 and' || user_mv_test
    into num_ondmd;

    /* get number of complex user mvs (mvs that can't be fast refreshed) */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 256) = 256 and' || user_mv_test
    into num_cmplx;

    /* get number of (user) mavs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 4096) = 4096 and' || user_mv_test
    into num_mav;

    /* get number of (user) mjvs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 8192) = 8192 and' || user_mv_test
    into num_mjv;

    /* get number of (user) mav1s */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 16384) = 16384 and' || user_mv_test
    into num_mav1;

    /* get number of on-commit user mvs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 32768) = 32768 and' || user_mv_test
    into num_oncmt;

    /* get number of rewrite enabled (user) mvs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 1048576) = 1048576 and' ||
                       user_mv_test
    into num_enqrw;

    /* get number of remote (user) mvs */
    execute immediate 'select count(*) from snap$ s
                       where s.mlink is not null and' || user_mv_test
    into num_rmt;

    /* get number of object (user) mvs */
    execute immediate 'select count(*) from snap$ s
                       where bitand(s.flag, 536870912) = 536870912 and' ||
                       user_mv_test
    into num_obj;
   
    /* get number of SyncRefresh mvs */
    execute immediate 'select sum(count#) from mv_refresh_usage_stats$ 
                       where refresh_method# = ''SYNC'''

    into num_sync;

    /* get number of out-of-place refreshes */
    execute immediate 'select sum(count#) from SYS.MV_REFRESH_USAGE_STATS$
                       where OUT_OF_PLACE# = ''YES'''
    into num_ofprf;

    feature_boolean := 1;

    feature_usage := 'total number of user mvs (user mvs of all types):' || to_char(num_mv) ||
          ',' || ' num of (user) mavs:' || to_char(num_mav) ||
          ',' || ' num of (user) mjvs:' || to_char(num_mjv) ||
          ',' || ' num of (user) mav1s:' || to_char(num_mav1) ||
          ',' || ' num of on-demand user mvs:' || to_char(num_ondmd) ||
          ',' || ' num of on-commit user mvs:' || to_char(num_oncmt) ||
          ',' || ' num of remote (user) mvs:' || to_char(num_rmt) ||
          ',' || ' num of pk (user) mvs:' || to_char(num_pk) ||
          ',' || ' num of rowid (user) mvs:' || to_char(num_rid) ||
          ',' || ' num of object (user) mvs:' || to_char(num_obj) ||
          ',' || ' num of rewrite enabled (user) mvs:' || to_char(num_enqrw) ||
          ',' || ' num of complex user mvs:' || to_char(num_cmplx) ||
          ',' || ' num of out-of-place refreshes:' || to_char(num_ofprf) ||
          ',' || ' num of SyncRefresh mvs:' || to_char(num_sync) ||
          '.';

    feature_info := to_clob(feature_usage);
  else
    feature_info := to_clob('User MVs do not exist.');
  end if;

end;
/

/****************************************************************
 * DBMS_FEATURE_IOT
 * The procedure to detect Index Organized Table usage
 ****************************************************************/
create or replace procedure DBMS_FEATURE_IOT(
    feature_boolean OUT NUMBER,
    aux_count       OUT NUMBER,
    feature_info    OUT CLOB)
AS
    feature_usage                 varchar2(1000);
    num_iot                       number;
    num_iotpart_index_segments    number;
    num_iotpart_overflow_segments number;

BEGIN
    feature_boolean             := 0;
    aux_count                   := 0;

    -- If the IOT or its overflow segment is stored in a tablespace other than 
    -- SYSTEM or SYSAUX, count it.
    execute immediate 
       'select count(*) from dba_tables, dba_indexes where ' ||
       'dba_tables.iot_type is not null and dba_indexes.table_name ' ||
       '= dba_tables.table_name and (dba_indexes.tablespace_name ' ||
       'not in (''SYSTEM'', ''SYSAUX'', ''TEMP'', ''SYSEXT'')' ||
       'or dba_tables.tablespace_name ' ||
       'not in (''SYSTEM'', ''SYSAUX'', ''TEMP'', ''SYSEXT''))'
    into num_iot;

    -- Partitioned IOT/overflow segment
    -- for the IOT overflow segment partitions
    -- Bug 21059266:
    -- Query rewritten in terms of Data Dictionary base tables; the three
    -- bitands correspond to the dba_tables.iot_type column in the original
    -- query. That column is not null if any of the following bitands are true:
    -- (bitand(property, 64) = 64) means IOT
    -- (bitand(property,512) = 512) means IOT_OVERFLOW
    -- (bitand(flags, 536870912) = 536870912) IOT_MAPPING
    execute immediate
       'select count(OBJ#) ' ||
       'from tabpart$ ' ||
       'where BO# in ' ||
          '((select OBJ# ' ||
            'from tab$ ' ||
            'where (((bitand(property, 64) = 64) or ' || 
                   '(bitand(property,512) = 512) or ' ||
                   '(bitand(flags, 536870912) = 536870912)) and ' || 
                   'TS# in ' ||
                     '(select TS# ' ||
                      'from ts$ ' ||
                      'where NAME not in ' ||
                         '(''SYSTEM'', ''SYSAUX'', ''TEMP'', ''SYSEXT'')))))'
    into num_iotpart_overflow_segments;

    -- for the IOT index segment partitions
    execute immediate
       'select count(*) ' ||
       'from dba_tables, dba_indexes, dba_ind_partitions where ' ||
       'dba_tables.iot_type is not null and dba_tables.table_name = ' ||
       'dba_indexes.table_name and dba_indexes.index_name = ' ||
       'dba_ind_partitions.index_name and ' ||
       'dba_ind_partitions.tablespace_name ' ||
       'not in (''SYSTEM'', ''SYSAUX'', ''TEMP'', ''SYSEXT'')'
    into num_iotpart_index_segments;

    -- Composite partitioning is not supported for IOTs

    --Summary
    feature_usage :=
        ' Index Organized Table Feature Usage: ' ||
                'Index Organized Tables: ' || 
                  to_char(num_iot) ||
        ', ' || 'Index Organized Table Partitions ' ||
        '(Index and Overflow Partitions) ' || 
                  to_char(num_iotpart_index_segments + 
                          num_iotpart_overflow_segments);

     if (num_iot + num_iotpart_index_segments + 
         num_iotpart_overflow_segments > 0) then
      feature_boolean := 1;
      feature_info := to_clob(feature_usage);
    else
      feature_boolean := 0;
      feature_info := to_clob('Index Organized Tables Not Detected');
    end if;

END;
/
show errors;

/****************************************************************
 * DBMS_FEATURE_IMC
 * The procedure to detect In-Memory Column Store usage
 ****************************************************************/
create or replace procedure DBMS_FEATURE_IMC(
    feature_boolean OUT NUMBER,
    aux_count       OUT NUMBER,
    feature_info    OUT CLOB)
AS
    feature_usage               varchar2(1000);
    num_tab                     number;
    num_tab_part                number;
    num_tab_subpart             number;
    num_tab_cc                  number;
    num_tab_part_cc             number;
    num_segs                    number;
    inmemory_size_value         number;

BEGIN
    feature_boolean             := 0;
    aux_count                   := 0;
    inmemory_size_value         := 0;
    
    -- check if any segments are enabled for in-memory
    execute immediate 
       'select count(*) from dba_tables where ' ||
       'inmemory_compression is not null and ' ||
       'not (tablespace_name in (''SYSTEM'', ''SYSAUX'') ' ||
       'and owner like ''SYS'')'
    into num_tab;

    execute immediate 
       'select count(*) from dba_tab_partitions where ' ||
       'inmemory_compression is not null and ' ||
       'not (tablespace_name in (''SYSTEM'', ''SYSAUX'') ' ||
       'and table_owner like ''SYS'') and table_name ' ||
       'not like ''BIN$%'''
    into num_tab_part;

    execute immediate 
       'select count(*) from dba_tab_subpartitions where ' ||
       'inmemory_compression is not null and ' ||
       'not (tablespace_name in (''SYSTEM'', ''SYSAUX'') ' ||
       'and table_owner like ''SYS'') and table_name ' ||
       'not like ''BIN$%'''
    into num_tab_subpart;

    -- check if any segments were enabled for cellmemory
    -- note some tables may show up twice as in-memory and cellmemory
    execute immediate 
       'select count(*) from dba_tables where ' ||
       'cellmemory like ''MEMCOMPRESS%'' and ' ||
       'not (tablespace_name in (''SYSTEM'', ''SYSAUX'') ' ||
       'and owner like ''SYS'')'
    into num_tab_cc;

    execute immediate 
       'select count(*) from dba_tab_partitions where ' ||
       'cellmemory like ''MEMCOMPRESS%'' and ' ||
       'not (tablespace_name in (''SYSTEM'', ''SYSAUX'') ' ||
       'and table_owner like ''SYS'') and table_name ' ||
       'not like ''BIN$%'''
    into num_tab_part_cc;

    -- check if any segments are actually in-memory
    execute immediate 
       'select count(*) from gv$im_segments_detail where ' ||
       'segtype=0'
    into num_segs;

    -- check the value of the parameter "inmemory_size" from 
    -- all instances
    execute immediate 
       'select nvl(max(value),0) from gv$parameter where ' || 
       'name = ''inmemory_size'''
    into inmemory_size_value;

    --Summary
    feature_usage :=
        ' In-Memory Column Store Feature Usage: ' ||
                'In-Memory Column Store Tables: ' || 
                  to_char(num_tab) ||
        ', ' || 'In-Memory Column Store Table Partitions: ' || 
                  to_char(num_tab_part) ||
        ', ' || 'In-Memory Column Store Table Subpartitions: ' ||
                  to_char(num_tab_subpart) ||
        ', ' || 'Total In-Memory Column Store Segments Populated: ' ||
                  to_char(num_segs) ||
        ', ' || 'Cellcache Column Store Tables: ' || 
                  to_char(num_tab_cc) ||
        ', ' || 'Cellcache Column Store Table Partitions: ' || 
                  to_char(num_tab_part_cc);

     if (((num_tab + num_tab_part + num_tab_subpart + num_segs > 0)
          AND (inmemory_size_value > 0)) 
    OR  (num_tab_cc + num_tab_part_cc > 0))
        then
      feature_boolean := 1;
      feature_info := to_clob(feature_usage);
    else
      feature_boolean := 0;
      feature_info := to_clob('In-Memory Column Store Not Detected');
    end if;
END;
/
show errors;

/****************************************************************
 * DBMS_FEATURE_IM_EXPRESSIONS
 * The procedure to detect In-Memory Virtual Columns and 
 * In-Memory Expressions usage
 ****************************************************************/
create or replace procedure DBMS_FEATURE_IM_EXPRESSIONS(
    feature_boolean OUT NUMBER,
    aux_count       OUT NUMBER,
    feature_info    OUT CLOB)
AS
    feature_usage                    varchar2(2000);
    num_imes_created                 number  := 0;
    num_vcs_enabled_for_inmem        number  := 0;
    num_osons_created                number  := 0;
    num_exp_inmem                    number  := 0;
    num_oson_inmem                   number  := 0;
    inmemory_size_value              number  := 0;
    inmemory_exp_usage_value         number  := 0;
    inmemory_virtual_columns_value   number  := 0;
    feature_params_enabled           boolean := FALSE;
    feature_check_static             boolean := FALSE;
    feature_check_dynamic            boolean := FALSE;
BEGIN
    feature_boolean := 0;
    aux_count       := 0;
   
    -- Check total number of IMEs created regardless of memcompress level
    -- Ignore OSON
    execute immediate
        'select count(*) from sys.col$ where name like ''SYS\_IME%''' ||
        ' escape ''\'' and name not like ''SYS\_IME\_OSON\_%'' escape ''\'''
    into num_imes_created;
   
    -- Check total number of OSON created regardless of in-memory compression
    execute immediate
        'select count(*) from sys.col$ where name like ''SYS\_IME\_OSON\_%''' ||
        ' escape ''\'''
    into num_osons_created;

    -- Check the number of VCs explicitly enabled for in-memory storage
    -- SYS_IMEs do not show up in this view
    execute immediate
        'select count(*) from gv$im_column_level where segment_column_id = 0' ||
        ' and inmemory_compression in (''DEFAULT'', ''NO MEMCOMPRESS'',' ||
        ' ''FOR DML'', ''FOR QUERY LOW'', ''FOR QUERY HIGH'',' ||
        ' ''FOR CAPACITY LOW'', ''FOR CAPACITY HIGH'')'
    into num_vcs_enabled_for_inmem;

    -- Check if any VC or IME (not OSON) are populated in-memory
    execute immediate
        'select count(*) from gv$im_imecol_cu where column_name not like' ||
        ' ''SYS\_IME\_OSON%'' escape ''\'''
    into num_exp_inmem;
 
    -- Check if any OSON is populated in-memory
    execute immediate
        'select count(*) from gv$im_imecol_cu where column_name like' ||
        ' ''SYS\_IME\_OSON%'' escape ''\'''
    into num_oson_inmem; 

    -- Check the value of "inmemory_size" from all instances
    execute immediate 
       'select nvl(max(value),0) from gv$parameter where ' || 
       'name = ''inmemory_size'''
    into inmemory_size_value;

    -- Check the value of "inmemory_virtual_columns" from all instances
    execute immediate
       'select count(*) from gv$parameter where ' ||
       'name = ''inmemory_virtual_columns'' and value != ''DISABLE'''
    into inmemory_virtual_columns_value;

     -- Check the value of "inmemory_expressions_usage" from all instances
    execute immediate
       'select count(*) from gv$parameter where ' ||
       'name = ''inmemory_expressions_usage'' and value != ''DISABLE'''
    into inmemory_exp_usage_value;

    -- Summary
    feature_usage :=
                ' In-Memory Virtual Columns enabled on: ' ||
                to_char(inmemory_virtual_columns_value) || ' instances' ||
                ', In-Memory Expressions enabled on: ' ||
                to_char(inmemory_exp_usage_value) || ' instances' ||
                ', Number of IMEs created: ' || 
                to_char(num_imes_created) ||
                ', Number of VCs explicitly enabled for in-memory: ' ||
                to_char(num_vcs_enabled_for_inmem) ||
                ', Number of OSONs created: ' ||
                to_char(num_osons_created) ||
                ', Number of VC or IME CUs in-memory: ' ||
                to_char(num_exp_inmem) ||
                ', Number of OSON CUs in-memory: ' ||
                to_char(num_oson_inmem);

    -- Check if either parameter is ENABLEd and in-memory size is setup
    feature_params_enabled := (
                               ((inmemory_exp_usage_value > 0) OR
                                (inmemory_virtual_columns_value > 0)) AND
                               (inmemory_size_value > 0)
                              );

    -- Static feature check based on IMEs created or VCs enabled for IM
    feature_check_static := ((num_imes_created > 0) OR
                             (num_vcs_enabled_for_inmem > 0));

    -- Dynamic feature check based on IME/VC/OSON populated in-memory
    -- This is necessary because of the ENABLE mode in inmemory_virtual_columns
    feature_check_dynamic := ((num_exp_inmem > 0) OR (num_oson_inmem > 0));

    -- Feature Check
    if (feature_params_enabled) then
      if (feature_check_static OR feature_check_dynamic) then
        feature_boolean := 1;
        feature_info := to_clob(feature_usage);
      end if;
    end if;
END;
/
show errors;

/********************************************************************
 * DBMS_FEATURE_IM_ADO
 * The procedure to detect usage for IM ADO policies. 
 ********************************************************************/

create or replace procedure DBMS_FEATURE_IM_ADO
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage         varchar2(1000);
    num_im_ado_pol        number := 0;
    num_obj_pol           number := 0;
    im_size               number := 0;

begin
    -- initialize
    feature_boolean := 0;
    aux_count       := 0;

    -- check for ILM usage by counting policies in ILM dictionary
    select count(*) into num_im_ado_pol
      from sys.ilmpolicy$
     where pol_subtype = 1;

    if (num_im_ado_pol > 0) then
      
     -- check the value of the parameter "inmemory_size" from 
     -- all instances
     execute immediate 
        'select nvl(max(value),0) from gv$parameter where ' || 
        'name = ''inmemory_size'''
      into im_size;

     if (im_size > 0) then 
        feature_boolean := 1;

        select count(*) into num_obj_pol
          from sys.ilmobj$
         where policy# in (select policy# from sys.ilmpolicy$
                          where pol_subtype = 1);

        feature_usage   :=
                'Number of IM ADO Policies: ' || to_char(num_im_ado_pol) ||
        ', ' || 'Number of Objects Affected: ' || to_char(num_obj_pol);
        feature_info    := to_clob(feature_usage);

     end if;      
   end if;
end;
/
show errors;

/****************************************************************
 * DBMS_FEATURE_IM_JOINGROUPS
 * The procedure to detect In-Memory Join Group Usage
 ****************************************************************/
create or replace procedure DBMS_FEATURE_IM_JOINGROUPS(
    feature_boolean OUT NUMBER,
    aux_count       OUT NUMBER,
    feature_info    OUT CLOB)
AS
    feature_usage               varchar2(1000);
    num_jg                      number;
    inmemory_size_value         number;
BEGIN
    feature_boolean             := 0;
    aux_count                   := 0;
    inmemory_size_value         := 0;
   
    execute immediate
        'select count(distinct(domain#)) from im_joingroup$'
    into num_jg;

    -- check the value of the parameter "inmemory_size" from 
    -- all instances
    execute immediate 
       'select nvl(max(value),0) from gv$parameter where ' || 
       'name = ''inmemory_size'''
    into inmemory_size_value;

    --Summary
    feature_usage :=
        ' In-Memory Join Groups Feature Usage: ' ||
                ' Number of join groups created: ' || 
                  to_char(num_jg);

     if ((num_jg > 0) AND (inmemory_size_value > 0)) then
      feature_boolean := 1;
      feature_info := to_clob(feature_usage);
    end if;
END;
/
show errors;

/****************************************************************
 * DBMS_FEATURE_IM_FORSERVICE
 * The procedure to detect In-Memory For Service usage
 ****************************************************************/
create or replace procedure DBMS_FEATURE_IM_FORSERVICE(
    feature_boolean OUT NUMBER,
    aux_count       OUT NUMBER,
    feature_info    OUT CLOB)
AS
    feature_usage               varchar2(1000);
    num_tablespaces             number;
    num_tab                     number;
    num_tab_part                number;
    num_tab_subpart             number;
BEGIN
    feature_boolean             := 0;
    aux_count                   := 0;
    num_tablespaces             := 0;
    num_tab                     := 0;
    num_tab_part                := 0;
    num_tab_subpart             := 0;
   
    execute immediate
        'select count(*) from dba_tables where ' ||
        'inmemory_service = ''USER_DEFINED'''
    into num_tab;

    execute immediate
        'select count(*) from dba_tab_partitions where ' ||
        'inmemory_service = ''USER_DEFINED'''
    into num_tab_part;

    execute immediate
        'select count(*) from dba_tab_subpartitions where ' ||
        'inmemory_service = ''USER_DEFINED'''
    into num_tab_subpart;

    execute immediate
        'select count(*) from dba_tablespaces where ' ||
        'def_inmemory_service = ''USER_DEFINED'''
    into num_tablespaces;
    
    --Summary
    feature_usage :=
        ' In-Memory For Service Feature Usage: ' ||
                'In-Memory USER_DEFINED For Service Tables: ' || 
                  to_char(num_tab) ||
        ', ' || 'In-Memory USER_DEFINED For Service Table Partitions: ' || 
                  to_char(num_tab_part) ||
        ', ' || 'In-Memory USER_DEFINED For Service Table Subpartitions: ' ||
                  to_char(num_tab_subpart) ||
        ', ' || 'In-Memory USER_DEFINED For Service Tablespaces: ' ||
                  to_char(num_tablespaces);

     if ((num_tab > 0) OR (num_tab_part > 0) OR (num_tab_subpart > 0) OR
         (num_tablespaces > 0)) then
      feature_boolean := 1;
      feature_info := to_clob(feature_usage);
     end if;
END;
/
show errors;
    
/****************************************************************
 * DBMS_FEATURE_ADV_TABCMP
 * The procedure to detect ADVANCED Table Compression usage
 ****************************************************************/
create or replace procedure DBMS_FEATURE_ADV_TABCMP(
    feature_boolean OUT NUMBER,
    aux_count       OUT NUMBER,
    feature_info    OUT CLOB)
AS
    feature_usage               varchar2(1000);
    num_tab                     number;
    num_tab_part                number;
    num_tab_subpart             number;

BEGIN
    feature_boolean             := 0;
    aux_count                   := 0;

    -- dbms_compression might create tables with 
    -- prefixes 'CMP4%' which we want to ignore
    execute immediate 
       'select count(*) from dba_tables where ' ||
       'compress_for = ''ADVANCED'' and table_name ' ||
       'not like ''' 
        || prvt_compression.COMP_TMP_OBJ_PREFIX || '%'''
    into num_tab;

    execute immediate 
       'select count(*) from dba_tab_partitions where ' ||
       'compress_for = ''ADVANCED'' '
    into num_tab_part;

    execute immediate 
       'select count(*) from dba_tab_subpartitions where ' ||
       'compress_for = ''ADVANCED'' '
    into num_tab_subpart;

    --Summary
    feature_usage :=
        ' ADVANCED Table Compression Feature Usage: ' ||
                ' Tables compressed for ADVANCED: ' || 
                  to_char(num_tab) ||
        ', ' || ' Table partitions compressed for ADVANCED: ' || 
                  to_char(num_tab_part) ||
        ', ' || ' Table subpartitions compressed for ADVANCED: ' ||
                  to_char(num_tab_subpart); 

     if (num_tab + num_tab_part + num_tab_subpart > 0) then
      feature_boolean := 1;
      feature_info := to_clob(feature_usage);
    else
      feature_boolean := 0;
      feature_info := to_clob('ADVANCED Table Compression Not Detected');
    end if;
END;
/
show errors;

/****************************************************************
 * DBMS_FEATURE_ADV_IDXCMP
 * The procedure to detect Advanced Index Compression usage
 ****************************************************************/
create or replace procedure DBMS_FEATURE_ADV_IDXCMP(
    feature_boolean OUT NUMBER,
    aux_count       OUT NUMBER,
    feature_info    OUT CLOB)
AS
    feature_usage               varchar2(1000);
    oltp_high_idx_cnt           number;
    oltp_low_idx_cnt            number;
    oltp_high_part_idx_cnt      number;
    oltp_low_part_idx_cnt       number;
    num_oltp_high               number;
    num_oltp_low                number;
    blk_oltp_high               number;
    blk_oltp_low                number;
    def_oltp_high               number;
    def_oltp_low                number;
    tmp_pattern                 varchar(50);

BEGIN
    feature_boolean             := 0;
    aux_count                   := 0;
    --Pattern used by Compression Advisor for creating temporary 
    --compressed objects
    tmp_pattern                 := '''' || 
                                   prvt_compression.COMP_TMP_OBJ_PREFIX ||
                                   '%''';

    --Check for ADVANCED HIGH seg, block, deferred seg
    execute immediate 'select count(*) from seg$ s, ind$ i, obj$ o ' ||
      ' where s.type# = 6 AND ' ||
            ' s.user# not in (select user# from user$ ' ||
                ' where name in (''SYS'' , ''SYSTEM'' )) AND ' ||
            ' bitand(s.spare1, 2048) = 2048 AND ' ||
            ' bitand(s.spare1, 16777216 + 1048576) = 16777216 AND ' ||
            ' s.ts# = i.ts# AND s.file# = i.file# AND ' ||
            ' s.block# = i.block# AND i.obj# = o.obj# AND ' ||
            ' o.name not like ' || tmp_pattern
      into num_oltp_high;

    if (num_oltp_high = 0) then
      blk_oltp_high := 0;
    else
      execute immediate 'select sum(blocks) from seg$ s, ind$ i, obj$ o ' ||
        ' where s.type# = 6 AND ' ||
              ' s.user# not in (select user# from user$ ' ||
                  ' where name in (''SYS'', ''SYSTEM'' )) AND' ||
              ' bitand(s.spare1, 2048) = 2048 AND ' ||
              ' bitand(s.spare1, 16777216 + 1048576) = 16777216 AND ' ||
              ' s.ts# = i.ts# AND s.file# = i.file# AND ' ||
              ' s.block# = i.block# AND i.obj# = o.obj# AND ' ||
              ' o.name not like ' || tmp_pattern
        into blk_oltp_high;
    end if;

    execute immediate 'select count(*) from deferred_stg$ ds ' ||
      ' where ds.obj# in (select obj# from obj$ ob' ||
                ' where ob.owner# not in (select user# from user$ ' ||
                          ' where name in (''SYS'', ''SYSTEM'' ))) AND' ||
            ' bitand(ds.flags_stg, 4) = 4 AND ' ||
            ' bitand(ds.cmpflag_stg, 6) = 2 '
      into def_oltp_high;

    --Check for ADVANCED LOW seg, block, deferred seg
    execute immediate 'select count(*) from seg$ s, ind$ i, obj$ o ' ||
      ' where s.type# = 6 AND ' ||
            ' s.user# not in (select user# from user$ ' ||
                ' where name in (''SYS'', ''SYSTEM'' )) AND' ||
            ' bitand(s.spare1, 2048) = 2048 AND ' ||
            ' bitand(s.spare1, 16777216 + 1048576) = 1048576 AND ' ||
            ' s.ts# = i.ts# AND s.file# = i.file# AND ' ||
            ' s.block# = i.block# AND i.obj# = o.obj# AND ' ||
            ' o.name not like ' || tmp_pattern
      into num_oltp_low;

    if (num_oltp_low = 0) then
      blk_oltp_low := 0;
    else
      execute immediate 'select sum(blocks) from seg$ s, ind$ i, obj$ o  ' ||
        ' where s.type# = 6 AND ' ||
              ' s.user# not in (select user# from user$ ' ||
                  ' where name in (''SYS'', ''SYSTEM'' )) AND' ||
              ' bitand(s.spare1, 2048) = 2048 AND ' ||
              ' bitand(s.spare1, 16777216 + 1048576) = 1048576  AND ' ||
              ' s.ts# = i.ts# AND s.file# = i.file# AND ' ||
              ' s.block# = i.block# AND i.obj# = o.obj# AND ' ||
              ' o.name not like ' || tmp_pattern
        into blk_oltp_low;
    end if;

    execute immediate 'select count(*) from deferred_stg$ ds ' ||
      ' where ds.obj# in (select obj# from obj$ ob' ||
                ' where ob.owner# not in (select user# from user$ ' ||
                          ' where name in (''SYS'', ''SYSTEM'' ))) AND' ||
            ' bitand(ds.flags_stg, 4) = 4 AND ' ||
            ' bitand(ds.cmpflag_stg, 6) = 4 '
      into def_oltp_low;

    --Summary
    feature_usage :=
        ' Advanced Index Compression feature usage: ' ||
                ' Segments Compressed for ADVANCED HIGH: ' || 
                  to_char(num_oltp_high) ||
        ', ' || ' Blocks Compressed for ADVANCED HIGH: ' || 
                  to_char(blk_oltp_high) ||
        ', ' || ' Deferred Segements Compressed for ADVANCED HIGH: ' ||
                  to_char(def_oltp_high) ||
        ', ' || ' Segments Compressed for ADVANCED LOW: ' || 
                  to_char(num_oltp_low) ||
        ', ' || ' Blocks Compressed for ADVANCED LOW: ' || 
                  to_char(blk_oltp_low) ||
        ', ' || ' Deferred Segements Compressed for ADVANCED LOW: ' ||
                  to_char(def_oltp_low);

    if (num_oltp_high + def_oltp_high + num_oltp_low + def_oltp_low > 0) then
      feature_boolean := 1;
      feature_info := to_clob(feature_usage);
    else
      feature_boolean := 0;
      feature_info := to_clob('Advanced Index Compression not detected');
    end if;
END;
/
show errors;


/****************************************************************
 * DBMS_FEATURE_HCC
 * The procedure to detect usage for Hybrid Columnar Compression
 ****************************************************************/

create or replace procedure DBMS_FEATURE_HCC
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage         varchar2(1000);
    num_cmp_dollar        number;
    num_level1            number;
    num_level2            number;
    num_level3            number;
    num_level4            number;
    num_hcc               number;
    num_dmls              number;
    num_rll               number;
    blk_level1            number;
    blk_level2            number;
    blk_level3            number;
    blk_level4            number;
    blk_nonhcc            number;
    blk_nonhcctry         number;
    blk_rll               number;

begin
    -- initialize
    feature_boolean := 0;
    aux_count := 0;
    num_cmp_dollar := 0;
    num_hcc := 0;
    num_level1  := 0;
    num_level2  := 0;
    num_level3  := 0;
    num_level4  := 0;
    blk_level1 := 0;
    blk_level2 := 0;
    blk_level3 := 0;
    blk_level4 := 0;

    execute immediate 'select count(*) from compression$ '
        into num_cmp_dollar;

    -- check if there is something compressed
    execute immediate 'select count(*) from seg$ s ' ||
         ' where bitand(s.spare1, 234881024) = 33554432 OR '  ||
               ' bitand(s.spare1, 234881024) = 67108864 OR '  ||
               ' bitand(s.spare1, 234881024) = 100663296 OR ' ||
               ' bitand(s.spare1, 234881024) = 134217728'         
        into num_hcc;

    if (num_hcc > 0) then
    
        feature_boolean := 1;

        -- check for HCC for Query LOW (level 1)
        execute immediate 'select count(*) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 33554432 '
           into num_level1;

        execute immediate 'select sum(blocks) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 33554432 '
           into blk_level1;

        -- check for HCC for Query HIGH (level 2)
        execute immediate 'select count(*) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 67108864 '
           into num_level2;

        execute immediate 'select sum(blocks) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 67108864 '
           into blk_level2;

        -- check for HCC for Archive LOW (level 3)
        execute immediate 'select count(*) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 100663296 '
           into num_level3;

        execute immediate 'select sum(blocks) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 100663296 '
           into blk_level3;
           
        -- check for HCC for Archive HIGH (level 4)
        execute immediate 'select count(*) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 134217728 '
           into num_level4;

        execute immediate 'select sum(blocks) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 234881024) = 134217728 '
           into blk_level4;
           

        -- track OLTP compression (non-HCC compression) w/in HCC
        execute immediate 'select value from v$sysstat' ||
            ' where name like ''HCC block compressions completed'''
            into blk_nonhcc;

        execute immediate 'select value from v$sysstat' ||
            ' where name like ''HCC block ' ||
            'compressions attempted'''
            into blk_nonhcctry;

        execute immediate 'select value from v$sysstat' ||
            ' where name like ''HCC DML conventional'''
            into num_dmls;

        -- check for HCC with Row Level Locking
        execute immediate 'select count(*) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 2147483648) = 2147483648 '
           into num_rll;

        execute immediate 'select sum(blocks) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 2147483648) = 2147483648 '
           into blk_rll;

     feature_usage :=
      'Number of Hybrid Columnar Compressed Segments: ' || to_char(num_hcc) ||
        ', ' || ' Segments Analyzed: ' || to_char(num_cmp_dollar) ||
        ', ' || ' Segments Compressed Query Low: ' || to_char(num_level1) ||
        ', ' || ' Blocks Compressed Query Low: ' || to_char(blk_level1) ||
        ', ' || ' Segments Compressed Query High: ' || to_char(num_level2) ||
        ', ' || ' Blocks Compressed Query High: ' || to_char(blk_level2) ||
        ', ' || ' Segments Compressed Archive Low: ' || to_char(num_level3) ||
        ', ' || ' Blocks Compressed Archive Low: ' || to_char(blk_level3) ||
        ', ' || ' Segments Compressed Archive High: ' || to_char(num_level4) ||
        ', ' || ' Blocks Compressed Archive High: ' || to_char(blk_level4) ||
        ', ' || ' Blocks Compressed Non-HCC: ' || to_char(blk_nonhcc) || 
        ', ' || ' Attempts to Block Compress: ' || to_char(blk_nonhcctry) || 
        ', ' || ' Conventional DMLs: ' || to_char(num_dmls) ||
        ', ' || ' Segments with HCC Row Level Locking: ' || to_char(num_rll) ||
        ', ' || ' Blocks with HCC Row Level Locking: ' || to_char(blk_rll);

        feature_info := to_clob(feature_usage);
    else
        feature_info := to_clob('Hybrid Columnar Compression not detected');
    end if;

end;
/
show errors;

/**************************************************************************
 * DBMS_FEATURE_HCCCONV
 * Procedure to detect use of Hybrid Columnar Compression Conventional Load
 **************************************************************************/
 CREATE OR REPLACE PROCEDURE DBMS_FEATURE_HCCCONV
      ( feature_boolean  OUT  NUMBER,
        aux_count        OUT  NUMBER,
        feature_info     OUT  CLOB) 
 AS
   feature_count NUMBER;
   feature_usage varchar2(100);
 BEGIN 
   -- initialize
   feature_info  := NULL; 
   feature_count := 0; 
 
   execute immediate 'select sum(value) from v$sysstat where name ' ||
   'like ''HCC load conventional bytes compressed''' into feature_count;
   
   feature_usage :=
    'Hybrid Columnar Compressed Conventional: ' || to_char(feature_count);
   feature_info  := to_clob(feature_usage);
 
   if (feature_count > 0) then
     feature_boolean := 1; 
   else  
     feature_boolean := 0; 
   end if;
   aux_count := feature_count;
 END;
 /
 show errors;
 
/****************************************************************
 * DBMS_FEATURE_HCCRLL
 * The procedure to detect usage of Hybrid Columnar Compression
 * Row Level Locking
 ****************************************************************/

create or replace procedure DBMS_FEATURE_HCCRLL
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage         varchar2(1000);
    num_cmp_dollar        number;
    num_hcc               number;
    num_rll               number;
    blk_rll               number;

begin
    -- initialize
    feature_boolean := 0;
    aux_count := 0;
    num_cmp_dollar := 0;
    num_hcc := 0;

    execute immediate 'select count(*) from compression$ '
        into num_cmp_dollar;

    -- check if there is something compressed
    execute immediate 'select count(*) from seg$ s ' ||
         ' where bitand(s.spare1, 100663296) = 33554432 OR ' ||
               ' bitand(s.spare1, 100663296) = 67108864 OR ' ||
               ' bitand(s.spare1, 100663296) = 100663296 '
        into num_hcc;

        -- check for HCC with Row Level Locking
        execute immediate 'select count(*) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 2147483648) = 2147483648 '
           into num_rll;

        execute immediate 'select sum(blocks) from seg$ s ' ||
          ' where bitand(s.spare1, 2048) = 2048 AND ' ||
                ' bitand(s.spare1, 2147483648) = 2147483648 '
           into blk_rll;

    if ((num_rll > 0) OR (blk_rll > 0)) then
    
        feature_boolean := 1;

     feature_usage :=
      'Number of Hybrid Columnar Compressed Segments: ' || to_char(num_hcc) ||
        ', ' || ' Segments Analyzed: ' || to_char(num_cmp_dollar) ||
        ', ' || ' Segments with HCC Row Level Locking: ' || to_char(num_rll) ||
        ', ' || ' Blocks with HCC Row Level Locking: ' || to_char(blk_rll);

        feature_info := to_clob(feature_usage);
    else
        feature_info := to_clob('Hybrid Columnar Compression Row Level Locking not detected');
    end if;

end;
/
show errors;

/********************************************************************
 * DBMS_FEATURE_ILM
 * The procedure to detect usage for Information Lifecycle Management
 ********************************************************************/

create or replace procedure DBMS_FEATURE_ILM
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage         varchar2(1000);
    num_ilm_pol           number := 0;
    num_obj_pol           number := 0;
begin
    -- initialize
    feature_boolean := 0;
    aux_count       := 0;
    feature_info    := to_clob('Information Lifecycle Management not detected');

    -- check for ILM usage by counting policies in ILM dictionary
    execute immediate 'select count(*) from ilm$ '
       into num_ilm_pol;

    if num_ilm_pol > 0 then
      
      feature_boolean := 1;

      execute immediate 'select count(*) from ilmobj$ '
         into num_obj_pol;   

      feature_usage   :=
                'Number of ILM Policies: ' || to_char(num_ilm_pol) ||
        ', ' || 'Number of Objects Affected: ' || to_char(num_obj_pol);
      feature_info    := to_clob(feature_usage);

    end if;      

end;
/
show errors;


/********************************************************************
 * DBMS_FEATURE_HEATMAP
 * The procedure tracks usage of heatmap feature.
 ********************************************************************/

create or replace procedure DBMS_FEATURE_HEATMAP
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage   varchar2(300);
    num_tbs         number := 0;
    num_seg         number := 0;
    num_blocks      number := 0;
begin
    -- initialize
    feature_boolean := 0;
    aux_count       := 0;
    feature_info    := to_clob('Heat Map feature is not used');

    -- Distinct tablespaces tracked
    execute immediate 'select count(distinct(ts#)) from heat_map_stat$ where
                       obj# <> -1'
       into num_tbs;


    -- Distinct segments tracked
    execute immediate 'select count(*) from '||
            ' (select distinct obj#, dataobj#, ts# from heat_map_stat$ where
                       obj# <> -1)' 
            into num_seg;

    -- Blocks tracked in memory
    execute immediate 'select count(*) from x$ktfsimstat'
           into num_blocks;

    if num_tbs > 0 OR num_seg > 0 OR num_blocks > 0 then

      feature_boolean := 1;
      feature_usage   :=
                'Number of Tablespaces Tracked: ' || to_char(num_tbs) ||
        ', ' || ' Number of Segments Tracked: ' || to_char(num_seg) ||
        ', ' || ' Number of Blocks Tracked in Memory: ' || to_char(num_blocks);
      feature_info    := to_clob(feature_usage);

    end if;     

end;
/
show errors;

/********************************************************************
 * DBMS_FEATURE_SHARD
 * The procedure tracks if the database is a database shard.
 ********************************************************************/

create or replace procedure DBMS_FEATURE_SHARD
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage   varchar2(300);
    deployed        number := 0;
    shard_method    number := 0;
begin
    -- initialize
    feature_boolean := 0;
    aux_count       := 0;
    feature_info    := to_clob('The database is not a shard');

    -- Determine if the sharded configuration has been deployed
    execute immediate 'select count(*) from v$parameter vp1 ' ||  
       ' where vp1.name = ''_gws_deployed'' and vp1.value = 2 ' ||
       ' and (select count(*) from gsmadmin_internal.cloud ) = 0'
       into deployed;

    -- Determine the  sharding method used 
    execute immediate 'select value from v$parameter ' ||
       ' where name=''_gws_sharding_method''' 
        into shard_method;
    if shard_method = 1 then
      feature_usage := 'System-managed';
    elsif shard_method = 2 then
      feature_usage := 'User-managed';
    elsif shard_method = 3 then
      feature_usage := 'Composite';
    end if; 

    if deployed > 0 AND shard_method > 0 AND shard_method < 4 then
      feature_boolean := 1;
      feature_info := to_clob(feature_usage);
    end if;
end;
/
show errors;

CREATE OR REPLACE LIBRARY DBMS_STORAGE_TYPE_LIB TRUSTED AS STATIC;
/
create or replace procedure kdzstoragetype(tsn IN number, type out NUMBER) as
  LANGUAGE C
  NAME "kdzstoragetype"
  LIBRARY DBMS_STORAGE_TYPE_LIB
  with context
  PARAMETERS (context, tsn OCINumber, type OCINumber);
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_ZFS_STORAGE
 * Procedure to detect use of ZFS storage
 *****************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_ZFS_STORAGE
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  feature_count NUMBER;
  tsn           NUMBER;
  stortype      NUMBER;
  TYPE cursor_t         IS REF CURSOR;
  cursor_objtype        cursor_t;
  feature_usage         varchar2(1000);
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  OPEN cursor_objtype FOR q'[select ts# from sys.ts$]';

  LOOP
    BEGIN
      FETCH cursor_objtype INTO tsn;
      EXIT WHEN cursor_objtype%NOTFOUND;
      kdzstoragetype(tsn, stortype);
      IF (stortype = 1) THEN
        feature_count := feature_count + 1;
      END IF;
    END;
  END LOOP;

  feature_usage := 'TS on ZFS: ' || to_char(feature_count);
  feature_info := to_clob(feature_usage);

  if (feature_count > 0) then
    feature_boolean := 1;
  else
    feature_boolean := 0;
  end if;
  aux_count       := feature_count;
END;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_PILLAR_STORAGE
 * Procedure to detect use of PILLAR storage
 *****************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_PILLAR_STORAGE
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  feature_count  NUMBER;
  tsn            NUMBER;
  stortype       NUMBER;
  TYPE cursor_t  IS REF CURSOR;
  cursor_objtype cursor_t;
  feature_usage         varchar2(1000);
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  OPEN cursor_objtype FOR q'[select ts# from sys.ts$]';

  LOOP
    BEGIN
      FETCH cursor_objtype INTO tsn;
      EXIT WHEN cursor_objtype%NOTFOUND;
      kdzstoragetype(tsn, stortype);
      IF (stortype = 2) THEN
        feature_count := feature_count + 1;
      END IF;
    END;
  END LOOP;

  feature_usage := 'TS on Pillar: ' || to_char(feature_count);
  feature_info := to_clob(feature_usage);
  
  if (feature_count > 0) then
    feature_boolean := 1;
  else
    feature_boolean := 0;
  end if;  
  aux_count       := feature_count;
END;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_ZFS_EHCC
 * Procedure to detect use of ZFS storage with EHCC
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_ZFS_EHCC
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  feature_count NUMBER;
  feature_usage         varchar2(1000);
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  execute immediate 'select value from v$sysstat' ||
    ' where name like ''HCC usage ZFS'''
    into feature_count;

  feature_usage := 'EHCC on ZFS: ' || to_char(feature_count);
  feature_info := to_clob(feature_usage);

  if (feature_count > 0) then
    feature_boolean := 1; 
  else
    feature_boolean := 0;
  end if;
  aux_count       := feature_count;
end;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_PILLAR_EHCC
 * Procedure to detect use of Pillar storage with EHCC
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_PILLAR_EHCC
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  feature_count NUMBER;
    feature_usage         varchar2(1000);
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  execute immediate 'select value from v$sysstat' ||
    ' where name like ''HCC usage pillar'''
    into feature_count;

  feature_usage := 'EHCC on Pillar: ' || to_char(feature_count);
  feature_info := to_clob(feature_usage);

  if (feature_count > 0) then
    feature_boolean := 1; 
  else
    feature_boolean := 0;
  end if;
  aux_count       := feature_count;
end;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_CLOUD_EHCC
 * Procedure to detect use of EHCC in Cloud Database
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_CLOUD_EHCC
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  feature_count         NUMBER;
  feature_usage         VARCHAR2(1000);
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  execute immediate 'select value from v$sysstat' ||
    ' where name like ''HCC usage cloud'''
    into feature_count;

  feature_usage := 'EHCC in Cloud Database: ' || to_char(feature_count);
  feature_info := to_clob(feature_usage);

  if (feature_count > 0) then
    feature_boolean := 1; 
  else
    feature_boolean := 0;
  end if;
  aux_count       := feature_count;
end;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_SECUREFILES_USR
 * Procedure to detect usage of Oracle SecureFiles 
 * by non-system users
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SECUREFILES_USR
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB','MDSYS'))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB','MDSYS'))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB','MDSYS'))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;
END;
/
show errors;


/*****************************************************************
 * DBMS_FEATURE_SECUREFILES_SYS
 * Procedure to detect usage of Oracle SecureFiles
 * by system (internal) users
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SECUREFILES_SYS
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;
END;
/
show errors;


/*****************************************************************
 * DBMS_FEATURE_SFENCRYPT_USR
 * Procedure to detect usage of Oracle SecureFile Encryption
 * by non-system users
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SFENCRYPT_USR
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(l.flags, 4096), 0, 'NO', 'YES')='YES' and 
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 4096), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 4096), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;
END;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_SFENCRYPT_SYS
 * Procedure to detect usage of Oracle SecureFile Encryption
 * by system (internal) users
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SFENCRYPT_SYS
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(l.flags, 4096), 0, 'NO', 'YES')='YES' and 
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 4096), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 4096), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;
END;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_SFCOMPRESS_USR
 * Procedure to detect usage of Oracle SecureFile Compression
 * by non-system users
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SFCOMPRESS_USR
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  -- bug 21562376: XML search index $D table (with name pattern 'DR%$D')
  --               uses compression, skip it.
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(l.flags, 57344), 0, 'NO', 'YES')='YES' and 
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.name not like 'DR%$D' and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 57344), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.name not like 'DR%$D' and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 57344), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.name not like 'DR%$D' and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;

END;
/
show errors;

/*****************************************************************
 * DBMS_FEATURE_SFCOMPRESS_SYS
 * Procedure to detect usage of Oracle SecureFile Compression
 * by system (internal) users
 *****************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SFCOMPRESS_SYS
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  -- bug 21562376: XML search index $D table (with name pattern 'DR%$D')
  --               uses compression, count it as system usage.
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(l.flags, 57344), 0, 'NO', 'YES')='YES' and 
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            (o.name like 'DR%$D' or 
             o.owner# in (select user# from user$ 
                          where name in ('SYS', 'SYSTEM', 'XDB')))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 57344), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            (o.name like 'DR%$D' or 
             o.owner# in (select user# from user$ 
                          where name in ('SYS', 'SYSTEM', 'XDB')))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 57344), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            (o.name like 'DR%$D' or 
             o.owner# in (select user# from user$ 
                          where name in ('SYS', 'SYSTEM', 'XDB')))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;

END;
/
show errors;

/********************************************************************
 * DBMS_FEATURE_SFDEDUP_USR
 * Procedure to detect usage of Oracle SecureFile Deduplication
 * by non-system users
 ********************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SFDEDUP_USR
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(l.flags, 458752), 0, 'NO', 'YES')='YES' and 
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 458752), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 458752), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# not in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;

END;
/
show errors;

/********************************************************************
 * DBMS_FEATURE_SFDEDUP_SYS
 * Procedure to detect usage of Oracle SecureFile Deduplication
 * by system (internal) users
 ********************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_SFDEDUP_SYS
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_count      NUMBER;
BEGIN
  -- initialize
  feature_info      := NULL;
  feature_count     := 0;

  -- skip internal usage by flashback archive
  select count(*) into feature_count from (
    select l.obj#, l.lobj#, l.lobj#, l.lobj#, 'U' fragtype  
      from tab$ t, lob$ l, obj$ o
      where l.obj#=t.obj# and 
            decode(bitand(l.property, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(l.flags, 458752), 0, 'NO', 'YES')='YES' and 
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                             where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select pl.tabobj#, pl.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, partlob$ pl, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 458752), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=pl.lobj# and pl.tabobj#=t.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
    union
    select l.obj#, lc.lobj#, fragobj#, parentobj#, fragtype$ 
      from lobfrag$ lf, lobcomppart$ lc, lob$ l, tab$ t, obj$ o
      where decode(bitand(lf.fragpro, 2048), 0, 'NO', 'YES')='YES' and
            decode(bitand(lf.fragflags, 458752), 0, 'NO', 'YES')='YES' and 
            lf.parentobj#=lc.partobj# and l.lobj#=lc.lobj# and 
            t.obj#=l.obj# and  
            decode(bitand(t.property, 8589934592), 0, 'NO', 'YES')='NO' and
            o.obj# = t.obj# and 
            o.owner# in (select user# from user$ 
                         where name in ('SYS', 'SYSTEM', 'XDB'))
  );
  
  feature_boolean := feature_count;
  aux_count       := feature_count;

END;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_DATA_GUARD
 * The procedure to detect usage for Data Guard
 ***************************************************************/

create or replace procedure DBMS_FEATURE_DATA_GUARD
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage         varchar2(1000);
    log_transport         varchar2(25);
    num_arch              number;
    num_casc_stby         number;
    num_compression       number;
    num_dgconfig          number;
    num_far_sync          number;
    num_fast_sync         number;
    num_lgwr_async        number;
    num_lgwr_sync         number;
    num_realtime_apply    number;
    num_redo_apply        number;
    num_snapshot          number;
    num_sql_apply         number;
    num_standbys          number;
    num_terminal_db       number;
    num_ra                number;
    protection_mode       varchar2(24);
    use_broker            varchar2(5);
    use_compression       varchar2(8);
    use_far_sync          varchar2(5);
    use_fast_sync         varchar2(5);
    use_flashback         varchar2(18);
    use_fs_failover       varchar2(22);
    use_realtime_apply    varchar2(5);
    use_redo_apply        varchar2(5);
    use_snapshot          varchar2(5);
    use_sql_apply         varchar2(5);
    use_ra                varchar2(5);

begin
    -- initialize
    feature_boolean := 0;
    aux_count := 0;
    log_transport := NULL;
    num_arch := 0;
    num_casc_stby := 0;
    num_compression := 0;
    num_dgconfig :=0;
    num_far_sync := 0;
    num_fast_sync := 0;
    num_lgwr_async := 0;
    num_lgwr_sync := 0;
    num_realtime_apply := 0;
    num_redo_apply := 0;
    num_snapshot := 0;
    num_sql_apply := 0;
    num_standbys := 0;
    num_terminal_db := 0;
    num_ra := 0;
    use_broker := 'FALSE';
    use_compression := 'FALSE';
    use_far_sync := 'FALSE';
    use_fast_sync := 'FALSE';
    use_flashback := 'FALSE';
    use_fs_failover := 'FALSE';
    use_realtime_apply := 'FALSE';
    use_redo_apply := 'FALSE';
    use_snapshot := 'FALSE';
    use_sql_apply := 'FALSE';
    use_ra := 'FALSE';

    -- check for Data Guard usage by counting valid standby destinations
    -- We use v$archive_dest here and NOT v$dataguard_config because if the
    -- dg_config is not initialized, v$dataguard_config will be empty.
    execute immediate 'select count(*) from v$archive_dest ' ||
        'where status = ''VALID'' and target = ''STANDBY'''
        into num_standbys;

    if (num_standbys > 0) then
        feature_boolean := 1;

        -- determine if v$dataguard_config is populated
        execute immediate 'select count(*) from v$dataguard_config'
            into num_dgconfig;

        -- Depending on whether v$dataguard_config is populated or not, some
        -- of the commands below will either use v$dataguard_config or
        -- v$archive_dest.

        if (num_dgconfig > 0) then
            -- get the real number of standbys
            execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
                'from v$dataguard_config ' ||
                'where (DEST_ROLE like ''% STANDBY'')'
                into num_standbys;

            -- get the number of cascading standbys
            execute immediate 'select count(unique(PARENT_DBUN)) ' ||
                'from v$dataguard_config ' ||
                'where (PARENT_DBUN not in ' ||
                '(select DB_UNIQUE_NAME from v$database) and ' ||
                'PARENT_DBUN != ''NONE'' and PARENT_DBUN != ''UNKNOWN'')'
                into num_casc_stby;

            -- get the number of terminal databases
            execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
                'from v$dataguard_config ' ||
                'where (DB_UNIQUE_NAME not in ' ||
                '(select PARENT_DBUN from v$dataguard_config) and ' ||
                'PARENT_DBUN != ''UNKNOWN'')'
                into num_terminal_db;

            -- check for Redo Apply (Physical Standby) usage
            execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
                'from v$dataguard_config ' ||
                'where (DEST_ROLE = ''PHYSICAL STANDBY'')'
                into num_redo_apply;
            if (num_redo_apply > 0) then
                use_redo_apply := 'TRUE';
            end if;

            -- check for SQL Apply (Logical Standby) usage
            execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
                'from v$dataguard_config ' ||
                'where (DEST_ROLE = ''LOGICAL STANDBY'')'
                into num_sql_apply;
            if (num_sql_apply > 0) then
                use_sql_apply := 'TRUE';
            end if;

            -- check for Far Sync Instance usage
            execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
                'from v$dataguard_config ' ||
                'where (DEST_ROLE = ''FAR SYNC INSTANCE'')'
                into num_far_sync;
            if (num_far_sync > 0) then
                use_far_sync := 'TRUE';
            end if;

            -- check for Snapshot Standby usage
            execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
                'from v$dataguard_config ' ||
                'where (DEST_ROLE = ''SNAPSHOT STANDBY'')'
                into num_snapshot;
            if (num_snapshot > 0) then
                use_snapshot := 'TRUE';
            end if;

            -- check for Recovery Appliance usage using v$dataguard_config
            execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
                'from v$dataguard_config ' ||
                'where (DEST_ROLE = ''BACKUP APPLIANCE'')'
                into num_ra;
            if (num_ra > 0) then
                use_ra := 'TRUE';
            end if;

        else
            -- check for Redo Apply (Physical Standby) usage
            execute immediate 'select count(*) from v$archive_dest_status ' ||
                'where status = ''VALID'' and type = ''PHYSICAL'''
                into num_redo_apply;
            if (num_redo_apply > 0) then
                use_redo_apply := 'TRUE';
            end if;

            -- check for SQL Apply (Logical Standby) usage
            execute immediate 'select count(*) from v$archive_dest_status ' ||
                'where status = ''VALID'' and type = ''LOGICAL'''
                into num_sql_apply;
            if (num_sql_apply > 0) then
                use_sql_apply := 'TRUE';
            end if;

            -- check for Far Sync Instance usage
            execute immediate 'select count(*) from v$archive_dest_status ' ||
                'where status = ''VALID'' and type = ''FAR SYNC'''
                into num_far_sync;
            if (num_far_sync > 0) then
                use_far_sync := 'TRUE';
            end if;

            -- copy far sync instance count into cascading standby
            num_casc_stby := num_far_sync;

            -- check for Snapshot Standby usage
            execute immediate 'select count(*) from v$archive_dest_status ' ||
                'where status = ''VALID'' and type = ''SNAPSHOT'''
                into num_snapshot;
            if (num_snapshot > 0) then
                use_snapshot := 'TRUE';
            end if;

            -- check for Recovery Appliance usage using v$archive_dest_status
            execute immediate 'select count(*) from v$archive_dest_status ' ||
                'where status = ''VALID'' and type = ''BACKUP APPLIANCE'''
                into num_ra;
            if (num_ra > 0) then
                use_ra := 'TRUE';
            end if;

        end if;

        -- check for Broker usage by selecting the init param value
        execute immediate 'select value from v$system_parameter ' ||
            'where name = ''dg_broker_start'''
            into use_broker;

        -- get all log transport methods
        execute immediate 'select count(*) from v$archive_dest ' ||
            'where status = ''VALID'' and target = ''STANDBY'' ' ||
            'and archiver like ''ARC%'''
            into num_arch;
        if (num_arch > 0) then
            log_transport := 'ARCH ';
        end if;
        execute immediate 'select count(*) from v$archive_dest ' ||
            'where status = ''VALID'' and target = ''STANDBY'' ' ||
            'and archiver = ''LGWR'' ' ||
            'and (transmit_mode = ''SYNCHRONOUS'' or ' ||
            '     transmit_mode = ''PARALLELSYNC'')'
            into num_lgwr_sync;
        if (num_lgwr_sync > 0) then
            log_transport := log_transport || 'LGWR SYNC ';
        end if;
        execute immediate 'select count(*) from v$archive_dest ' ||
            'where status = ''VALID'' and target = ''STANDBY'' ' ||
            'and archiver = ''LGWR'' ' ||
            'and transmit_mode = ''ASYNCHRONOUS'''
            into num_lgwr_async;
        if (num_lgwr_async > 0) then
            log_transport := log_transport || 'LGWR ASYNC';
        end if;

        -- get protection mode for primary db
        execute immediate 'select protection_mode from v$database'
            into protection_mode;

        -- check for Fast Sync usage
        if (protection_mode = 'MAXIMUM AVAILABILITY') then
            execute immediate 'select count(*) from v$archive_dest ' ||
                'where status = ''VALID'' and target = ''STANDBY'' ' ||
                'and archiver = ''LGWR'' ' ||
                'and (transmit_mode = ''SYNCHRONOUS'' or ' ||
                '     transmit_mode = ''PARALLELSYNC'') ' ||
                'and affirm = ''NO'' '
                into num_fast_sync;
            if (num_fast_sync > 0) then
                use_fast_sync := 'TRUE';
            end if;
        end if;

        -- check for fast-start failover usage
        execute immediate 'select fs_failover_status from v$database'
            into use_fs_failover;
        if (use_fs_failover != 'DISABLED') then
            use_fs_failover := 'TRUE';
        else
            use_fs_failover := 'FALSE';
        end if;

        -- check for realtime apply usage
        -- We can only count the directly connected standbys
        execute immediate 'select count(*) from v$archive_dest_status ' ||
            'where status = ''VALID'' ' ||
            'and recovery_mode like ''%REAL TIME APPLY'''
            into num_realtime_apply;
        if (num_realtime_apply > 0) then
            use_realtime_apply := 'TRUE';
        end if;

        -- check for network compression usage
        -- We can only count the directly connected standbys
        execute immediate 'select count(*) from v$archive_dest ' ||
            'where status = ''VALID'' and target = ''STANDBY'' ' ||
            'and compression = ''ENABLE'''
            into num_compression;
        if (num_compression > 0) then
            use_compression := 'TRUE';
        end if;

        -- check for flashback usage
        execute immediate 'select flashback_on from v$database'
            into use_flashback;
        if (use_flashback = 'YES') then
            use_flashback := 'TRUE';
        else
            use_flashback := 'FALSE';
        end if;

        feature_usage :=
                'Number of standbys: ' || to_char(num_standbys) ||
        ', ' || 'Number of Cascading databases: ' || to_char(num_casc_stby) ||
        ', ' || 'Number of Terminal databases: ' || to_char(num_terminal_db) ||
        ', ' || 'Redo Apply used: ' || upper(use_redo_apply) ||
        ', ' || 'SQL Apply used: ' || upper(use_sql_apply) ||
        ', ' || 'Far Sync Instance used: ' || upper(use_far_sync) ||
        ', ' || 'Snapshot Standby used: ' || upper(use_snapshot) ||
        ', ' || 'Broker used: ' || upper(use_broker) ||
        ', ' || 'Protection mode: ' || upper(protection_mode) ||
        ', ' || 'Log transports used: ' || upper(log_transport) ||
        ', ' || 'Fast Sync used: ' || upper(use_fast_sync) ||
        ', ' || 'Fast-Start Failover used: ' || upper(use_fs_failover) ||
        ', ' || 'Real-Time Apply used: ' || upper(use_realtime_apply) ||
        ', ' || 'Compression used: ' || upper(use_compression) ||
        ', ' || 'Flashback used: ' || upper(use_flashback) ||
        ', ' || 'Recovery Appliance used: ' || upper(use_ra)
        ;
        feature_info := to_clob(feature_usage);
    else
        feature_info := to_clob('Data Guard usage not detected');
    end if;
end;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_ACTIVE_DATA_GUARD
 * The procedure to detect usage for Active Data Guard
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_ACTIVE_DATA_GUARD
    (feature_boolean  OUT  NUMBER,
      aux_count        OUT  NUMBER,
      feature_info     OUT  CLOB)
AS
    feature_usage         varchar2(1000);
    num_casc_stby         number;
    num_dgconfig          number;
    num_far_sync          number;
    num_realtime_query    number;
    num_terminal_db       number;
    num_rolling           number;
    num_rolling_logs      number;
    num_rolling_parts     number;
    num_rolling_pops      number;
    num_rolling_pots      number;
    num_global_seq_use    number;
    use_global_sequences  varchar2(5);
    use_realtime_query    varchar2(5);
    use_rolling           varchar2(5);
BEGIN
    -- initialize
    feature_boolean := 0;
    aux_count := 0;
    num_casc_stby := 0;
    num_dgconfig :=0;
    num_far_sync := 0;
    num_realtime_query := 0;
    num_terminal_db := 0;
    num_rolling := 0;
    num_rolling_logs := 0;
    num_rolling_parts := 0;
    num_rolling_pops := 0;
    num_rolling_pots := 0;
    num_global_seq_use := 0;
    use_global_sequences := 'FALSE';
    use_realtime_query := 'FALSE';
    use_rolling := 'FALSE';

    -- We have to first look for each Active Data Guard feature before we can
    -- report if they are using any of them.

    -- determine if v$dataguard_config is populated
    execute immediate 'select count(*) from v$dataguard_config'
        into num_dgconfig;

    -- Depending on whether v$dataguard_config is populated or not, some
    -- of the commands below will either use v$dataguard_config or
    -- v$archive_dest.

    if (num_dgconfig > 0) then
        -- get number of Far Sync Instances
        execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
            'from v$dataguard_config ' ||
            'where (DEST_ROLE = ''FAR SYNC INSTANCE'')'
            into num_far_sync;
        if (num_far_sync > 0) then
            feature_boolean := 1;
        end if;

        -- get the number of cascading standbys
        execute immediate 'select count(unique(PARENT_DBUN)) ' ||
            'from v$dataguard_config ' ||
            'where (PARENT_DBUN not in ' ||
            '(select DB_UNIQUE_NAME from v$database) and ' ||
            'PARENT_DBUN != ''NONE'' and PARENT_DBUN != ''UNKNOWN'')'
            into num_casc_stby;
        if (num_casc_stby > 0) then
            feature_boolean := 1;
        end if;

        -- get the number of terminal databases
        -- Note: this is not an Active Data Guard feature, but we report it.
        execute immediate 'select count(unique(DB_UNIQUE_NAME)) ' ||
            'from v$dataguard_config ' ||
            'where (DB_UNIQUE_NAME not in ' ||
            '(select PARENT_DBUN from v$dataguard_config) and ' ||
            'PARENT_DBUN != ''UNKNOWN'')'
            into num_terminal_db;

        -- check for DBMS_ROLLING usage
        execute immediate 'select count(status) from dba_rolling_status'
            into num_rolling;
        if (num_rolling > 0) then
            feature_boolean := 1;
            use_rolling := 'TRUE';

            -- get the total number of DBMS_ROLLING participants
            execute immediate 'select count(dbun) '                     ||
                'from dba_rolling_databases '                           ||
                'where participant=''YES'''
                into num_rolling_parts;

            -- get the number of physicals of the original primary
            execute immediate 'select count(scope) '                    ||
                'from dba_rolling_parameters where name=''PROTECTS'''   ||
                'and curval=''PRIMARY'' and scope in (select dbun '     ||
                'from dba_rolling_databases where participant=''YES'''  ||
                'and role=''PHYSICAL'')' 
                into num_rolling_pops;

            -- get the number of physicals of the future primary
            execute immediate 'select count(scope) '                    ||
                'from dba_rolling_parameters where name=''PROTECTS'''   ||
                'and curval=''TRANSIENT'' and scope in (select dbun '   ||
                'from dba_rolling_databases where participant=''YES'' ' || 
                'and role=''PHYSICAL'')' 
                into num_rolling_pots;

            -- get the number of logical standbys
            execute immediate 'select count(dbun) '                     ||
                'from dba_rolling_databases where participant=''YES'' ' || 
                'and role=''LOGICAL'' and dbun != '                     ||
                '(select future_primary from dba_rolling_status)' 
                into num_rolling_logs;
        end if;
    else
        -- get number of Far Sync Instances
        execute immediate 'select count(*) from v$archive_dest_status ' ||
            'where status = ''VALID'' and type = ''FAR SYNC'''
            into num_far_sync;
        if (num_far_sync > 0) then
            feature_boolean := 1;
        end if;

        -- copy far sync instance count into cascading standby
        num_casc_stby := num_far_sync;
    end if;

    -- check for real time query usage
    -- We can only count the directly connected standbys
    execute immediate 'select count(*) from v$archive_dest_status ' ||
        'where status = ''VALID'' and ' ||
        'recovery_mode like ''MANAGED%'' and ' ||
        'database_mode = ''OPEN_READ-ONLY'''
        into num_realtime_query;
    if (num_realtime_query > 0) then
        use_realtime_query := 'TRUE';
        feature_boolean := 1;
    end if;
    -- check for global sequence usage
    execute immediate 'select count(*) from dba_sequences ' ||  
        'where sequence_owner != ''SYS'' and session_flag = ''N'''
        into num_global_seq_use;
    if (num_global_seq_use > 0) then
        use_global_sequences := 'TRUE';
    end if;
    if (feature_boolean = 1) then
        feature_usage :=
                'Number of Far Sync Instances: ' || to_char(num_far_sync) ||
        ', ' || 'Number of Cascading databases: ' || to_char(num_casc_stby) ||
        ', ' || 'Number of Terminal databases: ' || to_char(num_terminal_db) ||
        ', ' || 'Real Time Query used: ' || upper(use_realtime_query) ||
        ', ' || 'Global Sequences used: ' || upper(use_global_sequences) ||
        ', ' || 'DBMS_ROLLING used: ' || upper(use_rolling) ||
        ', ' || 'Number of DBMS_ROLLING Participants: ' 
             || to_char(num_rolling_parts) ||
        ', ' || 'Number of DBMS_ROLLING OP Physicals: ' 
             || to_char(num_rolling_pops) ||
        ', ' || 'Number of DBMS_ROLLING FP Physicals: ' 
             || to_char(num_rolling_pots) ||
        ', ' || 'Number of DBMS_ROLLING OP Logicals: ' 
             || to_char(num_rolling_logs) 
        ;
        feature_info := to_clob(feature_usage);
    else
        feature_info := to_clob('Active Data Guard usage not detected');
    end if;
END;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_MOVE_DATAFILE
 * The procedure to detect usage for Online Move Datafile
 ***************************************************************/

create or replace procedure DBMS_FEATURE_MOVE_DATAFILE
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage      varchar2(1000);
    use_omd_primary    varchar2(5);
    use_pri            number;

begin
    -- initialize
    feature_boolean := 0;
    aux_count := 0;
    use_omd_primary := 'FALSE';

    execute immediate 'select DI2MVUSE_PRI from X$KCCDI2'
      into use_pri;
    if (use_pri > 0) then
      use_omd_primary := 'TRUE';
      feature_boolean := 1;
    end if;

    if (feature_boolean = 1) then
        feature_usage :=
                'Online Move Datafile on primary used: '
             || upper(use_omd_primary)
        ;
        feature_info := to_clob(feature_usage);
    else
        feature_info := to_clob('Online Move Datafile usage not detected');
    end if;
end;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_DATA_REDACTION
 * The procedure to detect usage for Data Redaction
 ***************************************************************/

create or replace procedure DBMS_FEATURE_DATA_REDACTION
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
    feature_usage         varchar2(1000);
    num_policies          number;
    num_policies_enabled  number;
    num_full_redaction    number;
    num_partial_redaction number;
    num_random_redaction  number;
    num_regexp_redaction   number;

begin
    -- initialize
    feature_boolean := 0;
    aux_count := 0;
    num_policies := 0;
    num_policies_enabled := 0;
    num_full_redaction := 0;
    num_partial_redaction := 0;
    num_random_redaction := 0;
    num_regexp_redaction := 0;
    
    -- check for Data Redaction usage by counting number of policies
    execute immediate 'select count(*) from REDACTION_POLICIES '
        into num_policies;

    if (num_policies > 0) then
        feature_boolean := 1;

        -- check for enable Data Radaction policy usage
        execute immediate 'select count(*) from REDACTION_POLICIES ' ||
            'where upper(ENABLE) like ''%YES%'''
            into num_policies_enabled;

        -- check for Full Data Redaction type usage
        execute immediate 'select count(*) from REDACTION_COLUMNS ' ||
            'where FUNCTION_TYPE = ''FULL REDACTION'''
            into num_full_redaction;
     
        -- check for Partial Data Redaction type usage
        execute immediate 'select count(*) from REDACTION_COLUMNS ' ||
            'where FUNCTION_TYPE = ''PARTIAL REDACTION'''
            into num_partial_redaction;

        -- check for Random Data Redaction type usage
        execute immediate 'select count(*) from REDACTION_COLUMNS ' ||
            'where FUNCTION_TYPE = ''RANDOM REDACTION'''
            into num_random_redaction;

        -- check for Regexp-based Data Redaction type usage
        execute immediate 'select count(*) from REDACTION_COLUMNS ' ||
            'where FUNCTION_TYPE = ''REGEXP REDACTION'''
            into num_regexp_redaction;

        feature_usage :=
                'Number of data redaction policies: ' || 
                 to_char(num_policies) ||
        ', ' || 'Number of enabled policies: ' || 
                 to_char(num_policies_enabled) ||
        ', ' || 'Number of policies using full redaction: ' || 
                 to_char(num_full_redaction) ||
        ', ' || 'Number of policies using partial redaction: ' || 
                 to_char(num_partial_redaction) ||
        ', ' || 'Number of policies using random redaction: ' || 
                 to_char(num_random_redaction)  ||
        ', ' || 'Number of policies using regexp redaction: ' || 
                 to_char(num_regexp_redaction)
        ;
        feature_info := to_clob(feature_usage);
    else
        feature_info := to_clob('Data Redaction usage not detected');
    end if;

end;
/

/***************************************************************
 *  DBMS_FEATURE_DATABASE_ODM
 *  The procedure to detect usage for Oracle Data Mining
***************************************************************/

create or replace procedure DBMS_FEATURE_DATABASE_ODM
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   dm_on              NUMBER;     -- data mining option on
   model_cnt          NUMBER;
   dm_usage           varchar2(32767);
begin
  -- initialize
  feature_boolean   := 0;
  aux_count         := 0;
  feature_info      := NULL;
  model_cnt         := 0;

  -- check if ODM option is installed
  select count(*) into dm_on from v$option where
     parameter = 'Data Mining' and value = 'TRUE';

  if (dm_on = 0) then
    return;
  end if;
 
   execute immediate
         'select count(*), listagg(feature,'';'') within group (order by feature) ' ||
     'from ( ' ||
       'with a as ( ' ||
        'select decode(func ' ||
          ',1, ''CLASSIFICATION'' ' ||
          ',2, ''REGRESSION'' ' ||
          ',3, ''CLUSTERING'' ' ||
          ',4, ''FEATURE_EXTRACTION''  ' ||
          ',5, ''ASSOCIATION_RULES''  ' ||
          ',6, ''ATTRIBUTE_IMPORTANCE''  ' ||
          ',0)||''(''||   ' ||
          ' decode(alg ' ||
          ',1, ''NAIVE_BAYES''  ' ||
          ',2, ''ADAPTIVE_BAYES_NETWORK''  ' ||
          ',3, ''DECISION_TREE''   ' ||
          ',4, ''SUPPORT_VECTOR_MACHINES''  ' ||
          ',5, ''KMEANS''   ' ||
          ',6, ''O_CLUSTER''  ' ||
          ',7, ''NONNEGATIVE_MATRIX_FACTOR''   ' ||
          ',8, ''GENERALIZED_LINEAR_MODEL''  ' ||
          ',9, ''APRIORI_ASSOCIATION_RULES''   ' ||
          ',10, ''MINIMUM_DESCRIPTION_LENGTH''   ' ||
          ',11, ''SINGULAR_VALUE_DECOMP''   ' ||
          ',12, ''EXPECTATION_MAXIMIZATION''    ' ||
          ',0)||'')'' feat from model$  ' ||
          'where (alg not in (4,5)) or  ' ||
          '(alg in (4,5) and obj# in (select mod# from modeltab$ where typ#=2))) ' ||
      'select feat||''(''||count(*)||'')'' feature from a group by feat order by count(*))'
       into model_cnt, dm_usage;

    if (model_cnt  > 0)   then     --- feature used
        feature_boolean := 1;
        aux_count := model_cnt;
        feature_info := TO_CLOB(dm_usage);
    else                        --- feature not used
        feature_boolean := 0;
        aux_count := 0;
        feature_info := null;
    end if;

END DBMS_FEATURE_DATABASE_ODM;
/


/***************************************************************
 * DBMS_FEATURE_DYN_SGA
 * The procedure to detect usage of Dynamic SGA
 ***************************************************************/

create or replace procedure DBMS_FEATURE_DYN_SGA
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  num_resize_ops         number;                 -- number of resize operations
  feature_usage          varchar2(1000);
begin
  -- initialize
  num_resize_ops := 0;
  feature_boolean := 0;
  aux_count := 0;
  feature_info := to_clob('Dynamic SGA usage not detected');
  feature_usage := '';

  execute immediate 'select count(*) from v$sga_resize_ops ' ||
                    'where oper_type in (''GROW'', ''SHRINK'') and ' ||
                    'oper_mode=''MANUAL''and ' ||
                    'start_time >= ' ||
                    'to_date((select nvl(max(last_sample_date), sysdate-7) ' ||
                    'from dba_feature_usage_statistics))'
  into num_resize_ops;

  if num_resize_ops > 0
  then

    feature_boolean := 1;

    feature_usage := feature_usage||':rsz ops:'||num_resize_ops;

    -- get v$memory_dynamic_components info
    for item in (select component, current_size, min_size, max_size,
                 user_specified_size from
                 v$memory_dynamic_components where current_size != 0)
    loop
      feature_usage := feature_usage||':comp:'||item.component||
                       ':cur:'||item.current_size||':min:'||
                       item.min_size||':max:'||item.max_size||
                       ':usr:'||item.user_specified_size;
    end loop;

    -- get v$system_event info for SGA events
    for item in (select substr(event, 0, 15) evt, total_waits, time_waited
                 from v$system_event where event like '%SGA%')
    loop
      feature_usage := feature_usage||':event:'||item.evt||':waits:'||
                       item.total_waits||':time:'||item.time_waited;
    end loop;

    feature_info := to_clob(feature_usage);

  end if;

end;
/

/***************************************************************
 * DBMS_FEATURE_AUTO_SGA
 * The procedure to detect usage of Automatic SGA Tuning
 ***************************************************************/

create or replace procedure DBMS_FEATURE_AUTO_SGA
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  feature_usage          varchar2(1000);
  sga_target             number;
  sga_max_size           number;
begin

  -- initialize
  feature_boolean := 0;
  aux_count := 0;
  feature_info := to_clob('Automatic SGA Tuning usage not detected');
  feature_usage := '';
  sga_target := 0;
  sga_max_size := 0;

  execute immediate 'select to_number(value) from v$system_parameter where ' ||
                    'name like ''sga_target'''
  into sga_target;

  if sga_target > 0
  then

    feature_boolean := 1;

    feature_usage := feature_usage||':sga_target:'||sga_target;

    -- get sga_max_size value
    execute immediate 'select to_number(value) from v$system_parameter where ' ||
                      'name like ''sga_max_size'''
    into sga_max_size;

    feature_usage := feature_usage||':sga_max_size:'||sga_max_size;

    -- get v$memory_dynamic_components info
    for item in (select component, current_size, min_size, max_size,
                 user_specified_size from
                 v$memory_dynamic_components where current_size != 0)
    loop
      feature_usage := feature_usage||':comp:'||item.component||
                       ':cur:'||item.current_size||':min:'||
                       item.min_size||':max:'||item.max_size||
                       ':usr:'||item.user_specified_size;
    end loop;

    -- get v$system_event info for SGA events
    for item in (select substr(event, 0, 15) evt, total_waits, time_waited
                 from v$system_event where event like '%SGA%')
    loop
      feature_usage := feature_usage||':event:'||item.evt||':waits:'||
                       item.total_waits||':time:'||item.time_waited;
    end loop;
    feature_info := to_clob(feature_usage);

  end if;

end;
/

/***************************************************************
 * DBMS_FEATURE_AUTO_MEM
 * The procedure to detect usage of Automatic Memory Tuning
 ***************************************************************/

create or replace procedure DBMS_FEATURE_AUTO_MEM
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  feature_usage          varchar2(1000);
  memory_target             number;
  sga_max_size              number;
  memory_max_target         number;
begin

  -- initialize
  feature_boolean := 0;
  aux_count := 0;
  feature_info := to_clob('Automatic Memory Tuning usage not detected');
  feature_usage := '';
  memory_target := 0;
  sga_max_size := 0;
  memory_max_target := 0;

  execute immediate 'select to_number(value) from v$system_parameter where ' ||
                    'name like ''memory_target'''
  into memory_target;

  if memory_target > 0
  then

    feature_boolean := 1;

    feature_usage := feature_usage||':memory_target:'||memory_target;

    -- get sga_max_size value
    execute immediate 'select to_number(value) from v$system_parameter where ' ||
                      'name like ''sga_max_size'''
    into sga_max_size;

    feature_usage := feature_usage||':sga_max_size:'||sga_max_size;

    -- get memory_max_target value
    execute immediate 'select to_number(value) from v$system_parameter where ' ||
                      'name like ''memory_max_target'''
    into memory_max_target;

    feature_usage := feature_usage||':memory_max_target:'||memory_max_target;

    -- get v$memory_dynamic_components info
    for item in (select component, current_size, min_size, max_size,
                 user_specified_size from
                 v$memory_dynamic_components where current_size != 0)
    loop
      feature_usage := feature_usage||':comp:'||item.component||
                       ':cur:'||item.current_size||':min:'||
                       item.min_size||':max:'||item.max_size||
                       ':usr:'||item.user_specified_size;
    end loop;

    -- get v$pgastat info
    for item in (select name, value from v$pgastat where
                 name in ('tot PGA alc', 'over alc cnt',
                          'tot PGA for auto wkar',
                          'tot PGA for man wkar',
                          'glob mem bnd', 'aggr PGA auto tgt',
                          'aggr PGA tgt prm'))
    loop
      feature_usage := feature_usage||':'||item.name||':'||item.value;
    end loop;

    -- get v$memory_target_advice info
    feature_usage := feature_usage||':mem tgt adv:';
    for item in (select memory_size, memory_size_factor, estd_db_time,
                 estd_db_time_factor from v$memory_target_advice
                 order by memory_size)
    loop
      feature_usage := feature_usage||':msz:'||item.memory_size||
                       ':sf:'||item.memory_size_factor||
                       ':time:'||item.estd_db_time||
                       ':tf:'||item.estd_db_time_factor;
    end loop;

    -- get v$system_event info for SGA events
    for item in (select substr(event, 0, 15) evt, total_waits, time_waited
                 from v$system_event where event like '%SGA%')
    loop
      feature_usage := feature_usage||':event:'||item.evt||':waits:'||
                       item.total_waits||':time:'||item.time_waited;
    end loop;

    feature_info := to_clob(feature_usage);

  end if;

end;
/

/***************************************************************
 * DBMS_FEATURE_RESOURCE_MANAGER
 * The procedure to detect usage of Resource Manager
 ***************************************************************/

create or replace procedure DBMS_FEATURE_RESOURCE_MANAGER
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  feature_usage             varchar2(1000);
  non_maint_sql             varchar2(2000);
  non_maint_usage           number;
  non_maint_cpu             number;
  non_maint_other           number;
begin

  -- Initialize all variables

  feature_boolean := 0;  
  aux_count       := 0;
  feature_info    := to_clob('Resource Manager usage not detected');

  feature_usage   := NULL;
  non_maint_sql   := NULL;
  non_maint_cpu   := 0;
  non_maint_other := 0;

  -- 'feature_boolean' is set to 1 if Resource Manager was enabled, not
  -- including for maintenance windows.
  -- 
  -- The last SQL clause is saying RM is on if the plan is default_cdb_plan
  -- but it wasn't enabled by a PDB's maintenance window.  
  -- We cannot use the CDB's and PDB's sequence#s because the are not sync'd.
  -- Therefore, we see if count(PDBs with non-maintenance plans) > 0 or
  -- count(PDBs with plans) = 0.

  non_maint_sql := 
      'select decode(count(*), 0, 0, 1) from v$rsrc_plan_history top where ' ||
      '(name != ''ORA$INTERNAL_CDB_PLAN'' and ' ||
      ' name != ''INTERNAL_PLAN'' and name is not null and ' ||
      ' (name != ''DEFAULT_MAINTENANCE_PLAN'' or ' ||
      '   (window_name is null or ' ||
      '    (window_name != ''MONDAY_WINDOW'' and ' ||
      '     window_name != ''TUESDAY_WINDOW'' and ' ||
      '     window_name != ''WEDNESDAY_WINDOW'' and ' ||
      '     window_name != ''THURSDAY_WINDOW'' and ' ||
      '     window_name != ''FRIDAY_WINDOW'' and ' ||
      '     window_name != ''SATURDAY_WINDOW'' and ' ||
      '     window_name != ''SUNDAY_WINDOW''))) and ' ||
      ' (name != ''DEFAULT_CDB_PLAN'' or ' ||
      '   (select count(*) from v$rsrc_plan_history pdb ' ||
      '      where pdb.con_id > 1 and pdb.name is not null) = 0 or ' ||
      '   (select count(*) from v$rsrc_plan_history pdb ' ||
      '      where pdb.con_id > 1 and ' ||
      '            pdb.name is not null and ' ||
      '            pdb.name != ''INTERNAL_PLAN'' and ' ||
      '            (pdb.name != ''DEFAULT_MAINTENANCE_PLAN'' or ' ||
      '              (pdb.window_name is null or ' ||
      '               (pdb.window_name != ''MONDAY_WINDOW'' and ' ||
      '                pdb.window_name != ''TUESDAY_WINDOW'' and ' ||
      '                pdb.window_name != ''WEDNESDAY_WINDOW'' and ' ||
      '                pdb.window_name != ''THURSDAY_WINDOW'' and ' ||
      '                pdb.window_name != ''FRIDAY_WINDOW'' and ' ||
      '                pdb.window_name != ''SATURDAY_WINDOW'' and ' ||
      '                pdb.window_name != ''SUNDAY_WINDOW'')))) > 0))';
  execute immediate
    non_maint_sql
  into feature_boolean;

  -- 'aux_count' is not being used

  -- 'feature_info' is constructed of the following name-value pairs:
  --   Non-Maintenance CPU Management:
  --     This field is set to 1 if Resource Manager was enabled explicitly
  --     and the Resource Plan was managing CPU.
  --   Non-Maintenance Other Management:
  --     This field is set to 1 if Resource Manager was enabled explicitly
  --     and the Resource Plan was NOT managing CPU, i.e. the Resource Plan
  --     was managing idle time, switch time, DOP, etc.

  if feature_boolean > 0
  then
    execute immediate 
      non_maint_sql || ' and cpu_managed = ''ON'' '
    into non_maint_cpu;

    execute immediate 
      non_maint_sql || ' and cpu_managed = ''OFF'' '
    into non_maint_other;

    feature_usage := 
      'Non-Maintenance CPU Management: ' || non_maint_cpu ||
      ', Non-Maintenance Other Management: ' || non_maint_other;

    feature_info := to_clob(feature_usage);
  end if;

end dbms_feature_resource_manager;
/
show errors;


/***************************************************************
 * DBMS_FEATURE_RMAN_ZLIB
 *  The procedure to detect usage of RMAN ZLIB compression
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_ZLIB
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted => FALSE, 
                    compalg => 'ZLIB');
    
    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/

/***************************************************************
 * DBMS_FEATURE_RMAN_BZIP2
 *  The procedure to detect usage of RMAN BZIP2 compression
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_BZIP2
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted   => FALSE, 
                    compalg     => 'BZIP2');
    
    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/

/***************************************************************
 * DBMS_FEATURE_RMAN_BASIC
 *  The procedure to detect usage of RMAN BASIC compression
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_BASIC
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted   => FALSE, 
                    compalg     => 'BASIC');
    
    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/

/***************************************************************
 * DBMS_FEATURE_RMAN_LOW
 *  The procedure to detect usage of RMAN LOW compression
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_LOW
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted   => FALSE, 
                    compalg     => 'LOW');
    
    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/

/***************************************************************
 * DBMS_FEATURE_RMAN_MEDIUM
 *  The procedure to detect usage of RMAN MEDIUM compression
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_MEDIUM
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted   => FALSE, 
                    compalg     => 'MEDIUM');
    
    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/

/***************************************************************
 * DBMS_FEATURE_RMAN_HIGH
 *  The procedure to detect usage of RMAN HIGH compression
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_HIGH
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted   => FALSE, 
                    compalg     => 'HIGH');
    
    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/


/***************************************************************
 * DBMS_FEATURE_BACKUP_ENCRYPTION
 *  The procedure to detect usage of RMAN ENCRYPTION on backups
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_BACKUP_ENCRYPTION
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted   => TRUE, 
                    compalg     => NULL);

    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/


/***************************************************************
 * DBMS_FEATURE_RMAN_BACKUP
 *  The procedure to detect usage of RMAN backups
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_BACKUP
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => FALSE, 
                    encrypted   => FALSE, 
                    compalg     => NULL);

    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/

/***************************************************************
 * DBMS_FEATURE_RMAN_DISK_BACKUP
 *  The procedure to detect usage of RMAN backups on DISK
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_DISK_BACKUP
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => TRUE, 
                    nondiskonly => FALSE, 
                    encrypted   => FALSE, 
                    compalg     => NULL);

    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/

/***************************************************************
 * DBMS_FEATURE_RMAN_TAPE_BACKUP
 *  The procedure to detect usage of RMAN backups
 ***************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_RMAN_TAPE_BACKUP
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
BEGIN

    /* assume that feature is not used. */
    feature_boolean := 0;
    aux_count := 0;
    feature_info := NULL;

    aux_count := sys.dbms_backup_restore.rman_usage(
                    diskonly    => FALSE, 
                    nondiskonly => TRUE, 
                    encrypted   => FALSE, 
                    compalg     => NULL);

    IF aux_count > 0 THEN
       feature_boolean := 1;
    END IF;
END;
/


/***************************************************************
 * DBMS_FEATURE_AUTO_SSM
 *  The procedure to detect usage for Automatic Segment Space
 *  Managed tablespaces
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_auto_ssm
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  auto_seg_space boolean;
  ts_info        varchar2(1000);
BEGIN

  /* initialize everything */
  auto_seg_space := FALSE;
  ts_info        := '';
  aux_count      := 0;

  for ts_type in 
     (select segment_space_management, count(*) tcount, sum(size_mb) size_mb
       from
        (select ts.tablespace_name, segment_space_management, 
              sum(bytes)/1048576 size_mb
          from dba_data_files df, dba_tablespaces ts
         where df.tablespace_name = ts.tablespace_name
         group by ts.tablespace_name, segment_space_management)
       group by segment_space_management)
  loop

    /* check for auto segment space management */    
    if ((ts_type.segment_space_management = 'AUTO') and
         (ts_type.tcount > 0)) then
      auto_seg_space  := TRUE;
      aux_count       := ts_type.tcount;
    end if;

    ts_info := ts_info || 
        '(Segment Space Management: ' || ts_type.segment_space_management ||
       ', TS Count: ' || ts_type.tcount ||
       ', Size MB: '  || ts_type.size_mb || ') ';

  end loop; 

  /* set the boolean and feature info.  the aux count is already set above */
  if (auto_seg_space) then
    feature_boolean := 1;
    feature_info    := to_clob(ts_info);
  else
    feature_boolean := 0;
    feature_info    := null;
  end if;

END dbms_feature_auto_ssm;
/

show errors;


/******************************************************************
 * DBMS_FEATURE_LMT
 *  The procedure to detect usage for Locally Managed tablespaces
 ******************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_lmt
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  loc_managed boolean;
  ts_info     varchar2(1000);  
BEGIN

  /* initialize everything */
  loc_managed := FALSE;
  ts_info     := '';
  aux_count   := 0;

  for ts_type in 
     (select extent_management, count(*) tcount, sum(size_mb) size_mb
       from
        (select ts.tablespace_name, extent_management, 
                sum(bytes)/1048576 size_mb
           from dba_data_files df, dba_tablespaces ts
          where df.tablespace_name = ts.tablespace_name
          group by ts.tablespace_name, extent_management)
       group by extent_management)
  loop

    /* check for auto segment space management */    
    if ((ts_type.extent_management = 'LOCAL') and
         (ts_type.tcount > 0)) then
      loc_managed  := TRUE;
      aux_count       := ts_type.tcount;
    end if;

    ts_info := ts_info || 
        '(Extent Management: ' || ts_type.extent_management ||
       ', TS Count: ' || ts_type.tcount ||
       ', Size MB: '  || ts_type.size_mb || ') ';

  end loop; 

  /* set the boolean and feature info.  the aux count is already set above */
  if (loc_managed) then
    feature_boolean := 1;
    feature_info    := to_clob(ts_info);
  else
    feature_boolean := 0;
    feature_info    := null;
  end if;

END dbms_feature_lmt;
/

show errors;

/******************************************************************
 * DBMS_FEATURE_SEGADV_USER
 *  The procedure to detect usage for Segment Advisor (user)
 ******************************************************************/

CREATE OR REPLACE PROCEDURE dbms_feature_segadv_user
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  execs_since_sample    NUMBER;               -- # of execs since last sample
  total_execs           NUMBER;               -- total # of execs
  total_recs            NUMBER;               -- total # of recommendations
  total_space_saving    NUMBER;               -- total potential space saving
  tmp_buf               VARCHAR2(32767);      -- temp buffer
BEGIN
  -- executions since last sample
  SELECT  count(*) 
  INTO    execs_since_sample
  FROM    dba_advisor_executions
  WHERE   advisor_name = 'Segment Advisor' AND
          task_name not like 'SYS_AUTO_SPCADV%' AND
          execution_last_modified >= (SELECT nvl(max(last_sample_date),
                                                sysdate-7) 
                                     FROM   dba_feature_usage_statistics);
      
  -- total # of executions
  SELECT  count(*) 
  INTO    total_execs
  FROM    dba_advisor_executions
  WHERE   advisor_name = 'Segment Advisor' AND
          task_name not like 'SYS_AUTO_SPCADV%';

  -- total # of recommendations and total potential space saving
  SELECT  count(task.task_id), NVL(sum(msg.p3),0)
  INTO    total_recs, total_space_saving
  FROM    dba_advisor_tasks task, 
          sys.wri$_adv_findings fin,
          sys.wri$_adv_recommendations rec,
          sys.wri$_adv_message_groups msg
  WHERE   task.advisor_name = 'Segment Advisor' AND
          task.task_name not like 'SYS_AUTO_SPCADV%' AND
          task.task_id = rec.task_id AND
          nvl(rec.annotation,0) <> 3 AND
          fin.task_id = rec.task_id AND 
          fin.id = rec.finding_id AND
          msg.task_id = fin.task_id AND 
          msg.id = fin.more_info_id;
  
  -- set feature_used and aux_count 
  feature_boolean := execs_since_sample;
  aux_count := execs_since_sample;

  -- prepare feature_info
  tmp_buf := 'Executions since last sample: ' || execs_since_sample || ', ' ||
             'Total Executions: ' || total_execs || ', ' ||
             'Total Recommendations: ' || total_recs   || ', ' ||
             'Projected Space saving (byte): ' || total_space_saving;

  dbms_lob.createtemporary(feature_info, TRUE);
  dbms_lob.writeappend(feature_info, length(tmp_buf), tmp_buf);
  
END dbms_feature_segadv_user;
/

show errors;

/******************************************************************
 * DBMS_FEATURE_AUM
 *  The procedure to detect usage for Automatic Undo Management
 ******************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_aum
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  ts_info         varchar2(1000);  
  undo_blocks     number;
  max_concurrency number;  
BEGIN

  select count(*) into feature_boolean from v$system_parameter where
    name = 'undo_management' and upper(value) = 'AUTO';

  if (feature_boolean = 0) then
    /* not automatic undo management */
    aux_count    := 0;
    feature_info := null;
  else

    aux_count := 0;

    /* undo tablespace information */
    for ts_type in 
      (select retention, count(*) tcount, sum(size_mb) size_mb
        from
         (select ts.tablespace_name, retention, sum(bytes)/1048576 size_mb
           from dba_data_files df, dba_tablespaces ts
          where df.tablespace_name = ts.tablespace_name
            and ts.contents = 'UNDO'
          group by ts.tablespace_name, retention)
        group by retention)
    loop

      /* track total number of tablespaces */
      aux_count := aux_count + ts_type.tcount;

      ts_info := ts_info || 
          '(Retention: ' || ts_type.retention ||
         ', TS Count: ' || ts_type.tcount ||
         ', Size MB: '  || ts_type.size_mb || ') ';

    end loop; 

    /* get some more information */
    select sum(undoblks), max(maxconcurrency) 
      into undo_blocks, max_concurrency
      from v$undostat
      where begin_time >=
             (SELECT nvl(max(last_sample_date), sysdate-7) 
                FROM dba_feature_usage_statistics);

    ts_info := ts_info || '(Undo Blocks: ' || undo_blocks ||
                         ', Max Concurrency: ' || max_concurrency || ') ';

    for ssold in
      (select to_char(min(begin_time), 'YYYY-MM-DD HH24:MI:SS') btime,
              to_char(max(end_time),   'YYYY-MM-DD HH24:MI:SS') etime,
              sum(SSOLDERRCNT) errcnt
        from v$undostat 
        where (begin_time >=
               (SELECT nvl(max(last_sample_date), sysdate-7) 
                  FROM dba_feature_usage_statistics)))
    loop
      ts_info := ts_info || 
          '(Snapshot Old Info - Begin Time: ' || ssold.btime || 
                        ', End Time: '   || ssold.etime || 
                        ', SSOLD Error Count: ' || ssold.errcnt || ') ';
    end loop;

    feature_boolean := 1;
    feature_info    := to_clob(ts_info);

  end if;

END dbms_feature_aum;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_JOB_SCHEDULER
 *  The procedure to detect usage for DBMS_SCHEDULER
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_job_scheduler
      (is_used OUT number, nr_of_jobs OUT number, summary OUT clob)
AS
sum1 varchar2(4000);
n1 number;
n2 number;
n3 number;
n4 number;
n5 number;
n6 number;
n7 number;
n8 number;
n9 number;

BEGIN
  select count(*) into nr_of_jobs from dba_scheduler_jobs where 
      owner not in ('SYS', 'ORACLE_OCM', 'EXFSYS' ) 
       and job_name not like 'AQ$%'
       and job_name not like 'MV_RF$J_%';

  is_used := nr_of_jobs;
  -- if job used 
  if is_used = 0  then return; end if;

select count(*) into n1 from dba_scheduler_jobs;
    sum1  := sum1 
              || 'JNRA:' || n1
              || ',JNRU:' || nr_of_jobs; 

select count(*) into n1 from dba_jobs;
    sum1  := sum1 
              || ',DJOBS:' || n1; 
-- Direct per job type counts, i.e of the total number of jobs how many are
--  program vs executable vs plsql block vs stored procedure vs chain


  for it in  (
select jt t, count(*) n
from (select   nvl(job_type, 'PROGRAM') jt
from   dba_scheduler_jobs )
group by jt order by 1) 
  loop  
    sum1  := sum1 || ',JTD' || substr(it.t,1,3) || ':' || it.n;
  end loop;

 
-- Indirect per job type counts. 
-- In this case the you have to track down the program type of all 
-- the jobs whose jobs are of type program. 
-- So now of the the total number of jobs, how many are
--  executable vs plsql block vs stored procedure vs chain

  for it in  (
select jt t, count(*) n from 
   (select program_type jt
      from dba_scheduler_jobs j, 
         dba_scheduler_programs p 
    where 
            job_type is null 
            and p.owner = j.program_owner 
            and p.program_name = j.program_name 
    union all 
     select 'NAP' 
      from dba_scheduler_jobs j
       where
            j.job_type is null
            and not exists (select 1 from 
             dba_scheduler_programs p
              where
               p.owner = j.program_owner    
              and p.program_name = j.program_name) 
    union all 
    select   job_type
    from   dba_scheduler_jobs where job_type is not null)
     group by jt order by 1)
  loop  
    sum1  := sum1 || ',JTI' || substr(it.t,1,3) || ':' || it.n;
  end loop;
-- Direct per schedule type counts, i.e. of the total 
-- number of jobs how many are
-- repeat_interval is null, schedule based, event based, file watcher based, 
-- plsql repeat interval, calendar repeat interval, window based



  for it in  (
select schedule_type t, 
         count(*) n 
from   dba_scheduler_jobs 
group by schedule_type order by 1)
  loop  
    sum1  := sum1 || ',JDS' || substr(replace(it.t, 'WINDOW_','W'),1,3) || ':' || it.n;
  end loop;

-- Indirect per schedule type counts. In this case the schedule based jobs are 
-- tracked down to their eventual schedule type. So now of the total number of jobs, how many are
--  repeat_interval is null, event based, file watcher, plsql repeat interval, 
-- calendar repeat interval, window (group) based

  for it in  (
select schedule_type t, count(*) n from 
   (select p.schedule_type
      from dba_scheduler_jobs j, 
         dba_scheduler_schedules p 
    where 
            j.schedule_type = 'NAMED' 
            and p.owner = j.schedule_owner 
            and p.schedule_name = j.schedule_name 
    union all 
    select   schedule_type
    from   dba_scheduler_jobs where schedule_type <> 'NAMED')
     group by schedule_type order by 1)
  loop  
    sum1  := sum1 || ',JIS' || substr(replace(it.t, 'WINDOW_','W'),1,3) || ':' || it.n;
  end loop;
   

-- Number of jobs that have destination set to a 
-- single destination vs destination set to a destination group

 for it in (
select dest t, count(*) n 
   from (select decode(number_of_destinations,1, 'SD', 'MD') dest 
       from dba_scheduler_jobs where destination is not null)
   group by dest order by 1) 
  loop  
    sum1  := sum1 || ',JD' || it.t || ':' || it.n;
  end loop;

-- Number of external jobs (job type or program type executable) split across local without a credential, 
-- local with credential, remote single destination, remote destination group
 for it in (
select ext_type t, count(*) n from
(select job_name, decode(destination, null,
     decode(credential_name, null,'JXL','JXLC'),
     decode(dest_type,null,'JXRID','SINGLE','JXRSD','JXRGD')) ext_type from
(select job_name, job_type, credential_name, destination_owner, destination
from all_scheduler_jobs where program_name is null
union all
select job_name, program_type, credential_name, destination_owner, destination
from all_scheduler_jobs aj, all_scheduler_programs ap
where aj.program_owner = ap.owner and aj.program_name = ap.program_name) aij,
(select owner, group_name dest_name, 'GROUP' dest_type from all_scheduler_groups
where group_type = 'EXTERNAL_DEST'
union all
select 'SYS', destination_name, 'SINGLE' from all_scheduler_external_dests) ad
where job_type in ('EXECUTABLE','EXTERNAL_SCRIPT')  and aij.destination_owner = ad.owner(+) and
aij.destination = ad.dest_name(+)) group by ext_type order by 1)
  loop  
    sum1  := sum1 || ',' || it.t || ':' || it.n;
  end loop;


-- Number of remote database jobs with single destination versus number of jobs with destination group (i.e. destination is set and job type or program type is plsql block or stored procedure).

 for it in (
select dest_type t, count(*) n from
    (select  job_type, destination_owner, destination
        from all_scheduler_jobs where program_name is null
    union all
    select  program_type, destination_owner, destination
        from all_scheduler_jobs aj, all_scheduler_programs ap
            where aj.program_owner = ap.owner and aj.program_name = ap.program_name) aij,
    (select owner, group_name dest_name, 'JDBG' dest_type from all_scheduler_groups
            where group_type = 'DB_DEST'
     union all
     select owner, destination_name, 'JDBS' from all_scheduler_db_dests) ad
 where job_type in  ('STORED_PROCEDURE','PLSQL_BLOCK','SQL_SCRIPT', 'BACKUP_SCRIPT') and
       aij.destination is not null and aij.destination_owner = ad.owner(+) and
       aij.destination = ad.dest_name(+) group by dest_type order by 1)
  loop  
    sum1  := sum1 || ',' || it.t || ':' || it.n;
  end loop;

-- Number of jobs with arguments. For those jobs with arguments, avg,
-- median and max number of job arguments.

select count(*),  
       avg(number_of_arguments), 
       median(number_of_arguments), 
       max(number_of_arguments) into  n1, n2, n3, n4
from dba_scheduler_jobs where number_of_arguments > 0;

    sum1  := sum1 
              || ',JAC:' || n1 
              || ',JAA:' || round(n2) 
              || ',JAM:' || n3 
              || ',JAX:' || n4; 

-- Split total number of jobs across job_style, i.e. regular vs lightweight

 for it in (
select job_style t, count(*) n from dba_scheduler_jobs
     group by job_style order by 1)
  loop  
    sum1  := sum1 || ',JST' || substr(it.t,1,3) || ':' || it.n;
  end loop;
   

-- Number of jobs that have restartable set to true
-- How many have max_run_duration set
-- How many have schedule_limit set
-- How many have instance_id set
-- How many have allow_runs_in_restricted_mode set
-- How many have raise_events set
-- How many have parallel_instances set
select sum(decode(restartable,null, 0,1)),
       sum(decode(max_run_duration,null, 0,1)) ,
       sum(decode(schedule_limit,null, 0,1)) ,
       sum(decode(instance_id,null, 0,1)) ,
       sum(decode(allow_runs_in_restricted_mode,'FALSE', 0,1)) ,
       sum(decode(bitand(flags, 2147483648),2147483648,1,0)),
       sum(decode(bitand(flags, 68719476736),68719476736,1,0)),
       sum(decode(enabled,'FALSE',1,0)),
       sum(decode(raise_events,null, 0,1)) 
             into n1, n2, n3, n4, n5,n6, n7, n8, n9
from dba_scheduler_jobs;
    sum1  := sum1 
              || ',JRS:' || n1 
              || ',JMRD:' || n2 
              || ',JSL:' || n3 
              || ',JII:' || n4 
              || ',JAR:' || n5 
              || ',JFLW:' || n7 
              || ',JRE:' || n9 
              || ',JDIS:' || n8 
              || ',JPI:' || n6; 

-- Total number of programs
-- Per type program numbers, i.e. the number of executable, plsql_block, 
-- stored procedure, chain programs

 for it in (
select program_type t, count(*) n from dba_scheduler_programs 
    group by program_type order by 1)
  loop  
    sum1  := sum1 || ',PRT' || substr(it.t,1,3) || ':' || it.n;
  end loop;


-- Number of programs with arguments
-- For programs with arguments, avg, mean and max number of arguments
select count(*) ,  round(avg(number_of_arguments)) , 
       median(number_of_arguments) , 
      max(number_of_arguments)  
         into n1, n2, n3, n4
from dba_scheduler_programs where number_of_arguments > 0;
    sum1  := sum1 
              || ',PAC:' || n1 
              || ',PAA:' || n2 
              || ',PAM:' || n3 
              || ',PAX:' || n4; 

-- Total number of schedules
-- Split across schedule type. How many in each category:
-- run once, plsql repeat interval, calendar repeat interval, event based, 
-- file watcher, window based


 for it in (
select schedule_type t, count(*) n from dba_scheduler_schedules group by
     schedule_type order by 1)
  loop  
    sum1  := sum1 || ',SST' || substr(it.t,1,3) || ':' || it.n;
  end loop;


-- Total number of arguments
-- How many of them are named arguments

 for it in (
select an t, count(*) n 
    from (select  decode(argument_name, null, 'PA_', 'PAN') an from
    dba_scheduler_program_args) group by an order by 1)
  loop  
    sum1  := sum1 || ',' || it.t || ':' || it.n;
  end loop;

-- Split across count of metadata arguments, varchar based args, anydata based arguments
 for it in (
select metadata_attribute t, count(*) n from dba_scheduler_program_args where 
         metadata_attribute is not null group by metadata_attribute order by 1)
  loop  
    sum1  := sum1 || ',PM' || 
                  substr(replace(replace(it.t,'JOB_','J'),'WINDOW_','W'),1,3) 
                    || ':' || it.n;
  end loop;

-- Job Classes
-- Total number of job classes
-- How many have service set
-- How many have resource consumer group set
-- split across logging levels, i.e. how many no logging, failed runs, runs only, full

select count(*) , sum(decode(service, null, 0, 1)) ,
sum(decode(resource_consumer_group, null, 0, 1)) into n1,n2,n3 
from dba_scheduler_job_classes;
    sum1  := sum1 
              || ',JCNT:' || n1 
              || ',JCSV:' || n2 
              || ',JCCG:' || n3 ;

 for it in (
select logging_level t, count(*) n from dba_scheduler_job_classes 
    group by logging_level order by 1)
  loop  
    sum1  := sum1 || ',LL' || substr(it.t,1,3)  || ':' || it.n;
  end loop;

-- Windows
-- Total number of windows
-- Number of high priority windows (low = total - high)
-- Number of windows without a resource plan
-- Number of named schedule based windows (inlined schedule = total - named schedule)
 for it in (
select window_priority t, count(*) n from dba_scheduler_windows 
    group by window_priority order by 1) 
  loop  
    sum1  := sum1 || ',WIP' || substr(it.t,1,2) || ':' || it.n;
  end loop;

select count(*) into n1 from dba_scheduler_windows  where resource_plan is
 null;
    sum1  := sum1 
              || ',WINR:' || n1;

 for it in (
select st t, count(*) n from  
   (select schedule_type  st
     from
     dba_scheduler_windows)  group by st order by 1) 
  loop  
    sum1  := sum1 || ',SWT' || substr(it.t,1,2) || ':' || it.n;
  end loop;


-- Chains
-- Total number of chains
-- How many have evaluation interval set
-- How many were created with a rule set passed in
-- Total number of steps
-- How many steps have destination set
-- Avg, mean and max number of steps per chain
-- Total number of rules
-- Avg, mean and max number of rules per chain
-- ? How many of them use simple syntax
-- ? Avg, mean and max number of steps per rule condition
-- ? Avg, mean and max number of steps per rule action

select count(*), sum(decode(evaluation_interval, null, 0, 1)) EV,
       sum(decode(user_rule_set, 'TRUE', 1, 0)) UR, 
       sum(nvl(number_of_rules,0)) NR, sum(nvl(number_of_steps,0)) NS, 
       round(avg(number_of_steps)) VS , median(number_of_steps) MS, 
       max(number_of_steps) XS into n1, n2,n3,n4,n5,n6,n7,n8 
    from dba_scheduler_chains;
    sum1  := sum1 
              || ',CCNT:' || n1 
              || ',CEVI:' || n2 
              || ',CURS:' || n3 
              || ',CNRR:' || n4 
              || ',CNRS:' || n5 
              || ',CAVS:' || n6 
              || ',CMDS:' || n7 
              || ',CMXS:' || n8;
   

select count(*) into n1 
    from dba_scheduler_chain_steps where destination is not null; 
    sum1  := sum1 
              || ',CSRD:' || n1 ;

-- Direct per step type counts. Of total how many steps point to:
--    program vs (sub)chain vs event
 for it in (
select step_type t, count(*)  n from dba_scheduler_chain_steps 
   group by step_type order by 1)
  loop  
    sum1  := sum1 || ',CSP' || substr(it.t,1,3) || ':' || it.n;
  end loop;

-- Indirect per step type counts. By following the program type how many are:
--    executable vs plsql block vs stored procedure vs (sub)chain vs event

 for it in (
select step_type t, count(*) n from 
      (select step_type from dba_scheduler_chain_steps  
            where step_type <> 'PROGRAM' 
      union all 
       select program_type from dba_scheduler_programs p,
                                dba_scheduler_chain_steps s 
          where 
           s.step_type = 'PROGRAM' and
          s.program_owner =p.owner and 
          s.program_name = p.program_name)
   group by step_type order by 1)
  loop  
    sum1  := sum1 || ',CHST' || substr(it.t,1,3) || ':' || it.n;
  end loop;
     
-- Total number of credentials
-- How many have database role set
-- How many have windows domain set

select count(*), sum(decode(database_role, null, 0, 1)),
       sum(decode(windows_domain, null, 0, 1)) 
     into n1,n2,n3
    from dba_scheduler_credentials;
    sum1  := sum1 
              || ',CRNR:' || n1  
              || ',CRDB:' || n2  
              || ',CSWD:' || n3 ;

-- Total number of destinations
-- How many database destinations (external dests = total - database dests)
-- Of the database destinations, how many specified connect info (non null tns_name)

 for it in (
select dt t, count(*) n from 
   (select decode(destination_type, 'EXTERNAL', 'DSXT', 'DSDB') dt
     from dba_scheduler_dests )
    group by dt order by 1)
  loop  
    sum1  := sum1 || ',' || it.t || ':' || it.n;
  end loop;

select count(*) into n1 from dba_scheduler_db_dests 
         where connect_info is null;
    sum1  := sum1 
              || ',DSDN:' || n1  ;
-- File Watcher
-- Total number of file watchers
-- How many remote file watchers (destination is non null)
-- How many have minimum file size > 0
-- How many have steady_state_duration set to a non-null value
select count(*), 
       sum(decode(steady_state_duration, null, 0,1)),
       sum(decode(destination, null, 0,1)),
       sum(decode(nvl(min_file_size,0), 0, 0, 1))
      into n1,n2,n3,n4
 from dba_scheduler_file_watchers;
    sum1  := sum1 
              || ',FWNR:' || n1  
              || ',FWSS:' || n2  
              || ',FWDS:' || n3  
              || ',FWMF' || n4  ;


-- Groups
-- Total number of groups
-- Per group type count, i.e. how many are db_dest vs external_dest vs window
-- Avg, mean and max number of members per group

 for it in (
select group_type t, count(*) n , round(avg(number_of_members)) a ,
              max(number_of_members) b,
              median(number_of_members) c
        from dba_scheduler_groups group by group_type order by 1)
  loop  
    sum1  := sum1 || ',G' || substr(it.t,1,3) || 'N:' || it.n
                        || ',G' || substr(it.t,1,3) || 'A:' || it.a
                        || ',G' || substr(it.t,1,3) || 'X:' || it.b
                        || ',G' ||substr( it.t,1,3) || 'M:' || it.c;
  end loop;


-- Calendar Syntax
-- Total number of schedules
-- Total number of non-null repeat_intervals schedules
-- Of the calendar syntax ones how many:
-- use include, exclude, or intersect
-- have a user defined frequency
-- use offset

select count(*) into n1 from dba_scheduler_schedules; 
    sum1  := sum1 
              || ',SCHNRA:' || n1;  

select count(*) into n1 from dba_scheduler_schedules
       where repeat_interval is not null;
    sum1  := sum1 
              || ',SCHNNR:' || n1;  
                             
 for it in (
select typ t, count(*) n from 
      (select decode(instr(i,'FREQ=YEARLY'),1, 'Y', 
        decode(instr(i, 'FREQ=MONTHLY'),1,'M', 
         decode(instr(i,'FREQ=WEEKLY'),1, 'W', 
          decode(instr(i,'FREQ=DAILY'),1, 'D', 
           decode(instr(i,'FREQ=HOURLY'),1, 'H', 
           decode(instr(i,'FREQ=MINUTELY'),1, 'MI', 
           decode(instr(i,'FREQ=SECONDLY'),1, 'S',
           decode(instr(i,'FREQ='),1, 'U','X')))))))) typ
      from (select replace(upper(iv), ' ', '') i from (
         select repeat_interval iv 
        from dba_scheduler_jobs 
          where schedule_type = 'CALENDAR' 
       union all select repeat_interval from dba_scheduler_schedules where
         schedule_type = 'CALENDAR')))
 group by typ order by 1)
  loop  
    sum1  := sum1 || ',CAF' || it.t || ':' || it.n;
  end loop;


select sum(decode(instr(i, 'OFFSET'), 0, 0, 1)) "Offset",  
       sum(decode(instr(i, 'SPAN'), 0, 0, 1)) "Span",  
       sum(decode(instr(i, 'BYSETPOS'), 0, 0, 1)) "Bysetp",  
       sum(decode(instr(i, 'INCLUDE'), 0, 0, 1)) "Inc",  
       sum(decode(instr(i, 'EXCLUDE'), 0, 0, 1)) "EXC",  
      sum(decode(instr(i, 'INTERSECT'), 0, 0, 1)) "ISEC"
      into n1,n2,n3,n4,n5,n6
from (select replace(upper(iv), ' ', '') i from (
   select repeat_interval iv 
  from dba_scheduler_jobs 
    where schedule_type = 'CALENDAR' 
 union all select repeat_interval from dba_scheduler_schedules where
   schedule_type = 'CALENDAR'));
    sum1  := sum1 
              || ',CAOF:' || n1  
              || ',CASC:' || n2  
              || ',CABS:' || n3  
              || ',CAIC:' || n4  
              || ',CAEX:' || n5  
              || ',CAIS:' || n6;  
 

select count (distinct owner||job_name) into n1
     from dba_scheduler_notifications;
    sum1  := sum1 
              || ',SNNR:' || n1;  

 for it in (
select event t, count(*) n
     from dba_scheduler_notifications
     group by event order by 1)
  loop  
    sum1  := sum1 || ',JN' 
               || substr(replace(it.t, 'JOB_','J'),1,5) || ':' || it.n;
  end loop;
  summary := to_clob(sum1);
END;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_EXADATA
 *  The procedure to detect usage for EXADATA storage
 ***************************************************************/
create or replace procedure DBMS_FEATURE_EXADATA
    (feature_boolean  OUT  NUMBER,
     num_cells        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  feature_usage          varchar2(1000);
begin
  -- initialize
  num_cells := 0;
  feature_boolean := 0;
  feature_info := to_clob('EXADATA usage not detected');
  feature_usage := '';

  num_cells := sys.dbms_registry.num_of_exadata_cells();

  if num_cells > 0
  then

    feature_boolean := 1;

    feature_usage := feature_usage||':cells:'||num_cells;

    feature_info := to_clob(feature_usage);

  end if;

end;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_IDH
 *  The procedure to detect usage for In-Database Hadoop
 ***************************************************************/
create or replace procedure DBMS_FEATURE_IDH
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  feature_usage          varchar2(1000);
  cursor c1 is select grantee,granted_role 
                      from dba_role_privs where
                      granted_role = 'DBHADOOP' and grantee != 'SYS';
begin
  -- initialize
  aux_count := 0;
  feature_boolean := 0;
  feature_info := to_clob('In-Database Hadoop usage not detected');
  feature_usage := '';

  for i in c1
  loop
    aux_count := aux_count + 1; 
  end loop;

  if aux_count > 0
  then
    feature_boolean := 1;
    feature_usage := feature_usage||':DBHADOOP users:'||aux_count;
    feature_info := to_clob(feature_usage);
  end if;

end;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_GATEWAYS
 *  The procedure to detect usage for Oracle database gateways.
 ***************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_GATEWAYS
    ( feature_boolean OUT NUMBER,
      aux_count        OUT NUMBER,
      feature_info    OUT CLOB )
AS
  TYPE GtwCursorRef is REF CURSOR;
  TYPE ResultList   is TABLE of varchar(150);
  cur GtwCursorRef;
  res ResultList; 
  hs_sql varchar(250);
  i number;
BEGIN
 /*
 We are using "execute immediate" to query HS tables
 because they are not present at the moment this
 stored procedure is created.
 */ 

  hs_sql := 'select count(*)  ' ||
            '  from   HS_FDS_CLASS '        ||
            '  where fds_class_name <> ''BITE''';
  execute immediate hs_sql into aux_count;

  feature_boolean := aux_count;

  if aux_count = 0
  then
    feature_info := 'This feature is not used.';

    return;
  end if;

  feature_info := 'Num of FDS classes:' || aux_count;

  open cur for 'select ''(ID:''  || FDS_CLASS_ID || ''' ||
               ',NAME:'' || FDS_CLASS_NAME || '',' ||
               'COMMENTS:'' || substr(FDS_CLASS_COMMENTS, 1, 110) || '')''' ||
               ' from HS_FDS_CLASS where FDS_CLASS_NAME <> ''BITE''';
  fetch cur bulk collect into res;
  close cur;

  for i in res.FIRST .. res.LAST loop
    feature_info := feature_info || ',' || res(i);
    /* make sure we don't reach the 1000 chars limit */ 
    if LENGTH(feature_info) > 850 
    then
      feature_info := feature_info || '...'; 
      return;
    end if;
  end loop;

  hs_sql := 'select count(*)  ' ||
            '  from   HS_FDS_INST '        ||
            '  where fds_class_name <> ''BITE''';
  execute immediate hs_sql into i;

  feature_info := feature_info || ',Num of FDS instances:' || i;

  if i > 0 then
    open cur for 'select ''(CLASS:''  || FDS_CLASS_ID || '',ID:'' || ' ||
                 'FDS_INST_ID || '',NAME:'' || FDS_INST_NAME || ' ||
                 ''',COMMENTS:'' || substr(FDS_INST_COMMENTS, 1, 110)' ||
                 ' || '')'' from HS_FDS_INST where FDS_CLASS_NAME <> ''BITE''';
    fetch cur bulk collect into res;
    close cur;
  
    for i in res.FIRST .. res.LAST loop
      feature_info := feature_info || ',' || res(i);
      /* make sure we don't reach the 1000 chars limit */ 
      if LENGTH(feature_info) > 850 
      then
        feature_info := feature_info || '...';  
        return;
      end if;
    end loop;
  end if;
END;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_UTILITIES1
 *  The procedure to detect usage for Oracle database Utilities
 *  for datapump export.
 *  Also reports on compression/encryption usage if
 *  applicable. 
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_utilities1
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage      VARCHAR2(1000) := NULL;
   feature_count      NUMBER := 0;
   compression_count  NUMBER := 0;
   compressbas_count  NUMBER := 0;
   compresslow_count  NUMBER := 0;
   compressmed_count  NUMBER := 0;
   compresshgh_count  NUMBER := 0;
   encryption_count   NUMBER := 0;
   encrypt128_count   NUMBER := 0;
   encrypt192_count   NUMBER := 0;
   encrypt256_count   NUMBER := 0;
   encryptpwd_count   NUMBER := 0;
   encryptdual_count  NUMBER := 0;
   encrypttran_count  NUMBER := 0;
   parallel_count     NUMBER := 0;
   fulltts_count      NUMBER := 0;
BEGIN
  -- initialize
  feature_info      := NULL;

  -- Select stats from ku_utluse.
  begin
    select usecnt, encryptcnt, encrypt128, encrypt192, encrypt256,
           encryptpwd, encryptdual, encrypttran, compresscnt,
           compressbas, compresslow, compressmed, compresshgh, parallelcnt,
           fullttscnt
      into feature_count, encryption_count, encrypt128_count, encrypt192_count,
           encrypt256_count, encryptpwd_count, encryptdual_count,
           encrypttran_count, compression_count, compressbas_count,
           compresslow_count, compressmed_count, compresshgh_count,
           parallel_count, fulltts_count
      from sys.ku_utluse
     where utlname = 'Oracle Utility Datapump (Export)'
       and   (last_used >=
              (SELECT nvl(max(last_sample_date), sysdate-7)
                 FROM dba_feature_usage_statistics));
  exception
    when others then
      null;
  end;

  feature_usage := feature_usage || 'Oracle Utility Datapump (Export) ' || 
                'invoked: ' || feature_count || 
                ' times, compression used: '      || compression_count ||
                ' times (BASIC algorithm used: '  || compressbas_count ||
                ' times, LOW algorithm used: '    || compresslow_count ||
                ' times, MEDIUM algorithm used: ' || compressmed_count ||
                ' times, HIGH algorithm used: '   || compresshgh_count ||
                ' times), encryption used: '      || encryption_count  ||
                ' times (AES128 algorithm used: ' || encrypt128_count  ||
                ' times, AES192 algorithm used: ' || encrypt192_count  ||
                ' times, AES256 algorithm used: ' || encrypt256_count  ||
                ' times, PASSWORD mode used: '    || encryptpwd_count  ||
                ' times, DUAL mode used: '        || encryptdual_count ||
                ' times, TRANSPARENT mode used: ' || encrypttran_count ||
                ' times), parallel used: '        || parallel_count    ||
                ' times, full transportable used: ' || fulltts_count   ||
                ' times';

  feature_info := to_clob(feature_usage);

  feature_boolean := feature_count;
  aux_count       := feature_count;
END dbms_feature_utilities1;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_UTILITIES2
 *  The procedure to detect usage for Oracle database Utilities
 *  for datapump import
 *  Also reports on compression/encryption usage if
 *  applicable. 
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_utilities2
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage      VARCHAR2(1000) := NULL;
   feature_count      NUMBER := 0;
   parallel_count     NUMBER := 0;
   fulltts_count      NUMBER := 0;
BEGIN
  -- initialize
  feature_info := NULL;

  begin
    select usecnt, parallelcnt, fullttscnt
      into feature_count, parallel_count, fulltts_count
      from sys.ku_utluse
     where utlname = 'Oracle Utility Datapump (Import)'
       and   (last_used >=
              (SELECT nvl(max(last_sample_date), sysdate-7)
                 FROM dba_feature_usage_statistics));
  exception
    when others then
      null;
  end;

  feature_usage := feature_usage || 'Oracle Utility Datapump (Import) ' || 
                   'invoked: ' || feature_count ||
                   ' times, parallel used: ' || parallel_count ||
                   ' times, full transportable used: ' || fulltts_count ||
                   ' times';

  feature_info := to_clob(feature_usage);

  feature_boolean := feature_count;
  aux_count       := feature_count;
END dbms_feature_utilities2;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_UTILITIES3
 *  The procedure to detect usage for Oracle database Utilities
 *  for MetaData API.
 *
 *  Although this information could have been detected by a SQL
 *  statement, we'll leave this procedure for future feature
 *  tracking.
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_utilities3
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS 
   feature_usage      VARCHAR2(1000) := NULL;
   feature_count      NUMBER := 0;
BEGIN
  -- initialize     
  feature_info      := NULL;
  
  begin
    select usecnt into feature_count from sys.ku_utluse
      where utlname = 'Oracle Utility Metadata API'
       and   (last_used >=
             (SELECT nvl(max(last_sample_date), sysdate-7)
               FROM dba_feature_usage_statistics));
  exception
    when others then
      null;
  end;

  feature_boolean := feature_count;
  aux_count       := feature_count;
END dbms_feature_utilities3;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_UTILITIES4
 *  The procedure to detect usage for Oracle database Utilities
 *  for external tables (ORACLE_DATAPUMP). 
 *  Also reports on compression/encryption usage
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_utilities4
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage      VARCHAR2(1000) := NULL;
   feature_count      NUMBER := 0;
   compression_count  NUMBER := 0;
   compressbas_count  NUMBER := 0;
   compresslow_count  NUMBER := 0;
   compressmed_count  NUMBER := 0;
   compresshgh_count  NUMBER := 0;
   encryption_count   NUMBER := 0;
BEGIN
  -- initialize
  feature_info      := NULL;

  begin
    select usecnt, encryptcnt, compresscnt, compressbas, compresslow, 
           compressmed, compresshgh
      into feature_count, encryption_count, compression_count,
           compressbas_count, compresslow_count, compressmed_count,
           compresshgh_count
      from sys.ku_utluse
      where utlname = 'Oracle Utility External Table (ORACLE_DATAPUMP)'
      and   (last_used >=
            (SELECT nvl(max(last_sample_date), sysdate-7)
               FROM dba_feature_usage_statistics));
  exception
    when others then
      null;
  end;

  feature_usage := 'Oracle Utility External Table (ORACLE_DATAPUMP) invoked '   || 
                   feature_count                                                || 
                   ' times, compression used: '      || compression_count ||
                   ' times (BASIC algorithm used: '  || compressbas_count ||
                   ' times, LOW algorithm used: '    || compresslow_count ||
                   ' times, MEDIUM algorithm used: ' || compressmed_count ||
                   ' times, HIGH algorithm used: '   || compresshgh_count ||
                   ' times), encryption used: '      || encryption_count  ||
                   ' times';

  feature_info := to_clob(feature_usage);

  feature_boolean := feature_count;
  aux_count       := feature_count;
END dbms_feature_utilities4;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_UTILITIES5
 *  The procedure to detect usage for Oracle database Utilities
 *  for external tables (ORACLE_LOADER). 
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_utilities5
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage      VARCHAR2(1000) := NULL;
   feature_count      NUMBER := 0;
BEGIN
  -- initialize
  feature_info      := NULL;

  begin
    select usecnt
      into feature_count
      from sys.ku_utluse
      where utlname = 'Oracle Utility External Table (ORACLE_LOADER)'
      and   (last_used >=
            (SELECT nvl(max(last_sample_date), sysdate-7)
               FROM dba_feature_usage_statistics));
  exception
    when others then
      null;
  end;

  feature_usage := 'Oracle Utility External Table (ORACLE_LOADER) invoked ' ||
                   feature_count                                            || 
                   ' times';

  feature_info := to_clob(feature_usage);

  feature_boolean := feature_count;
  aux_count       := feature_count;
END dbms_feature_utilities5;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_UTILITIES6
 *  The procedure to detect usage for Oracle database Utilities
 *  for external tables (ORACLE_BIGSQL). 
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_utilities6
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage      VARCHAR2(1000) := NULL;
   feature_count      NUMBER := 0;
BEGIN
  -- initialize
  feature_info      := NULL;

  begin
    select usecnt
      into feature_count
      from sys.ku_utluse
      where utlname = 'Oracle Utility External Table (ORACLE_BIGSQL)'
      and   (last_used >=
            (SELECT nvl(max(last_sample_date), sysdate-7)
               FROM dba_feature_usage_statistics));
  exception
    when others then
      null;
  end;

  feature_usage := 'Oracle Utility External Table (ORACLE_BIGSQL) invoked ' ||
                   feature_count                                            || 
                   ' times';

  feature_info := to_clob(feature_usage);

  feature_boolean := feature_count;
  aux_count       := feature_count;
END dbms_feature_utilities6;
/

show errors;

/********************************************************* 
* DBMS_FEATURE_AWR
* counts snapshots since last sample
* also counts DB time and DB cpu over last 7 days
*********************************************************/ 
create or replace procedure DBMS_FEATURE_AWR
     ( feature_boolean_OUT  OUT  NUMBER,
       aux_count_OUT        OUT  NUMBER,
       feature_info_OUT     OUT  CLOB)
AS
  DBFUS_LAST_SAMPLE_DATE  DATE;

  l_DBtime7day_secs   number;
  l_DBcpu7day_secs    number;

  -- cursor fetches last 7 days of AWR snapshot DB time and DB cpu
  cursor TimeModel7day_cur
  IS
WITH snap_ranges AS
(select /*+ FULL(ST) */
        SN.dbid
       ,SN.instance_number
       ,SN.startup_time
       ,ST.stat_id
       ,ST.stat_name
       ,MIN(SN.snap_id) as MIN_snap
       ,MAX(SN.snap_id) as MAX_snap
       ,MIN(CAST(begin_interval_time AS DATE)) as MIN_date
       ,MAX(CAST(end_interval_time AS DATE)) as MAX_date
   from
        dba_hist_snapshot   SN
       ,wrh$_stat_name      ST
  where 
        SN.begin_interval_time > TRUNC(SYSDATE) - 7
    and SN.end_interval_time   < TRUNC(SYSDATE)
    and SN.dbid = ST.dbid
    and ST.stat_name IN ('DB time', 'DB CPU')
  group by
        SN.dbid,SN.instance_number,SN.startup_time,ST.stat_id,ST.stat_name
)
,delta_data AS
(select
        SR.dbid
       ,SR.instance_number
       ,SR.stat_name
       ,CASE WHEN SR.startup_time BETWEEN SR.MIN_date AND SR.MAX_date
               THEN TM1.value + (TM2.value - TM1.value)
             ELSE (TM2.value - TM1.value)
        END
        as delta_time
   from
        WRH$_SYS_TIME_MODEL   TM1
       ,WRH$_SYS_TIME_MODEL   TM2
       ,snap_ranges           SR
  where
        TM1.dbid = SR.dbid
    and TM1.instance_number = SR.instance_number
    and TM1.snap_id         = SR.MIN_snap
    and TM1.stat_id         = SR.stat_id
    and TM2.dbid = SR.dbid
    and TM2.instance_number = SR.instance_number
    and TM2.snap_id         = SR.MAX_snap
    and TM2.stat_id         = SR.stat_id
)
select
       stat_name
      ,ROUND(SUM(delta_time/1000000),2) as secs
  from
       delta_data
 group by
       stat_name;

begin
  --> initialize OUT parameters
  feature_boolean_OUT := 0;
  aux_count_OUT       := null;
  feature_info_OUT    := null;

  --> initialize last sample date
  select nvl(max(last_sample_date), sysdate-7)  
    into DBFUS_LAST_SAMPLE_DATE
   from wri$_dbu_usage_sample;

  if DBFUS_LAST_SAMPLE_DATE IS NOT NULL
  then
    --> get snapshot count since last sample date
    select count(*) 
      into feature_boolean_OUT
      from wrm$_snapshot 
     where dbid = (select dbid from v$database) 
       and status = 0 
       and bitand(snap_flag, 1) = 1 
       and end_interval_time > DBFUS_LAST_SAMPLE_DATE;
  end if;

  --> fetch 7 day DB time and DB CPU from AWR
  for TimeModel7day_rec in TimeModel7day_cur
  loop
    case TimeModel7day_rec.stat_name
      when 'DB time' then l_DBtime7day_secs := TimeModel7day_rec.secs;
      when 'DB CPU'  then l_DBcpu7day_secs := TimeModel7day_rec.secs;
    end case;
  end loop;

  --> assemble feature info CLOB
  feature_info_OUT := 'DBtime:'||TO_CHAR(l_DBtime7day_secs)||
                      ',DBcpu:'||TO_CHAR(l_DBcpu7day_secs);


end;
/
show errors


/***************************************************************
 * DBMS_FEATURE_DATABASE_VAULT
 *  The procedure to detect usage for Oracle Database Vault
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_database_vault
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   dv_linkon          NUMBER;
   dvsys_uid          NUMBER;
   dvowner_uid        NUMBER;
   dvacctmgr_uid      NUMBER;
BEGIN
  -- initialize
  feature_boolean   := 0;
  aux_count         := 0;
  feature_info      := NULL;

  -- check to see if DV is linked on
  select count(*) into dv_linkon from v$option where 
     parameter = 'Oracle Database Vault' and
     value = 'TRUE';

  if (dv_linkon = 0) then
    return;
  end if;

  -- get DVSYS hard coded uid
  select count(*) into dvsys_uid from user$ where
    name = 'DVSYS' and
    user# = 1279990;

  -- get uids for hard coded roles
  select count(*) into dvowner_uid from user$ where 
     name = 'DV_OWNER' and
     user# = 1279992;
  select count(*) into dvacctmgr_uid from user$ where
     name = 'DV_ACCTMGR' and
     user# = 1279991;

  if (dvsys_uid = 0 or
      dvowner_uid = 0 or
      dvacctmgr_uid = 0) then
     return;
  end if;
  
  feature_boolean := 1;

END dbms_feature_database_vault;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_DEFERRED_SEG_CRT
 *  The procedure to detect usage for the deferred segment
 *  creation feature.
 ***************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_DEFERRED_SEG_CRT
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage        VARCHAR2(1000);
   table_count          NUMBER;
   index_count          NUMBER;
   lob_count            NUMBER;
   tabpart_count        NUMBER;
   indpart_count        NUMBER;
   lobpart_count        NUMBER;
   tabsubpart_count     NUMBER;
   indsubpart_count     NUMBER;
   lobsubpart_count     NUMBER;
   total_segments       NUMBER;
   total_def_segments   NUMBER;
BEGIN
  -- initialize
  feature_boolean    := 0;
  aux_count          := 0;
  feature_info       := NULL;
  feature_usage      := NULL;
  table_count        := 0;
  index_count        := 0;
  lob_count          := 0;
  tabpart_count      := 0;
  indpart_count      := 0;
  lobpart_count      := 0;
  tabsubpart_count   := 0;
  indsubpart_count   := 0;
  lobsubpart_count   := 0;
  total_segments     := 0;
  total_def_segments := 0;

  -- check to see if DSC parameter is turned on
  select count(*) into feature_boolean from v$system_parameter where 
     name = 'deferred_segment_creation' and value = 'TRUE';

  -- Regardless of the value of the parameter, compute the number of 
  -- objects that do not yet have segments created

  -- non-partitioned tables
--  select count(*) into table_count from dba_tables where 
--      segment_created = 'NO';

  select count(*) into table_count from 
  (  select decode(bitand(t.property, 17179869184), 17179869184, 'NO', 
                   decode(bitand(t.property, 32), 32, 'N/A', 'YES')) x 
     from tab$ t
  ) 
  where x = 'NO';

  -- non-partitioned indexes
--  select count(*) into index_count from dba_indexes where 
--      segment_created = 'NO';

  select count(*) into index_count from 
  (  select  decode(bitand(i.flags, 67108864), 67108864, 'NO','?')  x
     from ind$ i
   )
   where x = 'NO';

  -- non-partitioned lobs
--  select count(*) into lob_count from dba_lobs where 
--      segment_created = 'NO';

  select count(*) into lob_count from 
  ( select decode(bitand(l.property, 4096), 4096, 'NO','?') x
    from lob$ l
   )
   where x = 'NO';

  -- table partitions
--  select count(*) into tabpart_count from dba_tab_partitions where 
--      segment_created = 'NO';

  select count(*) into tabpart_count from
  ( select  decode(bitand(tp.flags, 65536), 65536, 'NO', 'YES') x 
    from tabpart$ tp
  ) where x = 'NO';

  -- index partitions
--  select count(*) into indpart_count from dba_ind_partitions where 
--      segment_created = 'NO';

  select count(*) into indpart_count from
  ( select  decode(bitand(ip.flags, 65536), 65536, 'NO', 'YES') x 
    from indpart$ ip
  ) where x = 'NO';

  -- lob partitions
--  select count(*) into lobpart_count from dba_lob_partitions where 
--      segment_created = 'NO';

    select count(*) into lobpart_count from
  ( select decode(bitand(lf.fragflags, 33554432), 33554432, 'NO', 'YES') x
    from lobfrag$ lf where lf.fragtype$='P'
  ) where x = 'NO';

  -- table sub-partitions
--  select count(*) into tabsubpart_count from dba_tab_subpartitions where 
--      segment_created = 'NO';

  select count(*) into tabsubpart_count from
  ( select  decode(bitand(tsp.flags, 65536), 65536, 'NO', 'YES') x 
    from tabsubpart$ tsp
  ) where x = 'NO';

  -- index sub-partitions
--  select count(*) into indsubpart_count from dba_ind_subpartitions where 
--      segment_created = 'NO';

  select count(*) into indsubpart_count from
  ( select  decode(bitand(isp.flags, 65536), 65536, 'NO', 'YES') x 
    from indsubpart$ isp
  ) where x = 'NO';

  -- lob sub-partitions
--  select count(*) into lobsubpart_count from dba_lob_subpartitions where 
--      segment_created = 'NO';

  select count(*) into lobsubpart_count from
  ( select decode(bitand(lf.fragflags, 33554432), 33554432, 'NO', 'YES') x
    from lobfrag$ lf where lf.fragtype$='S'
  ) where x = 'NO';

  -- Total segments of objects which can have deferred segment creation
--  select count(*) into total_segments from dba_segments where
--      segment_type IN ('TABLE', 
--                       'INDEX', 
--                       'LOBSEGMENT', 
--                       'LOBINDEX', 
--                       'TABLE PARTITION', 
--                       'INDEX PARTITION', 
--                       'LOB PARTITION' );

 select count(*) into total_segments from seg$ where type# in (5,6,8);
 
  -- Total # of segments whose creation is deferred
  total_def_segments := table_count + index_count + lob_count +
                        tabpart_count + indpart_count + lobpart_count +
                        tabsubpart_count + indsubpart_count + lobsubpart_count;

  feature_usage := feature_usage || 'Deferred Segment Creation ' || 
                   ' Parameter:' || feature_boolean ||
                   ' Total Deferred Segments:' || total_def_segments || 
                   ' Total Created Segments:' || total_segments   ||
                   ' Table Segments:' || table_count   ||
                   ' Index Segments:' || index_count   ||
                   ' Lob Segments:'   || lob_count   ||
                   ' Table Partition Segments:' || tabpart_count   ||
                   ' Index Partition Segments:' || indpart_count   ||
                   ' Lob Partition Segments:'   || lobpart_count   ||
                   ' Table SubPartition Segments:' || tabsubpart_count   ||
                   ' Index SubPartition Segments:' || indsubpart_count   ||
                   ' Lob SubPartition Segments:'   || lobsubpart_count;

  -- update feature_boolean if even one segment is uncreated
  if (total_def_segments > 0) then
    feature_boolean := feature_boolean+1;
  end if;

  feature_info    := to_clob(feature_usage);
  aux_count       := total_def_segments;

END dbms_feature_deferred_seg_crt;
/

show errors;

/***************************************************************
 * DBMS_FEATURE_DBFS_CONTENT
 *  The procedure to detect usage of the Database File System 
 *  Content (DBFS Content) feature
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_dbfs_content
    (feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  table_not_found        exception;
  PRAGMA EXCEPTION_INIT(table_not_found, -942);
  num_content_stores     number;
BEGIN
  -- initialize
  feature_boolean    := 0;
  aux_count          := 0;
  num_content_stores := 0;

  -- check existence of content stores
  BEGIN
    execute immediate 'SELECT COUNT(*) FROM sys.dbfs$_stores' 
      INTO num_content_stores;
  EXCEPTION
    WHEN table_not_found THEN NULL;
  END;

  feature_boolean := num_content_stores;
  IF feature_boolean <> 0
  THEN
    feature_info := to_clob('DBFS Content feature in use. ' || 
                               num_content_stores || 
                               ' DBFS Content stores detected.');  
  ELSE
    feature_info := to_clob('DBFS Content feature not in use.');  
  END IF;
END;
/
show errors

/***************************************************************
 * DBMS_FEATURE_DBFS_SFS
 *  The procedure to detect usage of the Database File System 
 *  SecureFile Store (DBFS SFS) feature
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_dbfs_sfs
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  table_not_found        exception;
  PRAGMA EXCEPTION_INIT(table_not_found, -942);
  num_sf_stores          number;
BEGIN
  -- initialize
  feature_boolean := 0;
  aux_count := 0;
  num_sf_stores := 0;

  -- check existence of SecureFile stores (POSIX file-systems)
  BEGIN
    execute immediate 'SELECT COUNT(*) FROM sys.dbfs_sfs$_fs' 
      INTO num_sf_stores;
  EXCEPTION
    WHEN table_not_found THEN NULL;
  END;

  feature_boolean := num_sf_stores;
  IF feature_boolean <> 0
  THEN
    feature_info := to_clob('DBFS SFS feature in use. ' || 
                              num_sf_stores || 
                              ' DBFS SF stores detected.');
  ELSE
    feature_info := to_clob('DBFS SFS feature is not in use.');
  END IF;  
END;
/
show errors

/***************************************************************
 * DBMS_FEATURE_DBFS_HS
 *  The procedure to detect usage of the Database File System 
 *  Hierarchical Store (DBFS HS) feature
 ***************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_dbfs_hs
    (feature_boolean  OUT  NUMBER,
     aux_count        OUT  NUMBER,
     feature_info     OUT  CLOB)
AS
  table_not_found        exception;
  PRAGMA EXCEPTION_INIT(table_not_found, -942);
  num_hierarchical_stores number;
BEGIN
  -- initialize
  feature_boolean := 0;
  aux_count := 0;
  num_hierarchical_stores := 0;

  -- check existence of content stores of HS sub-types
  BEGIN
    execute immediate 'SELECT COUNT(*)  FROM sys.dbfs_hs$_fs' 
      INTO num_hierarchical_stores;
  EXCEPTION
    WHEN table_not_found THEN NULL;
  END;

  feature_boolean := num_hierarchical_stores;
  IF feature_boolean <> 0
  THEN
    feature_info := to_clob('DBFS HS in use. ' || 
                              num_hierarchical_stores ||
                              ' DBFS hierarchical stores detected.');
  ELSE
    feature_info := to_clob('DBFS HS feature not in use.');
  END IF;
END;
/
show errors

/***************************************************************
 * DBMS_FEATURE_DMU
 *  The procedure to detect usage for DMU
 ***************************************************************/
CREATE OR REPLACE PROCEDURE DBMS_FEATURE_DMU
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
  v_usage_value   varchar2(4000);
  v_last_used     date;
  v_last_sampled  date;
BEGIN
  --
  -- start with 'DMU usage not detected'
  -- we do not utilize aux_count.
  --
  feature_boolean := 0;
  feature_info := to_clob('DMU usage not detected');
  aux_count := 0;
  --
  -- test if DMU was used since last sampled date
  --
  begin
    --
    -- get the date DMU was used last time
    --
    select value$ into v_usage_value
      from sys.props$
     where name = 'NLS_DMU_USAGE';
    v_last_used := to_date(substr(v_usage_value,1,instr(v_usage_value,',')-1),
                           'YYYYMMDDHH24MISS');
    --
    -- get the date sampled last time
    --
    select nvl(max(last_sample_date), sysdate-7)
      into v_last_sampled
      from wri$_dbu_usage_sample;
    --
    -- DMU usage is detected
    --
    if v_last_sampled < v_last_used then
      feature_boolean := 1;
      feature_info := to_clob(v_usage_value);
    end if;
  exception
    --
    -- DMU usage is not detected if any exception is thrown including:
    --  * NLS_DMU_USAGE not found in sys.props$
    --  * the value is not in the format of 'YYYYMMDDHH24MISS'
    --
    when others then
      null;
  end;
END DBMS_FEATURE_DMU;
/
show errors;

/***************************************************************
 * DBMS_FEATURE_RAS
 *  The procedure to detect usage for Real Application Security
 ***************************************************************/

create or replace procedure DBMS_FEATURE_RAS
     ( feature_boolean  OUT NUMBER,
       aux_count        OUT NUMBER,
       feature_info     OUT CLOB)
AS
  row_count1              PLS_INTEGER;
  row_count2              PLS_INTEGER;
  policy_count            PLS_INTEGER;
  applied_policy_count    PLS_INTEGER;
  acl_count               PLS_INTEGER;
  ace_count               PLS_INTEGER;
  user_count              PLS_INTEGER;
  role_count              PLS_INTEGER;
  sc_count                PLS_INTEGER;
  privilege_count         PLS_INTEGER;
  session_count           PLS_INTEGER;
  external_session_count  PLS_INTEGER;
  regular_session_count   PLS_INTEGER;
  dispatcher_used         VARCHAR2(5);
  midtier_cache_used      VARCHAR2(5);
  max_seeded_id           NUMBER := 2147493647;
begin
 feature_boolean := 0;
 feature_info := to_clob('Real Application Security usage not detected');
 aux_count := 0;

 begin
   
   /* Check if Real Application Security objects are created. */
   select count(*) into row_count1 from sys.xs$obj where id > max_seeded_id and BITAND(flags,1) = 0;
   if row_count1 > 0 then
     feature_boolean := 1;
     
     /* Find the number of XDS policies. */
     select count(*) into policy_count from sys.xs$dsec;
     
     if policy_count > 0 then
     
       /* Find the number of applied XDS policies. */
       select count(*) into applied_policy_count from sys.DBA_XS_APPLIED_POLICIES p 
              where p.status = 'ENABLED';
     end if;

     /* Find the number of ACLs. */
     select count(*) into acl_count from sys.xs$acl where acl# > max_seeded_id;
     
     /* Find the number of ACEs. */
     select count(*) into ace_count from sys.xs$ace where acl# > max_seeded_id;
     
     /* Find the number of users. */
     select count(*) into user_count from sys.xs$prin p where p.type = 0 and prin# > max_seeded_id;
     
     /* Find the number of roles. */
     select count(*) into role_count from sys.xs$prin p where p.type <> 0 and prin# > max_seeded_id;

     /* Find number of security classes. */
     select count(*) into sc_count from sys.xs$seccls where sc# > max_seeded_id;

     /* Find number of privileges. */
     select count(*) into privilege_count from sys.xs$priv where priv# > max_seeded_id; 

     /* Find the number of sessions. */
     select count(*) into session_count from sys.rxs$sessions;
     
     /* Find the number of session created with external user. */
     select count(*) into external_session_count from sys.rxs$sessions r 
            where BITAND(r.flag,4) = 4;
     
     /* Find the number of session created with regular XS user. */
     regular_session_count := session_count - external_session_count;
    
     /* Find if dispatcher is being used. */
     select count(*) into row_count1 from sys.dba_xs_role_grants where granted_role = 'XSSESSIONADMIN';
     select count(*) into row_count2 from sys.dba_role_privs where granted_role = 'XS_SESSION_ADMIN' and grantee <> 'SYS';
     if ((row_count1 > 0) OR (row_count2 > 0)) then
       dispatcher_used := 'TRUE';
     else
       dispatcher_used := 'FALSE';
     end if;

     /* Find if midtier cache is used. */
     select count(*) into row_count1 from sys.dba_xs_role_grants where granted_role = 'XSCACHEADMIN';
     select count(*) into row_count2 from sys.dba_role_privs where granted_role = 'XS_CACHE_ADMIN' and grantee <> 'SYS';
     if ((row_count2 > 0) OR (row_count2 > 0)) then
       midtier_cache_used := 'TRUE';
     else
       midtier_cache_used := 'FALSE';
     end if;

     feature_info := to_clob('Number of policies: '||policy_count||
                            ' Number of policies applied: '||applied_policy_count||
                ' Number of ACLs created: '||acl_count||
                ' Number of ACEs: '||ace_count||
                ' Number of users created: '||user_count||
                ' Number of roles created: '||role_count||
                ' Number of security classes created: '||sc_count||
                ' Number of privileges created: '||privilege_count||
                ' Number of sessions created: '||session_count||
                ' Number of external sessions created: '||external_session_count||
                ' Number of regular sessions created: '||regular_session_count||
                ' Dispatcher used: '||dispatcher_used||
                ' Mid-tier cache used: '||midtier_cache_used);
   end if;
 exception
   when others then
     null;
 end;
END DBMS_FEATURE_RAS;
/
show errors;

/****************************************************************
 * DBMS_FEATURE_ONLINE_REDEF
 * The procedure to detect usage for Online Redefinition
 ****************************************************************/

CREATE OR REPLACE PROCEDURE DBMS_FEATURE_ONLINE_REDEF
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
 num_usage         number;                  
 num_redef         number;                              -- total number of redefinition
 num_finish      number;                       -- total number of finished redefinition
 num_abort      number;                         -- total number of aborted redefinition
 num_pk       number;                          -- total number of PK-based redefinition
 num_rowid        number;                   -- total number of rowid-based redefinition
 num_part      number;                  -- total number of partition-based redefinition
 num_batch      number;                           -- total number of batch redefinition
 num_auto_vpd      number;                -- total number of auto copy VPD redefinition
 num_manual_vpd        number;          -- total number of manual copy VPD redefinition
 last_refresh_date     date;                                  -- last redefinition date
 feature_usage  varchar2(1000);

BEGIN
  -- initialize
  num_usage  := 0;      
  num_redef  := 0;                                   
  num_finish := 0;
  num_abort := 0;
  num_pk := 0;
  num_rowid := 0;
  num_part  := 0;
  num_batch := 0;
  num_auto_vpd := 0;
  num_manual_vpd  := 0;
  num_pk := 0;
  
  feature_boolean := 0;
  aux_count := 0;

  /* get total number of redefinition */
  execute immediate 'select count(*) from sys.redef$'
  into num_usage;

  if (num_usage > 0)
  then
   /* get number of finished redefinition */
    execute immediate 'select redef# from sys.redef_track$'
    into num_redef;

    /* get number of finished redefinition */
    execute immediate 'select finish_redef# from sys.redef_track$'
    into num_finish;

    /* get number of aborted redefinition */
    execute immediate 'select abort_redef# from sys.redef_track$'
    into num_abort;

    /* get number of PK-based redefinition */
    execute immediate 'select pk_redef# from sys.redef_track$'
    into num_pk;

    /* get number of rowid-based redefinition */
    execute immediate 'select rowid_redef#  from sys.redef_track$'
    into num_rowid;

    /* get number of partition redefinition */
    execute immediate 'select part_redef# from sys.redef_track$'
    into num_part;

    /* get number of batch redefinition */
    execute immediate 'select batch_redef# from sys.redef_track$'
    into num_batch;

    /* get number of auto copy VPD  redefinition */
    execute immediate 'select vpd_auto# from sys.redef_track$'
    into num_auto_vpd;

    /* get number of manual copy VPD redefinition */
    execute immediate 'select vpd_manual# from sys.redef_track$'
    into num_manual_vpd;

    /* get last refresh date */
    execute immediate 'select last_redef_time from sys.redef_track$'
    into last_refresh_date;

    feature_boolean := 1;

    feature_usage := 'total number of redefinition:' || to_char(num_redef) ||
          ',' || ' num of finished redefinition:' || to_char(num_finish) ||
          ',' || ' num of abort redefinition:' || to_char(num_abort) ||
          ',' || ' num of PK-based redefinition:' || to_char(num_pk) ||
          ',' || ' num of rowid-based redefinition:' || to_char(num_rowid) ||
          ',' || ' num of partition-based redefinition:' || to_char(num_part) ||
          ',' || ' num of batch redefinition:' || to_char(num_batch) ||
          ',' || ' num of automatic copy VPD redefinition:' || to_char(num_auto_vpd) ||
          ',' || ' num of manual copy VPD redefinition:' || to_char(num_manual_vpd) ||
          ',' || ' last redefiniton date:' ||  to_char(last_refresh_date, 'Month DD, YYYY') ||
          '.';

    feature_info := to_clob(feature_usage);
  else
    feature_info := to_clob('Online Redefinition usage not detected!');
  end if;
  exception
    when others then
      null;
end DBMS_FEATURE_ONLINE_REDEF;
/
show errors;

  -- ******************************************************** 
  --   TEST_PROC_1
  -- ******************************************************** 

create or replace procedure DBMS_FEATURE_TEST_PROC_1
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
begin
     
    /* doesn't matter what I do here as long as the values get 
     * returned correctly 
     */
    feature_boolean := 0;
    aux_count := 12;
    feature_info := NULL;
    
end;
/

  -- ******************************************************** 
  --   TEST_PROC_2
  -- ******************************************************** 

create or replace procedure DBMS_FEATURE_TEST_PROC_2
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
begin
     
    /* doesn't matter what I do here as long as the values get 
     * returned correctly 
     */
    feature_boolean := 1;
    aux_count := 33;
    feature_info := 'Extra Feature Information for TEST_PROC_2';    
end;
/


-- ******************************************************** 
--   TEST_PROC_3
-- ******************************************************** 

create or replace procedure DBMS_FEATURE_TEST_PROC_3
  ( current_value  OUT  NUMBER) 
AS
begin
     
    /* doesn't matter what I do here as long as the values get 
     * returned correctly.
     */
    current_value := 101;    
end;
/

  -- ******************************************************** 
  --   TEST_PROC_4
  -- ******************************************************** 

create or replace procedure DBMS_FEATURE_TEST_PROC_4
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
begin
    
    /* raise an application error to make sure the error is being
     * handled correctly 
     */     
    raise_application_error(-20020, 'Error for Test Proc 4 ');
    
end;
/

  -- ******************************************************** 
  --   TEST_PROC_5
  -- ******************************************************** 

create or replace procedure DBMS_FEATURE_TEST_PROC_5
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
begin
    
    /* What happens if values are not set? */     
    feature_info := 'TEST PROC 5';
    
end;
/

/*******************************************************************
 * DBMS_FEATURE_AUDIT_OPTIONS
 *  The procedure to detect usage for Oracle Database Standard audit
 *******************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_audit_options
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   uniaud_linkon        NUMBER;
   system_audit_options NUMBER;
   object_audit_options NUMBER;
   audit_trail          VARCHAR2(100);
   feature_usage        VARCHAR2(1000);
BEGIN

  -- Initialize
  feature_boolean       := 0;
  aux_count             := 0;
  feature_info          := NULL;
  system_audit_options  := 0;
  object_audit_options  := 0;

  -- Check if 'uniaud_on' is linked
  select count(*) into uniaud_linkon from v$option
    where parameter like '%Unified Auditing%' and value = 'TRUE';

  -- Get the value of 'audit_trail' parameter
  select UPPER(value) into audit_trail from v$parameter 
    where UPPER(name) = 'AUDIT_TRAIL';

  -- If Unified auditing is ON, then Audit options(OLD) are always disabled
  if ((uniaud_linkon = 0) AND (audit_trail != 'NONE')) then
    feature_boolean := 1;
  end if;

  select count(*) into system_audit_options from audit$;
  select count(*) into object_audit_options from dba_obj_audit_opts;

  feature_usage := 'AUDIT_TRAIL=' || audit_trail || '; ' ||
   'Number of system audit options=' || to_char(system_audit_options) || '; '||
   'Number of object audit options=' || to_char(object_audit_options);
  feature_info := to_clob(feature_usage);

END dbms_feature_audit_options;
/
show errors;

/****************************************************************************
 * DBMS_FEATURE_FGA_AUDIT
 *  The procedure to detect usage for Oracle Database Fine Grained Audit(FGA)
 ****************************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_fga_audit
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage           VARCHAR2(1000);
   fga_policies_total      NUMBER;
   fga_policies_enabled    NUMBER;
   fga_policies_column     NUMBER;
   fga_policies_condition  NUMBER;
   fga_policies_handler    NUMBER;
   fga_policies_DB_trail   NUMBER;
   fga_policies_XML_trail  NUMBER;
BEGIN

  -- Initialize
  feature_boolean         := 0;
  aux_count               := 0;
  feature_info            := NULL;
  fga_policies_total      := 0;
  fga_policies_enabled    := 0;
  fga_policies_column     := 0;
  fga_policies_condition  := 0;
  fga_policies_handler    := 0;
  fga_policies_DB_trail   := 0;
  fga_policies_XML_trail  := 0;

  -- Get FGA policy details from the database
  FOR item IN (SELECT enabled, policy_text, policy_column, pf_function,
                      audit_trail FROM DBA_AUDIT_POLICIES)
  LOOP
    IF (item.enabled = 'YES') THEN
      fga_policies_enabled := fga_policies_enabled + 1;
    END IF;

    IF (item.policy_text IS NOT NULL) THEN
      fga_policies_condition := fga_policies_condition + 1;
    END IF;

    IF (item.policy_column IS NOT NULL) THEN
      fga_policies_column := fga_policies_column + 1;
    END IF;

    IF (item.pf_function IS NOT NULL) THEN
      fga_policies_handler := fga_policies_handler + 1;
    END IF;

    IF (item.audit_trail LIKE 'DB%') THEN
      fga_policies_DB_trail := fga_policies_DB_trail + 1;
    ELSE 
      fga_policies_XML_trail := fga_policies_XML_trail + 1;
    END IF;
    
    fga_policies_total := fga_policies_total + 1;
  END LOOP;

  -- If atleast a single FGA policy is enabled, then FGA feature is enabled
  if (fga_policies_enabled > 0) then
    feature_boolean := 1;
  end if;

  feature_usage := 'Number of FGA policies=' ||
                       to_char(fga_policies_total) || '; ' ||
                   'Number of Enabled FGA policies=' || 
                       to_char(fga_policies_enabled) || '; ' ||
                   'Number of FGA policies with audit_condition=' || 
                       to_char(fga_policies_condition) || '; ' ||
                   'Number of FGA policies with column-level audit=' ||
                       to_char(fga_policies_column) || '; ' ||
                   'Number of FGA policies with handler=' ||
                       to_char(fga_policies_handler) || '; ' ||
                   'Number of FGA policies with DB audit_trail=' ||
                       to_char(fga_policies_DB_trail) || '; ' ||
                   'Number of FGA policies with XML audit_trail=' ||
                       to_char(fga_policies_XML_trail);

  feature_info := to_clob(feature_usage);

END dbms_feature_fga_audit;
/
show errors;

/*******************************************************************
 * DBMS_FEATURE_UNIFIED_AUDIT
 *  The procedure to detect usage for Oracle Database Unified Audit
 *******************************************************************/
CREATE OR REPLACE PROCEDURE dbms_feature_unified_audit
     ( feature_boolean  OUT  NUMBER,
       aux_count        OUT  NUMBER,
       feature_info     OUT  CLOB)
AS
   feature_usage         VARCHAR2(1000);
   uniaud_linkon         NUMBER;
   unified_policies      NUMBER;
   unified_policies_enb  NUMBER;
   unified_policies_cond NUMBER;
   unified_policies_dv   NUMBER;
   unified_policies_ols  NUMBER;
   unified_policies_xs   NUMBER;
   unified_policies_dp   NUMBER;
   unified_contexts      NUMBER;
BEGIN

  -- Initialize
  feature_boolean       := 0;
  aux_count             := 0;
  feature_info          := NULL;
  unified_policies      := 0;
  unified_policies_enb  := 0;
  unified_policies_cond := 0;
  unified_policies_dv   := 0;
  unified_policies_ols  := 0;
  unified_policies_xs   := 0;
  unified_policies_dp   := 0;
  unified_contexts      := 0;

  -- Check if 'uniaud_on' is linked
  select count(*) into uniaud_linkon from v$option
    where parameter like '%Unified Auditing%' and value = 'TRUE';

  -- Get number of Unified Audit policies created in the database
  select count(*) into unified_policies from aud_policy$;

  -- Get number of Unified Audit policies enabled in the database
  select count(distinct policy#) into unified_policies_enb from audit_ng$;

  -- Get number of Unified Audit policies with condition 
  select count(*) into unified_policies_cond from aud_policy$ 
    where condition is NOT NULL;

  -- Get number of Unified Audit policies for each componenet
  FOR item IN (SELECT audit_option_type, count(distinct policy_name) pol_cnt
               FROM AUDIT_UNIFIED_POLICIES group by audit_option_type)
  LOOP
    IF (item.audit_option_type LIKE 'DV%') THEN
      unified_policies_dv := item.pol_cnt;
    ELSIF (item.audit_option_type LIKE 'OLS%') THEN
      unified_policies_ols := item.pol_cnt;
    ELSIF (item.audit_option_type LIKE 'XS%') THEN
      unified_policies_xs := item.pol_cnt;
    ELSIF (item.audit_option_type LIKE 'DATAPUMP%') THEN
      unified_policies_dp := item.pol_cnt;
    END IF;
  END LOOP;

  -- Get number of contexts enabled for audit
  select count(*) into unified_contexts from aud_context$;

  -- If 'uniaud_on' is linked, then Unified audit feature is enabled. 
  -- Else if atleast a single Unified audit policy is enabled,
  --   then Unified audit feature is enabled.
  if ((uniaud_linkon > 0) OR (unified_policies_enb > 0)) then
    feature_boolean := 1;
  end if;

  feature_usage := 'Number of Unified Audit policies=' ||
                   to_char(unified_policies) || '; ' ||
                   'Number of Enabled Unified Audit policies=' || 
                   to_char(unified_policies_enb) || '; ' ||
                   'Number of Unified Audit policies with condition=' || 
                   to_char(unified_policies_cond) || '; ' ||
                   'Number of Unified Audit policies on DV=' || 
                   to_char(unified_policies_dv) || '; ' ||
                   'Number of Unified Audit policies on OLS=' || 
                   to_char(unified_policies_ols) || '; ' ||
                   'Number of Unified Audit policies on XS=' || 
                   to_char(unified_policies_xs) || '; ' ||
                   'Number of Unified Audit policies on DATAPUMP=' || 
                   to_char(unified_policies_dp) || '; ' ||
                   'Number of Enabled Unified Audit Contexts=' || 
                   to_char(unified_contexts);
  feature_info := to_clob(feature_usage);

END dbms_feature_unified_audit;
/
show errors;

/*******************************************************************
 * DBMS_PDB_NUM
 *  The procedure to check if Oracle Multitenant is used and count
 *  user-created pluggable databases
 *******************************************************************/

CREATE OR REPLACE PROCEDURE dbms_pdb_num
     ( feature_boolean  OUT NUMBER,
       aux_count        OUT NUMBER,
       feature_info     OUT CLOB)
AS
begin
  feature_boolean := 0;
  aux_count := 0;
  feature_info := NULL;

  select count(*) into feature_boolean from v$database where cdb = 'YES';
  if (feature_boolean = 1) then
    select count(*) into aux_count from v$pdbs where con_id > 2;
  end if;

end dbms_pdb_num;
/
show errors;


/*******************************************************************
 * DBMS_FEATURE_LABEL_SECURITY
 *  The procedure to detect usage for Oracle Label Security
 *******************************************************************/

CREATE OR REPLACE PROCEDURE dbms_feature_label_security
     ( feature_boolean  OUT NUMBER,
       aux_count        OUT NUMBER,
       feature_info     OUT CLOB)
AS
begin
  -- execute immediate as lbacsys is not installed yet
  execute immediate 'begin lbacsys.feature_usage(:1,:2,:3); end;'
                     USING OUT feature_boolean, 
                           OUT aux_count,
                           OUT feature_info;

  EXCEPTION WHEN OTHERS THEN
    null;
 
end dbms_feature_label_security;
/
show errors;

/*******************************************************************
 * DBMS_FEATURE_PRIV_CAPTURE
 *  The procedure to detect usage for privilege capture feature
 *******************************************************************/

