Data Cartridge Developers Guide
Data Cartridge Developers Guide
23ai
F47575-04
May 2024
Oracle Database Data Cartridge Developer's Guide, 23ai
F47575-04
Contributing Authors: Eric Belden, Timothy Chorma, Dinesh Das, Janis Greenberg, Ying Hu, Susan
Kotsovolos, Geoff Lee, Roza Leyderman, Susan Mavris, Valarie Moore, Magdi Morsi, Chuck Murray, Den
Raphaely, Helen Slattery, Seema Sundara
This software and related documentation are provided under a license agreement containing restrictions on
use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your
license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license,
transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means. Reverse
engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is
prohibited.
The information contained herein is subject to change without notice and is not warranted to be error-free. If
you find any errors, please report them to us in writing.
If this is software, software documentation, data (as defined in the Federal Acquisition Regulation), or related
documentation that is delivered to the U.S. Government or anyone licensing it on behalf of the U.S.
Government, then the following notice is applicable:
U.S. GOVERNMENT END USERS: Oracle programs (including any operating system, integrated software,
any programs embedded, installed, or activated on delivered hardware, and modifications of such programs)
and Oracle computer documentation or other Oracle data delivered to or accessed by U.S. Government end
users are "commercial computer software," "commercial computer software documentation," or "limited rights
data" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental
regulations. As such, the use, reproduction, duplication, release, display, disclosure, modification, preparation
of derivative works, and/or adaptation of i) Oracle programs (including any operating system, integrated
software, any programs embedded, installed, or activated on delivered hardware, and modifications of such
programs), ii) Oracle computer documentation and/or iii) other Oracle data, is subject to the rights and
limitations specified in the license contained in the applicable contract. The terms governing the U.S.
Government's use of Oracle cloud services are defined by the applicable contract for such services. No other
rights are granted to the U.S. Government.
This software or hardware is developed for general use in a variety of information management applications.
It is not developed or intended for use in any inherently dangerous applications, including applications that
may create a risk of personal injury. If you use this software or hardware in dangerous applications, then you
shall be responsible to take all appropriate fail-safe, backup, redundancy, and other measures to ensure its
safe use. Oracle Corporation and its affiliates disclaim any liability for any damages caused by use of this
software or hardware in dangerous applications.
Oracle®, Java, MySQL and NetSuite are registered trademarks of Oracle and/or its affiliates. Other names
may be trademarks of their respective owners.
Intel and Intel Inside are trademarks or registered trademarks of Intel Corporation. All SPARC trademarks are
used under license and are trademarks or registered trademarks of SPARC International, Inc. AMD, Epyc,
and the AMD logo are trademarks or registered trademarks of Advanced Micro Devices. UNIX is a registered
trademark of The Open Group.
This software or hardware and documentation may provide access to or information about content, products,
and services from third parties. Oracle Corporation and its affiliates are not responsible for and expressly
disclaim all warranties of any kind with respect to third-party content, products, and services unless otherwise
set forth in an applicable agreement between you and Oracle. Oracle Corporation and its affiliates will not be
responsible for any loss, costs, or damages incurred due to your access to or use of third-party content,
products, or services, except as set forth in an applicable agreement between you and Oracle.
Contents
Preface
Audience xxiii
Documentation Accessibility xxiii
Conventions xxiii
Part I Introduction
iii
2.4 Data Cartridge Deployment 2-5
2.4.1 Data Cartridge Naming Conventions 2-6
2.4.1.1 Need for Naming Conventions in Data Cartridges 2-6
2.4.1.2 Unique Name Format 2-7
2.4.1.3 Data Cartridge Naming Conventions 2-7
2.4.2 Cartridge Registration 2-8
2.4.3 Cartridge Directory Structure and Standards 2-8
2.4.4 Cartridge Upgrades 2-8
2.4.5 Import and Export of Cartridge Objects 2-9
2.4.6 Cartridge Versioning 2-9
2.4.7 Cartridge Internationalization 2-9
2.4.8 Cartridge Administration 2-9
2.4.9 Data Cartridge Development Approach 2-10
2.4.9.1 Creating a Data Cartridge Plan 2-10
2.4.9.2 Developing Data Cartridges 2-11
iv
4.1.2.1 General Syntax for Invoking Methods 4-3
4.1.2.2 SQL Syntax for Invoking Methods 4-3
4.1.2.3 PL/SQL Syntax for Invoking Methods 4-3
4.1.2.4 Using the SELF Build-In Parameter 4-4
4.1.3 Referencing Attributes in a Method 4-4
4.1.3.1 Setting Variable Values 4-4
4.2 Debugging PL/SQL Code 4-4
4.2.1 Notes for C and C++ Developers of Data Cartridges 4-5
4.2.2 Common Potential Errors 4-5
4.2.2.1 Signature Mismatches 4-6
4.2.2.2 RPC Time Out 4-6
4.2.2.3 Package Corruption 4-6
v
5.8 Guidelines for Using External Procedures with Data Cartridges 5-12
5.9 Java Methods 5-13
vi
7.2 Extensible Indexing Framework 7-6
7.3 Using the Text Indextype 7-7
7.3.1 Defining the Indextype 7-7
7.3.1.1 Non-Index-Based Functional Implementations 7-7
7.3.1.2 Index-Based Functional Implementations 7-8
7.3.2 Using the Indextype 7-8
7.3.2.1 Declaring a New Table 7-8
7.3.2.2 Building a Text Domain Index for the Table 7-9
7.3.2.3 Querying a Table Using a Contains() Operator 7-9
vii
8.4.1.4 Rebuilding a Domain Index 8-11
8.4.1.5 Truncating a Domain Index 8-11
8.4.1.6 Dropping a Domain Index 8-11
8.4.2 Domain Indexes on Index-Organized Tables 8-11
8.4.2.1 About Rowid Storage in a UROWID Column 8-12
8.4.2.2 Determining the Size of a UROWID Column 8-12
8.4.2.3 DML on Index Storage Tables 8-12
8.4.2.4 Start, Fetch, and Close Operations on Index Storage Tables 8-13
8.4.2.5 Indexes on Non-Unique Columns 8-13
8.4.3 Domain Index Metadata 8-13
8.4.4 Moving Domain Indexes Using Export/Import 8-13
8.4.5 Moving Domain Indexes Using Transportable Tablespaces 8-14
8.4.6 Domain Index Views 8-14
8.5 Object Dependencies, Drop Semantics, and Validation 8-15
8.5.1 Object Dependencies 8-15
8.5.2 Object Drop Semantics 8-16
8.5.3 Object Validation 8-16
8.6 Indextype, Domain Index, and Operator Privileges 8-16
8.7 Partitioned Domain Indexes 8-17
8.7.1 Using Local Domain Index Methods 8-17
8.7.2 About Partitioned Indexes 8-18
8.7.3 Creating a Local Domain Index 8-18
8.7.4 Dropping a Local Domain Index 8-18
8.7.5 Altering a Local Domain Index 8-19
8.7.6 Summary of Index States 8-19
8.7.7 DML Operations with Local Domain Indexes 8-20
8.7.8 Table Operations that Affect Indexes 8-20
8.7.9 ODCIIndex Interfaces for Partitioning Domain Indexes 8-21
8.7.10 Using SQL*Loader for Domain Indexes 8-21
8.8 Using System Partitioning 8-21
8.8.1 Advantages of System Partitioned Tables 8-21
8.8.2 Implementing System Partitioning 8-22
8.8.2.1 Creating a System-Partitioned Table 8-22
8.8.2.2 Inserting Data into a System-Partitioned Table 8-22
8.8.2.3 Deleting and Updating Data in a System-Partitioned Table 8-23
8.8.3 Supporting Operations with System-Partitioned Tables 8-23
8.8.4 Running Partition Maintenance Operations 8-24
8.8.5 Altering Table Exchange Partitions with Indexes 8-24
8.9 Using System-Managed Domain Indexes 8-25
8.10 Designing System-Managed Domain Indexes 8-26
8.10.1 Methods for Non-Partitioned Domain Indexes 8-27
viii
8.10.2 Methods for Local System-Managed Domain Indexes 8-28
8.11 Creating Local Domain Indexes 8-30
8.12 Maintaining Local Domain Indexes with INSERT, DELETE, and UPDATE 8-31
8.13 Querying Local Domain Indexes 8-31
8.14 System Managed Domain Index - Supported Schemes 8-31
8.15 Restrictions of System-Managed Domain Indexing 8-32
8.16 Migrating Non-Partitioned Indexes 8-32
8.17 Migrating Local Partitioned Indexes 8-32
9 Defining Operators
9.1 User-Defined Operators 9-1
9.1.1 Operator Bindings 9-1
9.1.2 Operator Privileges 9-2
9.1.3 Creating Operators 9-2
9.1.4 Dropping Operators 9-2
9.1.5 Altering Operators 9-3
9.1.5.1 Necessary Privileges for ALTER OPERATOR 9-3
9.1.5.2 Restrictions of ALTER OPERATOR 9-3
9.1.6 Commenting Operators 9-3
9.1.7 About Invoking Operators 9-4
9.1.7.1 Creating Contains() Operator 9-4
9.1.7.2 Using Contains() Operator in a Query 9-4
9.1.7.3 Using Contains() Operator Incorrectly 9-5
9.2 Operators and Indextypes 9-5
9.2.1 Operators in the WHERE Clause 9-5
9.2.1.1 Using Operator Predicates 9-5
9.2.1.2 Resolving Query Results with the Contains() Operator 9-6
9.2.1.3 Setting Up an Index Scan 9-6
9.2.1.4 Execution Model for Index Scan Methods 9-7
9.2.1.5 Filtering Multiple Table Queries with Contains() Operator 9-7
9.2.1.6 Invoking Indextrype Routines for the Contains() Operator 9-7
9.2.2 Using Operators Outside the WHERE Clause 9-8
9.2.2.1 Creating Index-based Functional Implementations 9-8
9.2.2.2 Implementing the Contains() Operator in Index-Based Functions 9-9
9.2.2.3 Binding the Contains() Operator to the Functional Implementation 9-9
9.2.2.4 Operator Resolution 9-9
9.2.2.5 Operator Execution 9-9
9.2.3 Operators that Return Ancillary Data 9-10
9.2.3.1 Operator Bindings that Compute Ancillary Data 9-11
9.2.3.2 Operator Bindings That Model Ancillary Data 9-11
ix
9.2.3.3 Operator Resolution 9-12
9.2.3.4 Operator Execution 9-12
x
10.4 Predicate Ordering 10-20
10.5 Dependency Model 10-20
10.6 Restrictions and Suggestions 10-21
10.6.1 Distributed Execution 10-21
10.6.2 System-Managed Storage Tables and ASSOCIATE STATISTICS 10-21
10.6.3 Aggregate Object-Level Statistics 10-21
10.6.4 System-Managed Domain Indexing 10-22
10.6.5 Collecting and Deleting User-Defined Statistics for System-Managed Indexes 10-22
10.6.5.1 Collecting statistics for a system-managed domain index 10-23
10.6.5.2 Deleting statistics for a system-managed domain index 10-23
10.6.5.3 Collecting statistics for all partitions of a local system-managed domain
index 10-23
10.6.5.4 Deleting statistics for all partitions of a local system-managed domain
index 10-24
10.6.5.5 Collecting statistics for partition P2 of a local system-managed domain
index 10-24
10.6.5.6 Deleting statistics for partition P2 of a local system-managed domain
index 10-24
10.6.5.7 Collecting statistics for all subpartitions of a composite partition of a
local system-managed domain index 10-25
10.6.5.8 Deleting statistics for all subpartitions of a composite partition of a local
system-managed domain index 10-25
10.6.5.9 Collecting statistics for a subpartition of a local system-managed
domain index 10-25
10.6.5.10 Deleting statistics for a subpartition of a local system-managed
domain index 10-25
10.6.6 Performance 10-26
xi
11.6.1 Input Processing and Support for Special Characters 11-6
11.6.2 Parameter Manager Behavior Flag 11-7
11.6.3 Key Registration 11-7
11.6.4 Parameter Storage and Retrieval 11-7
11.6.5 Parameter Manager Context 11-8
11.7 File I/O 11-8
11.8 String Formatting 11-8
xii
13.3.5 Combining PIPE ROW with AUTONOMOUS_TRANSACTION 13-8
13.3.6 Implementing the Interface Approach 13-8
13.3.6.1 Scan Context 13-9
13.3.6.2 Start Routine 13-9
13.3.6.3 Fetch Routine 13-10
13.3.6.4 Close Routine 13-10
13.3.6.5 Describing Returned Data Sructures; Describe Method 13-11
13.3.6.6 Preparing a Query for Execution; Prepare Method 13-13
13.3.7 Querying Table Functions 13-14
13.3.7.1 Implementing Multiple Calls to Table Functions 13-14
13.3.7.2 Using PL/SQL REF CURSOR Variables 13-14
13.3.8 Performing DML Operations Inside Table Functions 13-15
13.3.9 Performing DML Operations on Table Functions 13-15
13.3.10 Handling Exceptions in Table Functions 13-16
13.4 Parallel Table Functions 13-16
13.4.1 Inputting Data with Cursor Variables 13-16
13.4.1.1 Using Multiple REF CURSOR Input Variables 13-17
13.4.1.2 Explicitly Opening a REF CURSOR for a Query 13-17
13.4.1.3 PL/SQL REF CURSOR Arguments to Java and C/C++ Functions 13-17
13.4.2 Input Data Partitioning 13-19
13.4.3 Parallel Execution of Leaf-Level Table Functions 13-20
13.5 Input Data Streaming for Table Functions 13-21
13.5.1 Setting up the Input Stream 13-22
13.5.2 Parallel Execution: Partitioning and Clustering 13-22
13.6 Creating Domain Indexes in Parallel 13-23
13.6.1 Loading Domain Indexes 13-24
13.7 Transient and Generic Types 13-24
xiii
14.6 Designing Operators 14-4
14.7 Designing for the Extensible Optimizer 14-4
14.7.1 Weighing Cost and Selectivity 14-5
14.7.2 Cost for functions 14-5
14.7.2.1 Selectivity for Functions 14-5
14.7.2.2 Statistics for Tables 14-5
14.7.2.3 Statistics for Indexes 14-5
14.8 Designing for Maintenance 14-6
14.9 Enabling Cartridge Installation 14-6
14.10 Designing for Portability 14-6
xiv
15.5 Defining Types and Methods for Extensible Optimizing 15-32
15.5.1 Creating the Statistics Table, PowerCartUserStats 15-33
15.5.2 Creating the Extensible Optimizer Methods 15-33
15.5.2.1 Creating the Type Definition 15-34
15.5.2.2 ODCIGetInterfaces() 15-36
15.5.2.3 ODCIStatsCollect() Method for PowerDemand_Typ Columns 15-36
15.5.2.4 ODCIStatsDelete() Method for PowerDemand_Typ Columns 15-38
15.5.2.5 ODCIStatsCollect() Method for power_idxtype Domain Indexes 15-39
15.5.2.6 ODCIStatsDelete() Method for power_idxtype Domain Indexes 15-40
15.5.2.7 ODCIStatsSelectivity() Method for Specific Queries 15-40
15.5.2.8 ODCIStatsIndexCost() Method for Specific Queries 15-46
15.5.2.9 ODCIStatsIndexCost() Method for Any Queries 15-47
15.5.2.10 ODCIStatsFunctionCost() Method 15-48
15.5.3 Associating the Extensible Optimizer Methods with Database Objects 15-49
15.5.4 Analyzing the Database Objects 15-50
15.6 Testing the Domain Index 15-50
15.6.1 Creating and Populating the Power Demand Table 15-51
15.6.2 Querying Without the Index 15-53
15.6.3 Creating the Index 15-54
15.6.4 Querying with the Index 15-54
xv
16.4.4 Registering the C Implementation of the ODCIIndexXXX() Methods 16-9
16.4.4.1 Registering the Implementation of ODCIIndexInsert() 16-9
16.4.4.2 Registering the Implementation of ODCIIndexDelete() 16-10
16.4.4.3 Registering the Implementation of ODCIIndexUpdate() 16-10
16.4.4.4 Registering the Implementation of ODCIIndexStart() 16-11
16.4.4.5 Registering the Implementation of ODCIIndexFetch() 16-11
16.4.4.6 Registering the Implementation of ODCIIndexClose() 16-12
16.4.5 Defining Additional Structures in C Implementation 16-12
16.4.6 Defining C Methods in the Implementation Body 16-13
16.4.6.1 Implementing a Common Error Processing Routine in C 16-13
16.4.6.2 Implementing ODCIIndexInsert() for PSBTREE in C 16-14
16.4.6.3 Implementing ODCIIndexDelete() for PSBTREE in C 16-16
16.4.6.4 Implementing ODCIIndexUpdate() for PSBTree in C 16-18
16.4.6.5 Implementing ODCIIndexStart() for PSBTREE in C 16-20
16.4.6.6 Implementing ODCIIndexFetch() for PSBTREE in C 16-24
16.4.6.7 Implementing ODCIIndexClose() for PSBTREE in C 16-26
16.4.7 Implementing the Indextype for PSBTREE 16-27
16.5 Using PSBTREE 16-27
16.5.1 Creating and Populating a Partitioned Table for PSBTREE 16-28
16.5.2 Creating a PSBTREE Index on a Column 16-28
16.5.3 Using PSBTREE Operators in a Query 16-28
Part IV Reference
xvi
18.3 Cartridge Services-Maintaining Context 18-3
18.3.1 ContextManager 18-3
18.3.2 CountException() 18-4
18.3.3 CountException(String) 18-4
18.3.4 InvalidKeyException() 18-4
18.3.5 InvalidKeyException(String) 18-4
xvii
19.2.19 ODCITabStats 19-14
19.2.20 ODCIBFileList 19-15
19.2.21 ODCITabFuncInfo 19-15
19.2.22 ODCIDateList 19-15
19.2.23 ODCINumberList 19-15
19.2.24 ODCIRawList 19-15
19.2.25 ODCIVarchar2List 19-16
19.2.26 ODCIFuncCallInfo 19-16
19.3 Mappings of Constants and Types 19-16
19.3.1 Mappings in PL/SQL 19-16
19.3.2 Mappings in C 19-16
xviii
21.2.3 ODCIStatsDelete() 21-7
21.2.4 ODCIStatsFunctionCost() 21-8
21.2.5 ODCIStatsExchangePartition() 21-9
21.2.6 ODCIStatsIndexCost() 21-10
21.2.7 ODCIStatsSelectivity() 21-12
21.2.8 ODCIStatsTableFunction() 21-14
21.2.9 ODCIStatsUpdPartStatistics() 21-14
Index
xix
List of Figures
1-1 Oracle Services 1-4
1-2 External Programs Executing in a Separate Address Space 1-7
2-1 Cartridge Development Process 2-3
5-1 Calling an External Procedure 5-5
7-1 B-tree Index Structure 7-3
7-2 Hash Index Structure 7-4
7-3 2-d Index Structure 7-4
7-4 Point Quadtree Index Structure 7-5
8-1 Three-Partition Table with a Local Domain Index, and Associated Structures 8-25
8-2 A Three-Partition Table after ALTER TABLE SPLIT PARTITION 8-26
10-1 Storing Index-Specific Statistics with Index Tables 10-12
10-2 Storing Index-Specific Statistics in a Separate Table 10-13
10-3 Storing Index-Partition Statistics in a Common Table 10-13
12-1 Sequence of Calls for Parallel Evaluation of User-Defined Aggregates 12-5
13-1 Typical Data Processing with Non-Parallel, Non-Pipelined Table Functions 13-2
13-2 Data Processing Using Pipelining and Parallel Execution 13-2
13-3 Flowchart of Table Function Row Source Execution 13-11
15-1 Region Served by the Power Utility 15-2
15-2 Regional Grid Cells in Numbered Sequence 15-3
15-3 Grayscale Representation of Satellite Image 15-4
15-4 Grayscale Representation of Weather Conditions at Second Recording 15-5
15-5 Grayscale Representation of Conditions as Projected 15-5
15-6 Distribution of Power Stations Across the Region 15-6
15-7 Areas Served by Three Power Stations 15-7
15-8 Application Object Model of the Power Demand Cartridge 15-8
15-9 Implementation Model of the Power Demand Cartridge 15-9
xx
List of Tables
1-1 Data Cartridge Domains; Content and Scope 1-3
2-1 Data Cartridge Naming Conventions 2-8
5-1 Parameter Datatype Mappings 5-7
5-2 External Data Type Mappings 5-8
6-1 Summary of OCI Functions for Manipulating LOBs 6-5
6-2 OCI and PL/SQL (DBMS_LOB) Interfaces Compared 6-7
6-3 Summary of DBMS_LOB Package Routines 6-10
8-1 Views *_INDEXTYPE_COMMENTS 8-9
8-2 Views *_SECONDARY_OBJECTS 8-15
8-3 Default and Explicit Drop Options for Operators and Index Types 8-16
8-4 Summary of Index States 8-19
8-5 Summary of Table Operations 8-20
8-6 Summary of ALTER TABLE Operations with Partition Maintenance 8-20
8-7 ODCIXXX() Methods for Non-Partitioned Domain Indexes 8-27
8-8 ODCIXXX() Methods for Local System-Managed Domain Indexes 8-28
10-1 Statistics Methods and Default Statistics for Various Schema Objects 10-6
10-2 Effects of DDL on Partition and Global Statistics 10-19
10-3 Dependency Model for DDLs 10-20
11-1 Special Characters 11-6
13-1 Generic SQL Types 13-25
15-1 Sample Power Demand Readings for an Hour 15-3
15-2 Sample Power Demand Readings for an Hour 15-10
15-3 Operators and Implementing Functions 15-15
15-4 Indextype Methods 15-18
15-5 Extensible Optimizer Methods 15-33
19-1 ODCIArgDesc.ArgType Values 19-2
19-2 ODCIEnv.CallProperty Values 19-2
19-3 ODCIIndexAlter Options 19-3
19-4 ODCIIndexInfo.Flags Bits 19-3
19-5 Description of the ODCIIPartInfo.PartOp System Defined Constant 19-4
19-6 ODCIIPredInfo.Flags Bits 19-4
19-7 ODCIFuncInfo.Flags Bits 19-5
19-8 ODCIQueryInfo.Flags Bits 19-5
19-9 ODCIStatsOptions.Flags Bits 19-6
19-10 ODCIStatsOptions.Options Bits 19-6
xxi
19-11 Return Status Values 19-6
19-12 ScnFlg Values; Function with Index Context 19-7
19-13 ODCIArgDesc Function and Operator Argument Description - Attributes 19-8
19-14 ODCIColInfo Column Related Information - Attributes 19-9
19-15 ODCICost Cost Information - Attributes 19-9
19-16 ODCIEnv Environment Variable Descriptor Information - Attributes 19-10
19-17 ODCIFuncInfo Function Information - Attributes 19-11
19-18 ODCIIndexInfo Index Related Information - Attributes 19-11
19-19 ODCIIndexCtx Index Context Related Information - Attributes 19-12
19-20 ODCIObject Index Context Related Information - Attributes 19-12
19-21 ODCIPartInfo Index-Related Information - Attributes 19-12
19-22 ODCIPredInfo Operator Related Information - Attributes 19-13
19-23 ODCIQueryInfo Index Context Related Information - Attributes 19-14
19-24 ODCIStatsOptions Cost Information - Attributes 19-14
19-25 ODCITabFuncStats Parameter 19-14
19-26 ODCITabStats - Attributes 19-15
19-27 ODCITabFuncInfo Parameters 19-15
19-28 ODCIFuncCallInfo - Attributes 19-16
20-1 Summary of System-Defined Extensible Indexing Interface Routines 20-1
21-1 Summary of User-Defined ODCIStats Functions 21-3
22-1 Summary of User-Defined Aggregate Functions 22-1
23-1 Summary of Pipelined and Parallel Table Functions for C 23-1
xxii
Preface
The Oracle Database Data Cartridge Developer's Guide describes how to build and use data
cartridges to create custom extensions to the Oracle server's indexing and optimizing
capabilities.
• Audience
• Documentation Accessibility
• Conventions
Audience
Oracle Database Data Cartridge Developer's Guide is intended for developers who want to
learn how to build and use data cartridges to customize the indexing and optimizing
functionality of the Oracle server to suit specialty data such as that associated with chemical,
genomic, or multimedia applications.
To use this document, you must be familiar with using Oracle and should have a background
in an Oracle-supported programming language such as PL/SQL, C, C++, or Java.
Documentation Accessibility
For information about Oracle's commitment to accessibility, visit the Oracle Accessibility
Program website at http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc.
Conventions
The following text conventions are used in this document:
Convention Meaning
boldface Boldface type indicates graphical user interface elements associated with an
action, or terms defined in text or the glossary.
italic Italic type indicates book titles, emphasis, or placeholder variables for which
you supply particular values.
monospace Monospace type indicates commands within a paragraph, URLs, code in
examples, text that appears on the screen, or text that you enter.
xxiii
Part I
Introduction
Oracle Database provides Data Cartridges to support customized application development in
the object mode.
• Introduction to Data Cartridges
• Roadmap to Building a Data Cartridge
1
Introduction to Data Cartridges
Oracle Database provides Data Cartridges to support customized application development in
the object mode.
In addition to efficient and secure management of data ordered under the relational model,
Oracle provides support for data organized under the object model. Object types and other
features such as large objects (LOBs), external procedures, extensible indexing, and query
optimization can be used to build powerful, reusable, server-based components called data
cartridges.
• Overview of Data Cartridges
• Uses of Data Cartridges
• Extending the Server: Services and Interfaces
1-1
Chapter 1
Uses of Data Cartridges
• Data cartridges are integrated with the server. The Oracle Extensibility Framework
defines a set of interfaces that integrate data cartridges with the components of
the server engine, allowing for domain-specific indexing, domain-specific
optimized access to the CPU resources, and domain-specific optimization of I/O
access to cartridge data.
• Data cartridges are packaged. A data cartridge is installed as a unit. After it is
installed, the data cartridge handles all access issues for each user, including
verification of schemas and privileges.
1-2
Chapter 1
Extending the Server: Services and Interfaces
You can also use scalar data types to construct more complex user-defined types. The
object-relational database management system provides foundational data cartridges that
package multimedia and complex data. These data cartridges can be used in developing
applications across many different industries:
• Oracle Text uses the tokenized serial byte stream database model are used to implement
display compress, reformat, and indexing behavior.
• Oracle Spatial is for use with geometric objects (points, lines, polygons); it implements
project, rotate, transform and map behavior.
Another way of viewing the relationship of cartridges to domains is to consider basic
multimedia data types as an extensible foundation that can be customized for specific
industries. For example, medical applications can customize the Oracle Text for records and
Oracle Spatial for demographic analysis.
A cartridge that provides basic services can be deployed across many industries. A cartridge
can also leverage domain expertise across an industry. These cartridges can be further
extended for more specialized vertical applications.
• Extensibility Services
• Extensibility Interfaces
See Also:
Power Demand Cartridge Example
1-3
Chapter 1
Extending the Server: Services and Interfaces
Indexing
Data
Processing
Query
Cartridge
Cartridge
Data
Data
Extensibility
Execution
Server
Universal
Universal
Server
DataServer
System
Type
Oracle8
Oracle
Data
• User-Defined Types
• Collection Types
• Reference Types
• Large Objects
1-4
Chapter 1
Extending the Server: Services and Interfaces
See Also:
A VARRAY type contains a variable number of ordered elements and can be used for a column
of a table or an attribute of an object type. The element type of a VARRAY can be either a
native data type, such as NUMBER, or a user-defined type.
To provide the semantics of an unordered collection, you could create a nested table using
Oracle SQL As with a VARRAY, a nested table can define a column of a table or an attribute of
a user-defined type.
1-5
Chapter 1
Extending the Server: Services and Interfaces
See Also:
For internal LOB types, both the locators and related data participate fully in the
transactional model of the Oracle server. The data for BFILEs does not participate in
transactions; however, BFILE locators are fully supported by Oracle server
transactions.
Unlike scalar quantities, a LOB value cannot be indexed by built-in indexing schemes.
However, you can use the various LOB APIs to build modules, including methods of
user-defined types, to access and manipulate LOB content. You can define the
semantics of data residing in LOBs and manipulate this data using the extensible
indexing framework.
See Also:
1-6
Chapter 1
Extending the Server: Services and Interfaces
extproc
SQL
C
Inter-Language
Method Service
JAVA
Oracle
Server
Database
Oracle
1-7
Chapter 1
Extending the Server: Services and Interfaces
1-8
Chapter 1
Extending the Server: Services and Interfaces
for a SQL statement. This information is used by the optimizer in choosing a query plan, thus
extending the optimizer to use the user-supplied information. The rule-based optimizer
remains unchanged.
An execution plan generated by the optimizer includes an access method for each table in
the FROM clause, and an ordering, called the join order, of the tables in the FROM clause.
System-defined access methods include indexes, hash clusters, and table scans. For each
table in the join order, the optimizer chooses a plan by generating a set of join orders or
permutations, computing the cost of each, and selecting the one with the lowest cost. The
cost of the join order is the sum of the access method and join method costs.
The cost model is a group of algorithms used for calculating the cost of a given operation. It
can include varying levels of detail about the physical environment in which the query runs.
The current cost model includes the number of disk accesses and estimates of network
costs, with minor adjustments.
The optimizer also uses statistics about the objects referenced in the query to calculate cost
and selectivity, or the fraction of rows in a table that a query selects (between 0 and 100, a
percentage). The DBMS_STATS package contains methods for generating these statistics.
Extensibility allows users to define new operators, index types, and domain indexes, and
enables the control of the three main components used by the optimizer to select an
execution plan: statistics, selectivity, and cost.
See Also:
Oracle Database PL/SQL Packages and Types Reference for information about
DBMS_STATS.
DBMS Interfaces
The DBMS interfaces offer the simplest kind of extensibility services. They can be used
through extensions to SQL or to the Oracle Call Interface (OCI). For example, the extensible
type manager uses the CREATE TYPE syntax in SQL. Similarly, extensible indexing uses DDL
and DML support for specifying and manipulating indexes.
1-9
Chapter 1
Extending the Server: Services and Interfaces
calls functions implemented by the data cartridge to compute the cost of user-defined
operators or functions.
1-10
2
Roadmap to Building a Data Cartridge
Consider the recommended process for developing data cartridges, including relationships
and dependencies among the steps of the process.
• Data Cartridge Development Process
• Installing and Using Data Cartridges
• Requirements and Guidelines for Data Cartridge Components
• Data Cartridge Deployment
2-1
Chapter 2
Data Cartridge Development Process
2-2
Chapter 2
Installing and Using Data Cartridges
Inventory domain
Yes Have
implementation
No
Inventory access
methods
Yes Multi-domain No
queries
Test
Develop installation
scripts and
documentation
2-3
Chapter 2
Requirements and Guidelines for Data Cartridge Components
where
• C$CARTRIDGE is the name of the cartridge where the error originated
• NNNN is the number of the error message, unique to that cartridge
• %s is the description of the cartridge-specific error
2-4
Chapter 2
Data Cartridge Deployment
Note:
Oracle Database Error Messages for information on writing and managing error
messages
2-5
Chapter 2
Data Cartridge Deployment
See Also:
2-6
Chapter 2
Data Cartridge Deployment
vendors, thus developed in isolation. Each data cartridge consists of various schema objects
inside the database, and other components visible at the operating system level, such as
external procedures in shared libraries. If multiple data cartridges tried to use the same
names for schema objects or operating system-level entities, the result would be incorrect
and inconsistent behavior.
Furthermore, because exception conditions during the run-time operation of data cartridges
can cause the Oracle server to return errors, it is important to prevent conflicts between error
or message codes of different data cartridges. These conflicts can arise if, for example, two
cartridges use the same error code for different error conditions. Having unique error and
message codes ensures that the origin of the exception condition can be readily identified.
Data cartridges and their components should have names of the following format:
C$pppptttm.ccccc
• C$ACMEVID1.mf_rewind
• C$ACMEVID1.vid_ops_package
• C$ACMEVID1.vid_stream_lib
Each organization is responsible for specific naming requirements after the C$pppp portion of
the object name. For example, Acme Cartridge Company must ensure that all of its cartridges
have unique names and that all components within a cartridge have unique names.
2-7
Chapter 2
Data Cartridge Deployment
Note:
The EXDSYS user must have special privileges necessary for running
cartridges. This user may be installed as part of cartridge installation, but a
better solution is to make the user part of the database installation by
including this process in the standard database creation script.
2-8
Chapter 2
Data Cartridge Deployment
External Versioning
External versioning is the easier of the two versioning problems. You must be able to track a
cartridge version number and act accordingly upon installation or configuration based on
versioning information.
Internal Versioning
Internal versioning is the harder problem. Ideally, you would like a mechanism to support
multiple versions of a cartridge in the database. This would provide backward compatibility
and also make for high availability.
See Also:
Oracle Database Globalization Support Guide
2-9
Chapter 2
Data Cartridge Deployment
Invoker’s Rights
Invoker's rights is a special privilege that allows the system to access database objects
to which it would not normally have access. The special user SYS has such rights.
Unless you grant privileges to public, the user you create to install and run your
cartridge needs this privilege.
1. Experiment with the examples in: Power Demand Cartridge Example , PSBTREE:
Extensible Indexing Example, and Pipelined Table Functions: Interface Approach
Example.
2. Create the prototype of your own data cartridge, starting with a single user-defined
type and a few data elements and methods. You can add user-defined types, data
elements, and methods, specific indextypes, and user-defined operators as you
expand the cartridge's capabilities.
2-10
Chapter 2
Data Cartridge Deployment
3. Begin by implementing your methods entirely in SQL, and add callouts to 3GL code later
if you need them.
4. Test and debug your cartridge.
3. Consider the feasibility of packaging one or more of these areas as a new data cartridge
or as an extension to an existing cartridge.
4. Use an object-oriented methodology to help decide what object types to include in data
cartridges.
5. Build and test the cartridges, one at a time.
2-11
Part II
Building Data Cartridges
This part describes the processes used to build data cartridge components.
• Defining Object Types for Data Cartridges
• Implementing Data Cartridges in PL/SQL
• Implementing Data Cartridges in C, C++, and Java
• Working with Unstructured Data Types
• Using Extensible Indexing
• Building Domain Indexes
• Defining Operators
• Using Extensible Optimizer
• Using Cartridge Services
• Using User-Defined Aggregate Functions
• Using Pipelined and Parallel Table Functions
• Designing Data Cartridges
3
Defining Object Types for Data Cartridges
Object types are crucial to building data cartridges in that they enable domain-level
abstractions to be captured in the database.
See Also:
A method is a procedure or function that is part of the object type definition and that can
operate on the object type data attributes. Such methods are called member methods, and
they take the keyword MEMBER when you specify them as a component of the object type. The
DataStream type definition declares three methods. The first two, DataStreamMin and
DataStreamMax, calculate the minimum and maximum values, respectively, in the data stream
stored inside the character LOB. The third method, DataStreamToInt, a map method,
governs comparisons between instances of data stream type.
After declaring the type, define the type body. The body contains the code for type methods.
Example 3-2 shows the type body definition for the DataStream type. It defines the member
function methods, DataStreamMin and DataStreamMax, and the map method
DataStreamToInt.
3-1
Chapter 3
Objects and Object Types for Data Cartridges
See Also:
3-2
Chapter 3
Assigning an Object Identifier to an Object Type in Data Cartridges
Consider creating a SpecialPerson type, and then instantiating this type in two different
databases in tables named SpecialPersonTable1 and SpecialPersonTable2. The RDBMS
must know that the SpecialPerson type is the same type in both instances, and therefore the
type must be defined using the same OID in both databases. If you do not specify an OID with
CREATE TYPE, a unique identifier is created automatically by the RDBMS. The syntax for
specifying an OID for an object type is in Example 3-3.
In Example 3-4, the SELECT statement generates an OID, and the CREATE TYPE statement uses
the OID in creating an object type named mytype. Be sure to use the SELECT statement to
generate a different OID for each object type to be created, because this is the only way to
guarantee that each OID is valid and globally unique.
3-3
Chapter 3
Constructor Methods in Data Cartridges
Comparison of objects uses special member functions of the object type: map
methods and order methods. To perform object comparison, you must implement
either a map method or an order method in the CREATE TYPE and CREATE TYPE BODY
statements. In Example 3-7, the type body for the DataStream type implements the
map member function.
The definition of the map member function relies on the presence of the id attribute of
the DataStream type to map instances to integers. Whenever a comparison operation
is required between objects of type DataStream, the map function DataStreamToInt()
is called implicitly by the system.
The object type rational_type does not have a simple id attribute like DataStream.
Instead, its map member function is complicated, as demonstrated in Example 3-8.
3-4
Chapter 3
Object Comparison in Data Cartridges
Because a map function can return any of the built-in types, rational_type can return a
value or type REAL.
If you have not defined a map or order function for an object type, it can only support equality
comparisons. Oracle SQL performs the comparison by doing a field-by-field comparison of
the attributes of that type.
• Implementing a Member Function
• Implementing Functions for Types Without a Simple Id Attribute
3-5
4
Implementing Data Cartridges in PL/SQL
You can use PL/SQL to implement data cartridge methods. Methods are procedures and
functions that define the operations permitted on data defined using the data cartridge.
• Methods
• Debugging PL/SQL Code
4.1 Methods
A method is procedure or function that is part of the object type definition, and that can
operate on the attributes of the type. Such methods are also called member methods, and
they take the keyword MEMBER when you specify them as a component of the object type.
Consider simple examples for implementing a method, invoking a method, and referencing
an attribute in a method.
• Implementing Methods
• Invoking Methods
• Referencing Attributes in a Method
See Also:
The definition in Example 4-2 defines the function gcd, which is used in the definition of the
normalize method in the CREATE TYPE BODY statement.
The statements in Example 4-3 implement the methods rat_to_real, normalize, and plus
for the object type rational_type.
4-1
Chapter 4
Methods
4-2
Chapter 4
Methods
An alternative way to invoke a method is by using the SELF built-in parameter. Because the
implicit first parameter of each method is the name of the object on whose behalf the method
is invoked, Example 4-7 performs the same action as the salary :=
employee.get_emp_sal(); line in Example 4-6.
In Example 4-7, employee is the name of the object on whose behalf the get_emp_sal()
method is invoked.
• General Syntax for Invoking Methods
• SQL Syntax for Invoking Methods
• PL/SQL Syntax for Invoking Methods
• Using the SELF Build-In Parameter
4-3
Chapter 4
Debugging PL/SQL Code
...
BEGIN
salary := employee.get_emp_sal();
...
END;
The statements var1 := 42 and SELF.var1 := 42 have the same effect. Because
var1 is the name of an attribute of the object type a_type and because set_var1 is a
member method of this object type, no qualification is required to access var1 in the
method code. However, for code readability and maintainability, you can use the
keyword SELF in this context to make the reference to var1 more clear.
4-4
Chapter 4
Debugging PL/SQL Code
PL/SQL functions or procedures that a method calls. The tracing tool also provides
information about exception conditions in the application code. The trace output is written to
the Oracle server trace file. Note that only the database administrator has access to the file.
See Also:
• The Oracle Database SQL Tuning Guide describes the tracing tool
• The Oracle Database PL/SQL Packages and Types Reference and the Oracle
Database PL/SQL Language Reference describe the DBMS_OUTPUT package
4-5
Chapter 4
Debugging PL/SQL Code
If you see either or both of these messages, you have made an error with the
signature for a procedure or function. In other words, you have a mismatch between
the function or procedure prototype that you entered in the object specification, and
the definition in the object body.
Ensure that parameter orders, parameter spelling (including case), and function
returns are identical. Use copy-and-paste to avoid errors in typing.
This error might occur after you exit the debugger for the DLL. Restart the program
outside the debugger.
This error might occur if you are extending an existing data cartridge; it indicates that
the package has been corrupted and must be recompiled.
Before you can perform the recompilation, you must delete all tables and object types
that depend upon the package that you are recompiling. To find the dependents on a
Windows NT system, use the Oracle Administrator toolbar. Click the Schema button,
log in as sys\<password>, and find packages and tables that you created. Drop these
packages and tables by entering SQL statements in the SQL*Plus interface, as shown
in Example 4-10:
Example 4-10 Dropping Packages and Tables
Drop type type_name;
Drop table table_name cascade constraints;
The recompilation can then be done using the SQL statements in Example 4-11:
Example 4-11 Recompiling Packages
Alter type type_name compile body;
Alter type type_name compile specification;
4-6
5
Implementing Data Cartridges in C, C++, and
Java
You can use C, C++, and Java to implement data cartridge methods. Methods are
procedures and functions that define the operations permitted on data defined using the data
cartridge. The focus is on issues related to developing and debugging external procedures.
• Shared Libraries
• External Procedures
• How PL/SQL Calls an External Procedure
• Configuring Files for External Procedures
• Using Callbacks
• Common Potential Errors
• Debugging External Procedures
• Guidelines for Using External Procedures with Data Cartridges
• Java Methods
See Also:
5-1
Chapter 5
Shared Libraries
library is created, you can refer to the shared library by the name DS_Lib from PL/SQL.
Example 5-1 specifies an absolute path for the library.
If you have copies of the library on multiple systems, to support distributed execution
of external procedures by designated or dedicated agents, use an environment
variable to specify the location of the libraries more generally, as in Example 5-2. This
statement uses the environment variable ${DS_LIB_HOME} to specify a common point
of reference or root directory from which the library can be found on all systems. The
string following the AGENT keyword specifies the agent (actually, a database link) that is
used to run any external procedure declared to be in library DS_Lib.
Oracle Database 12c Release 1 (12.1) introduces two extensions to the CREATE
LIBRARY syntax: an additional optional CREDENTIAL option and a DIRECTORY object
option. The credential option specifies the credentials of the operating system user
that the extproc agent impersonates when running an external subprogram that
specifies the library. The directory object option specifies the directory where the
shared library can be found.
Example 5-3 specifies a directory object option instead of a full path string; it also
specifies an optional credential argument. When an external procedure call through
the extproc process loads the PL/SQL library, extproc can authenticate and
'impersonate' on behalf of the defined smith_credential credential.
Note that if the directory or the credential does not exist when calling CREATE LIBRARY
statement, the library is created with errors.
• Creating an Alias Library
• Specifying the Location of the Library
• Creating an Alias Library through Directory Objects
5-2
Chapter 5
External Procedures
See Also:
PL/SQL User's Guide and Reference for details on external procedures and their
use
The final part of the EXTERNAL clause in the example is the WITH CONTEXT specification. Here,
a context pointer is passed to the external procedure. The context pointer is opaque to the
external procedure, but is available so that the external procedure can call back to the Oracle
server, to potentially access more data in the same transaction context.
Although the example describes external procedure calls from object type methods, a data
cartridge can use external procedures from a variety of other places in PL/SQL. External
procedure calls can appear in:
5-3
Chapter 5
How PL/SQL Calls an External Procedure
• Anonymous blocks
• Standalone and packaged subprograms
• Methods of an object type
• Database triggers
• SQL statements (calls to packaged functions only)
See Also:
After receiving the name of the DLL and the external procedure, extproc loads the
DLL and runs the external procedure. Also, extproc handles service calls, such as
raising an exception, and callbacks to the Oracle server. Finally, extproc passes to
PL/SQL any values returned by the external procedure. Figure 5-1 shows the flow of
control.
5-4
Chapter 5
Configuring Files for External Procedures
extproc
Oracle
Database /data_cartridge_dir/libdatastream.so
After the external procedure completes, extproc remains active throughout your Oracle
session. Thus, you incur the cost of spawning extproc only one time, no matter how many
calls you make. Still, you should call an external procedure only when the computational
benefits outweigh the cost. When you log off, extproc is killed.
Note that the Listener must start extproc on the system that runs the Oracle server. Starting
extproc on a different system is not supported.
See Also:
5-5
Chapter 5
Configuring Files for External Procedures
Example 5-6 assumes that IPC mechanisms are used to communicate with the
external procedure. You can also use, for example, TCP/IP for communication, in
which case the PROTOCOL parameter must be set to tcp.
See Also:
Oracle Database Administrator's Guide for more information about
configuring the listener.ora and tnsnames.ora files
CONNECT_TIMEOUT_LISTENER = 0
LISTENER = (ADDRESS_LIST=
(ADDRESS=(PROTOCOL=ipc)(KEY=o10))
(ADDRESS=(PROTOCOL=tcp)(HOST=unix123)(PORT=1521))
)
SID_LIST_LISTENER = (SID_LIST=
SID_DESC=(SID_NAME=o10)(ORACLE_HOME=/rdbms/u01/app/oracle/product/11.2.0.1.0)
SID_DESC=(SID_NAME=extproc)
(ORACLE_HOME=/rdbms/u01/app/oracle/product/11.2.0.1.0)
(PROGRAM=extproc))
5-6
Chapter 5
Configuring Files for External Procedures
• The set of PL/SQL data types does not correspond one-to-one with the set of C data
types.
• PL/SQL parameters can be null, whereas C parameters cannot. Unlike C, PL/SQL
includes the RDBMS concept of nullity.
• The external procedure might need the current length or maximum length of CHAR, LONG
RAW, RAW, and VARCHAR2 parameters.
• The external procedure might need character set information about CHAR, VARCHAR2, and
CLOB parameters.
• PL/SQL might need the current length, maximum length, or null status of values returned
by the external procedure.
Consider how to best specify a parameter list that deals with these circumstances. An
example of parameter passing is shown in Example 5-7, where the package function
DS_Findmin(data CLOB) calls the C routine c_findmin and the CLOB argument is passed to
the C routine as an OCILobLocator().
To avoid errors when declaring C prototype parameters, refer to Table 5-2, which shows the
C data type to specify for a given external data type and PL/SQL parameter mode. For
example, if the external data type of an OUT parameter is CHAR, specify the data type char* in
your C prototype.
• Conversion to External Datatypes
• Conversion from External Datatypes
5-7
Chapter 5
Configuring Files for External Procedures
5-8
Chapter 5
Configuring Files for External Procedures
See Also:
Oracle Database PL/SQL Language Reference.
When the C routine reads the data, it can use the OCI buffering and streaming interfaces
associated with LOBs, so that only incremental amounts of stack are needed. Such re-access
of the database from an external procedure is known as a callback.
5-9
Chapter 5
Using Callbacks
To be able to call back to a database, you must use the WITH CONTEXT clause to give
the external procedure access to the database environment, service, and error
handles. When an external procedure is called using WITH CONTEXT, the corresponding
C routine automatically gets an argument of type OCIExtProcContext* as its first
parameter. The order of the parameters can be changed using the PARAMETERS clause.
You can use this context pointer to fetch the handles using the OCIExtProcGetEnv call,
and then call back to the database. This procedure is shown in Example 5-7.
See Also:
Oracle Call Interface Programmer's Guide for details about OCI callbacks
• Restrictions on Callbacks
5-10
Chapter 5
Common Potential Errors
– OCIInitialize()
– OCIPasswordChange()
– OCIServerAttach()
– OCIServerDetach()
– OCISessionBegin ()
– OCISessionEnd ()
– OCISvcCtxToLda()
– OCITransCommit()
– OCITransDetach()
– OCITransRollback()
– OCITransStart()
• Also, with OCI routine OCIHandleAlloc(), the following handle types are not supported:
– OCI_HTYPE_SERVER
– OCI_HTYPE_SESSION
– OCI_HTYPE_SVCCTX
– OCI_HTYPE_TRANS
5-11
Chapter 5
Debugging External Procedures
Note that DEBUG_EXTPROC works only on platforms with debugger utilities that can
attach to a running process.
5-12
Chapter 5
Java Methods
For help in creating a dynamic link library, look in the RDBMS subdirectory /public, where a
template makefile can be found.
When calling external procedures, never write to IN parameters or overflow the capacity of
OUT parameters. PL/SQL does no run-time checks for these error conditions. Likewise, never
read an OUT parameter or a function result. Also, always assign a value to IN OUT and OUT
parameters and to function results. Otherwise, your external procedure does not return
successfully.
If you include the WITH CONTEXT and PARAMETERS clauses, you must specify the parameter
CONTEXT, which shows the position of the context pointer in the parameter list. If you omit the
PARAMETERS clause, the context pointer is the first parameter passed to the external
procedure.
If you include the PARAMETERS clause and the external procedure is a function, you must
specify the parameter RETURN (not RETURN property) in the last position.
For every formal parameter, there must be a corresponding parameter in the PARAMETERS
clause. Also, ensure that the data types of parameters in the PARAMETERS clause are
compatible with those in the C prototype, because no implicit conversions are done.
A parameter for which you specify INDICATOR or LENGTH has the same parameter mode as
the corresponding formal parameter. However, a parameter for which you specify MAXLEN,
CHARSETID, or CHARSETFORM is always treated like an IN parameter, even if you also specify BY
REFERENCE.
With a parameter of type CHAR, LONG RAW, RAW, or VARCHAR2, you must use the property
LENGTH. Also, if that parameter is IN OUT or OUT and null, you must set the length of the
corresponding C parameter to zero.
See Also:
For more information about multithreading, see the Oracle Database
Heterogeneous Connectivity Administrator's Guide.
See Also:
Cartridge Services Using C, C++ and Javafor information on ODCI classes.
5-13
6
Working with Unstructured Data Types
Some data types that are not native to the Oracle Database can be represented in the Oracle
Database as Large Objects (LOBs). You can implement them in PL/SQL and OCI for Data
Cartridges.
• Overview of Cartridges and Unstructured Data Types
• Using DDL for LOBs
• LOB Locators
• Emptying LOBs
• Using the OCI to Manipulate LOBs
• Using DBMS_LOB Package to Manipulate LOBs
• LOBs in External Procedures
• LOBs and Triggers
• Using Open/Close as Bracketing Operations for Efficient Performance
• Internal LOBs are stored in the database tablespaces in a way that optimizes space and
provides efficient access. Internal LOBs participate in the transactional model of the
server.
Internal LOBs can store binary data (BLOBs), single-byte character data (CLOBs), or fixed-
width single-byte or multibyte character data (NCLOBs). An NCLOB consists of character
data that corresponds to the national character set defined for the Oracle database.
Varying width character data is not supported in Oracle.
• External LOBs are stored in operating system files outside the database tablespaces as
BFILEs, binary data. They cannot participate in transactions.
Both internal LOBs and in BFILEs provide considerable flexibility in handling large amounts of
data.
Data stored in a LOB is called the LOB's value. To the Oracle server, a LOB's value is
unstructured and cannot be queried. You must unpack and interpret a LOB's value in
cartridge-specific ways.
LOBs can be manipulated using the Oracle Call Interface, OCI, or the PL/SQL DBMS_LOB
package. You can write functions, including methods on object types that can contain LOBs, to
manipulate parts of LOBs.
6-1
Chapter 6
Using DDL for LOBs
See Also:
Oracle Database SecureFiles and Large Objects Developer's Guide for
details on LOBs
Example 6-2 creates an object table, lob_table, in which each row is an instance of
lob_type data:
Example 6-3 shows how to store LOBs in a regular table, as opposed to an object table
as in Example 6-2.
When creating LOBs in tables, you can set the LOB storage, buffering, and caching
properties.
See Also:
Oracle Database SQL Language Reference manual and the Oracle
Database SecureFiles and Large Objects Developer's Guide for information
about using LOBs in CREATE TABLE, ALTER TABLE, CREATE TYPE and ALTER
TYPE statements
6-2
Chapter 6
LOB Locators
When you use an API function to manipulate the LOB value, you refer to the LOB using the
locator. The PL/SQL DBMS_LOB package contains useful routines to manipulate LOBs, such as
PUT_LINE() and GETLENGTH(), as in Example 6-5.
For a BFILE, the LOB column has its own distinct locator, which refers to the LOB's value that
is stored in an external file in the server's file system. This implies that two rows in a table
with a BFILE column may refer to the same file or two distinct files. A BFILE locator variable in
a PL/SQL or OCI program behaves like any other automatic variable. With respect to file
operations, it behaves like a file descriptor available as part of the standard I/O library of most
conventional programming languages.
See Also:
6-3
Chapter 6
Emptying LOBs
...
END;
Before writing data to an internal LOB using OCI or the DBMS_LOB package, the LOB
column must be made non-null; it must contain a locator that points to an empty or
populated LOB value. You can initialize a BLOB column's value to empty by using the
function EMPTY_BLOB in the VALUES clause of an INSERT statement. Similarly, a CLOB or
NCLOB column's value can be initialized by using the function EMPTY_CLOB. Here is the
syntax of these functions:
FUNCTION EMPTY_BLOB() RETURN BLOB;
FUNCTION EMPTY_CLOB() RETURN CLOB;
EMPTY_BLOB returns an empty locator of type BLOB and EMPTY_CLOB returns an empty
locator of type CLOB, which can also be used for NCLOBs. The functions don't have an
associated pragma.
An exception is raised if you use these functions anywhere but in the VALUES clause of
a SQL INSERT statement or as the source of the SET clause in a SQL UPDATE
statement.
Example 6-6 shows EMPTY_BLOB() used with SQL DML.
6-4
Chapter 6
Using the OCI to Manipulate LOBs
Table 6-2 compares the OCI and PL/SQL (DBMS_LOB package) interfaces in terms of LOB
access.
Example 6-8 shows how to select a LOB from the database into a locator. It assumes that the
type lob_type has two attributes, id of type INTEGER and data of type CLOB, and that a table,
lob_table, of type lob_type, exists.
A sample program, populate.c, uses the OCI to populate a CLOB with the contents of a file.
See Also:
Oracle Call Interface Programmer's Guide for detailed documentation, including
parameters, parameter types, return values, and example code.
Function Description
OCILobAppend() Appends LOB value to another LOB.
OCILobArrayRead() Reads LOB data for multiple locators in one round-trip.
6-5
Chapter 6
Using the OCI to Manipulate LOBs
Function Description
OCILobArrayWrite() Writes LOB data for multiple locators in one round-trip.
OCILobAssign() Assigns one LOB locator to another.
OCILobCharSetForm() Returns the character set form of a LOB.
OCILobCharSetId() Returns the character set ID of a LOB.
OCILobClose() Closes a previously opened LOB or BFILE.
OCILobCopy2() Copies all or a portion of a LOB into another LOB; replaces
the deprecated method OCILobCopy().
OCILobCreateTemporary() Creates a temporary LOB.
OCILobErase2() Erases all or part of aLOB, starting at a specified offset;
replaces the deprecated method OCILobErase().
OCILobFileClose() Closes an open BFILE.
OCILobFileCloseAll() Closes all open BFILEs.
OCILobFileExists() Determines if a BFILE exists.
OCILobFileGetName() Returns the name of a BFILE.
OCILobFileIsOpen() Determines if the BFILE is open.
OCILobFileOpen() Opens a BFILE.
OCILobFileSetName() Sets the name of a BFILE in a locator.
OCILobFreeTemporary() Frees a temporary LOB.
OCILobGetChunkSize() Gets the chunk size of a LOB.
OCILobGetContentType() Gets the user-specified content type string for the data in a
SecureFile, if set.
OCILobGetLength2() Gets the length of a LOB; replaced the deprecated method
OCILobGetLength().
OCILobGetOptions() Gets the enabled settings that correspond to the specified
input option types for a specified SecureFile LOB.
OCILobGetStorageLimit() Gets the maximum length of a LOB (BLOB, CLOB, or NCLOB),
in bytes.
OCILobIsEqual() Determines if two LOB locators refer to the same LOB.
OCILobIsOpen() Determines if a LOB or BFILE is open.
OCILobIsTemporary() Determines if a locator points to a temporary LOB.
OCILobLoadFromFile2() Loads BFILE data into an internal LOB; replaced the
deprecated method OCILobLoadFromFile().
OCILobLocatorAssign() Assigns one LOB or BFILE locator to another.
OCILobLocatorIsInit() Tests to see if a LOB locator is initialized.
OCILobOpen() Opens a LOB in the specified mode.
OCILobRead2() Reads a specified portion of a non-null LOB or a BFILE into
a buffer; replaces the deprecated method OCILobRead().
OCILobSetContentType() Sets a content type string for the data in the SecureFile
LOB.
OCILobSetOptions() Enables option settings for a SecureFile LOB.
6-6
Chapter 6
Using the OCI to Manipulate LOBs
Function Description
OCILobTrim2() Truncates a LOB; replaces the deprecated method
OCILobTrim().
OCILobWrite2() Writes data from a buffer into a LOB, writing over existing
data; replaces the deprecated method OCILobWrite().
OCILobWriteAppend2() Writes data starting at the current end of a LOB; replaces
the deprecated method OCILobWriteAppend().
N/A DBMS_LOB.INSTR()
N/A DBMS_LOB.SUBSTR()
OCILobAppend() DBMS_LOB.APPEND()
OCILobCharSetForm() N/A
OCILobCharSetId() N/A
OCILobCopy() DBMS_LOB.COPY()
OCILobErase() DBMS_LOB.ERASE()
OCILobFileClose() DBMS_LOB.FILECLOSE()
OCILobFileCloseAll() DBMS_LOB.FILECLOSEALL()
OCILobFileExists() DBMS_LOB.FILEEXISTS()
OCILobFileGetName() DBMS_LOB.FILEGETNAME()
OCILobFileIsOpen() DBMS_LOB.FILEISOPEN()
6-7
Chapter 6
Using the OCI to Manipulate LOBs
OCILobFileOpen() DBMS_LOB.FILEOPEN()
OCILobGetLength() DBMS_LOB.GETLENGTH()
OCILobIsEqual() N/A
OCILobRead() DBMS_LOB.READ()
OCILobTrim() DBMS_LOB.TRIM()
OCILobWrite() DBMS_LOB.WRITE()
6-8
Chapter 6
Using DBMS_LOB Package to Manipulate LOBs
if (!flag)
{
(void) printf("Select_Locator --ERROR: LOB Locator is not initialized.\n");
return OCI_ERROR;
}
return OCI_SUCCESS;
}
6-9
Chapter 6
Using DBMS_LOB Package to Manipulate LOBs
See Also:
Oracle Database PL/SQL Packages and Types Reference provides full
details on using the routines of the DBMS_LOB package.
Routine Description
APPEND() Appends the contents of the source LOB to the destination LOB.
COPY() Copies all or part of the source LOB to the destination LOB.
INSTR() Return the matching position of the nth occurrence of the pattern
in the LOB.
READ() Reads data from the LOB starting at the specified offset
SUBSTR() Returns part of the LOB value starting at the specified offset.
FILEISOPEN() Tests the file was opened using the input BFILE locators.
6-10
Chapter 6
LOBs in External Procedures
The routine c_findmin is in a shared library associated with DS_Lib. To use the pointer
OCILobLocator* to get data from the LOB, you must reconnect to the database by making a
callback.
• Defining an External Procedure (PL/SQL)
In regular triggers, you can read the :old value, but you cannot read the :new value. In
INSTEAD OF triggers, you can read both the :old and the :new values.
You cannot specify LOB type columns in an OF clause, because BFILE types can be updated
without updating the underlying table on which the trigger is defined.
Using OCI functions or the DBMS_LOB package to update LOB values or LOB attributes of object
columns does not fire triggers defined on the table that contains the columns or attributes.
6-11
Chapter 6
Using Open/Close as Bracketing Operations for Efficient Performance
You do not have to wrap all LOB operations inside the Open/Close operations, but code
block can be very valuable for the following reasons:
• If you do not wrap LOB operations inside an Open/Close call, then each
modification to the LOB implicitly opens and closes the LOB, thereby firing all
triggers. If you do wrap the LOB operations inside a pair of Open...Close
operations, then the triggers are not fired for each LOB modification. Instead, one
trigger is fired when the Close call is made. Likewise, extensible indexes are not
updated until the Close call. This means that any extensible indexes on the LOB
are not valid between the Open...Close calls.
• You must apply this technology carefully because state, which reflects the changes
to the LOB, is not saved between the Open and the Close operations. When you
have called Open, Oracle no longer keeps track of what portions of the LOB value
were modified, nor of the old and new values of the LOB that result from any
modifications. The LOB value is still updated directly for each OCILob* or DBMS_LOB
operation, and the usual read consistency mechanism is still in place. You may
also want extensible indexes on the LOB to be updated, as LOB modifications are
made because the extensible LOB indexes are always valid and may be used at
any time.
• The API enables you to determine if the LOB is open. In all cases, openness is
associated with the LOB, not the locator. The locator does not save any state
information.
• Errors and Restrictions Regarding Open/Close Operations
Example 6-11 assumes that loc1 is refers to an open LOB, and is assigned to loc2. If
loc2 is subsequently used to modify the LOB value, the modification is grouped with
loc1's modifications. This means that there is only one entry in the LOB manager's
state, not one for each locator. When the LOB is closed, either through loc1 or loc2,
the triggers are fired, so all updates made to the LOB through either locator are
committed. After the close of the LOB, if the user tries to use either locator to modify the
LOB, the operation performs an implicit Open() and Close(), as Open() ...
6-12
Chapter 6
Using Open/Close as Bracketing Operations for Efficient Performance
operation ... Close(). Note that consistent read is still maintained for each locator.
Remember that it is the LOB, not the locator, that is opened and closed. No matter how many
copies of the locator are made, the triggers for the LOB are fired only one time on the first
Close() call.
6-13
7
Using Extensible Indexing
Extensible indexing allows you to implement modes of indexing in addition to those that are
built into Oracle. Consider when you should create domain indexes, which are indexes
created using the extensible indexing framework.
• Overview of Extensible Indexing
• Extensible Indexing Framework
• Using the Text Indextype
7-1
Chapter 7
Overview of Extensible Indexing
7-2
Chapter 7
Overview of Extensible Indexing
• Hash
• k-d tree
• Point Quadtree
7.1.4.1 B-tree
No index structure can satisfy all needs, but the self-balancing B-tree index comes closest to
optimizing the performance of searches on large sets of data. Each B-tree node holds
multiple keys and pointers. The maximum number of keys in a node supported by a specific
B-tree is the order of that tree. Each node has a potential of order+1 pointers to the level
below it. For example, the order=2 B-tree illustrated in Figure 7-1 has tree pointers: to child
nodes whose value is less than the first key, to the child nodes whose value is greater than
the first key and less than the second key, and to the child nodes whose value is greater than
the second key. Thus, the B-tree algorithm minimizes the number of reads and writes
necessary to locate a record by passing through fewer nodes than in a binary tree algorithm,
which has only one key and at most two children for each decision node. Here we describe
the Knuth variation in which the index consists of two parts: a sequence set that provides
fast sequential access to the data, and an index set that provides direct access to the
sequence set.
Although the nodes of a B-tree generally do not contain the same number of data values, and
they usually contain a certain amount of unused space, the B-tree algorithm ensures that the
tree remains balanced and that the leaf nodes are at the same level.
(with pointers to
Sequence set
data records)
Index set
x x x
x x x
x
x
x x x
x x x
x x x
x
x
x
x x x
x x x
x x x
x
x
x x x
7.1.4.2 Hash
Hashing gives fast direct access to a specific stored record based on a given field value.
Each record is placed at a location whose address is computed as some function of some
field of that record. The same function is used to insert and retrieve.
The problem with hashing is that the physical ordering of records has little if any relation to
their logical ordering. Also, there can be large unused areas on the disk.
7-3
Chapter 7
Overview of Extensible Indexing
Paris
10
Jones
S200
11
5
8
2
20 London
Paris
30
Blake
Clark
S400
S300
10
4
7
1
30 London
Athens
S500 Adams 30
Smith
S100
12
0
9
7.1.4.3 k-d tree
Data that has two dimensions, such as latitude and longitude, can be stored and
retrieved efficiently using a variation on the k-d tree known as the 2-d tree.
In this structure, each node is a data type with fields for information, the two co-
ordinates, and a left-link and right-link, which can point to two children.
B (XX, XX)
C (XX, XX)
A (XX, XX)
A (XX, XX)
A (XX, XX)
7-4
Chapter 7
Overview of Extensible Indexing
This structure is good at range queries. That is, if the user specifies a point (xx, xx) and a
distance, the query returns the set of all points within the specified distance of the original
point.
2-d trees are easy to implement. However, because a 2-d tree containing k nodes can have a
height of k, insertion and querying can be complex.
A B
A B C
7-5
Chapter 7
Extensible Indexing Framework
Like 2-d trees, point quadtrees are easy to implement. However, a point quadtree
containing k nodes can have a height of k, so insertion and querying can be complex.
Each comparison requires comparisons on at least two co-ordinates. In practice,
though, the lengths from root to leaf tend to be shorter in point quadtrees.
See Also:
7-6
Chapter 7
Using the Text Indextype
1. Define and code the functional implementation for the supported operator
The Text indextype supports an operator called Contains, which accepts a text value
and a key, and returns a number indicating whether the text contains the key. The
functional implementation of this operator is a regular function defined as:
CREATE FUNCTION TextContains(Text IN VARCHAR2, Key IN VARCHAR2)
RETURN NUMBER AS
BEGIN
.......
END TextContains;
7-7
Chapter 7
Using the Text Indextype
...
);
CREATE TYPE BODY TextIndexMethods
(
...
);
To build a text domain index on the resume column, a user issues the statement in
Example 7-2.
To query the text data in the resume column, users issue statements like the one in
Example 7-3. The query execution uses the text index on resume to evaluate the
Contains predicate.
7-8
Chapter 7
Using the Text Indextype
resume VARCHAR2(2000),
location VARCHAR2(200),
department_id NUMBER(4));
7-9
8
Building Domain Indexes
Consider domain indexes and the ODCIIndex interface: how to use of domain indexes, their
partitioning, applicable restrictions, and migration procedures.
If you use user-managed domain indexes, the information specific to their implementation is
in User-Managed Local Domain Indexes
• Overview of Indextypes and Domain Indexes
• ODCIIndex Interface
• Creating, Dropping, and Commenting Indextypes
• Domain Indexes
• Object Dependencies, Drop Semantics, and Validation
• Indextype, Domain Index, and Operator Privileges
• Partitioned Domain Indexes
• Using System Partitioning
• Using System-Managed Domain Indexes
• Designing System-Managed Domain Indexes
• Creating Local Domain Indexes
• Maintaining Local Domain Indexes with INSERT, DELETE, and UPDATE
• Querying Local Domain Indexes
• System Managed Domain Index - Supported Schemes
• Restrictions of System-Managed Domain Indexing
• Migrating Non-Partitioned Indexes
• Migrating Local Partitioned Indexes
8-1
Chapter 8
ODCIIndex Interface
See Also:
Defining Operators
8-2
Chapter 8
ODCIIndex Interface
See Also:
Extensible Indexing Interface for method signatures and parameter descriptions
• ODCIIndexCreate()
• ODCIIndexAlter()
• ODCIIndexDrop()
8.2.1.1 ODCIIndexCreate()
When a user issues a CREATE INDEX statement that references the indextype, Oracle calls
your ODCIIndexCreate() method, passing it any parameters specified as part of the CREATE
INDEX... PARAMETERS (...) statement, plus the description of the index.
Typically, this method creates the tables or files in which you plan to store index data. Unless
the base table is empty, the method should also build the index.
8.2.1.2 ODCIIndexAlter()
When a user issues an ALTER INDEX statement referencing your indextype, Oracle calls your
ODCIIndexAlter() method, passing it the description of the domain index to be altered along
with any specified parameters. This method is also called to handle an ALTER INDEX with the
REBUILD or RENAME options. What your method must do depends on the nature of your domain
index, so the details are left to you as the designer of the indextype.
8-3
Chapter 8
ODCIIndex Interface
8.2.1.3 ODCIIndexDrop()
When a user destroys an index of your indextype by issuing a DROP INDEX statement,
Oracle calls your ODCIIndexDrop() method.
8.2.2.1 ODCIIndexInsert()
When a user inserts a record, Oracle calls your ODCIIndexInsert() method, passing it
the new values in the indexed columns and the corresponding row identifier.
8.2.2.2 ODCIIndexDelete()
When a user deletes a record, Oracle calls your ODCIIndexDelete() method, passing it
the old values in the indexed columns and the corresponding row identifier.
8.2.2.3 ODCIIndexUpdate()
When a user updates a record, Oracle calls your ODCIIndexUpdate() method, passing
it the old and new values in the indexed columns and the corresponding row identifier.
8-4
Chapter 8
ODCIIndex Interface
• ODCIIndexFetch()
• ODCIIndexClose()
8.2.3.1 ODCIIndexStart()
Oracle calls your ODCIIndexStart() method at the beginning of an index scan, passing it
information on the index and the operator. Typically, this method:
• Initializes data structures used in the scan
• Parses and executes SQL statements that query the tables storing the index data
• Saves any state information required by the fetch and cleanup methods, and returns the
state or a handle to it
• Sometimes generates a set of result rows to be returned at the first invocation of
ODCIIndexFetch()
The information on the index and the operator is not passed to the fetch and cleanup
methods. Thus, ODCIIndexStart() must save state data that must be shared among the
index scan routines and return it through an output sctx parameter. To share large amounts
of state data, allocate cursor-duration memory and return a handle to the memory in the sctx
parameter.
As member methods, ODCIIndexFetch() and ODCIIndexClose() are passed the built-in SELF
parameter, through which they can access the state data.
See Also:
• ODCIIndexClose()
• ODCIIndexFetch()
• ODCIIndexStart()
• Oracle Call Interface Developer's Guide for information on memory services
and maintaining context
8.2.3.2 ODCIIndexFetch()
Oracle calls your ODCIIndexFetch() method to return the row identifiers of the next batch of
rows that satisfies the operator predicate, passing it the state data returned by
ODCIIndexStart() or the previous ODCIIndexFetch() call. The operator predicate is specified
in terms of the operator expression (name and arguments) and a lower and upper bound on
the operator return values. Thus, ODCIIndexFetch() must return the row identifiers of the
rows for which the operator return value falls within the specified bounds. To indicate the end
of index scan, return a NULL.
8-5
Chapter 8
ODCIIndex Interface
See Also:
• ODCIIndexFetch()
• ODCIIndexStart()
8.2.3.3 ODCIIndexClose()
Oracle calls your ODCIIndexClose() method when the cursor is closed or reused,
passing it the current state. ODCIIndexClose() should perform whatever cleanup or
closure operations your indextype requires.
8-6
Chapter 8
ODCIIndex Interface
See Also:
• ODCIIndexAlter()
• ODCIIndexCreate()
• ODCIIndexDrop()
• ODCIIndexInsert()
See Also:
ODCIIndexCreate()
8-7
Chapter 8
Creating, Dropping, and Commenting Indextypes
See Also:
Oracle Database SQL Language Reference for details on CREATE TYPE
• Creating Indextypes
• Dropping Indextypes
• Commenting Indextypes
See Also:
Oracle Database SQL Language Reference for complete descriptions of
these SQL statements
The default DROP behavior is DROP RESTRICT semantics, that is, if one or more domain
indexes exist that uses the indextype then the DROP operation is disallowed. Users can
override the default behavior with the FORCE option, which drops the indextype and
marks any dependent domain indexes invalid.
8-8
Chapter 8
Domain Indexes
See Also:
"Object Dependencies_ Drop Semantics_ and Validation" for details on object
dependencies and drop semantics
• INDEXTYPE Comments
8-9
Chapter 8
Domain Indexes
The INDEXTYPE clause specifies the indextype to be used. The PARAMETERS clause
identifies any parameters for the domain index, specified as a string. This string is
passed uninterpreted to the ODCIIndexCreate() routine for creating the domain index.
In the preceding example, the parameters string identifies the language of the text
document (thus identifying the lexical analyzer to use) and the list of stop words which
are to be ignored while creating the text index.
Example 8-4 Creating a Domain Index
CREATE INDEX ResumeTextIndex ON MyEmployees(resume)
INDEXTYPE IS TextIndexType
PARAMETERS (':Language English :Ignore the a an');
8-10
Chapter 8
Domain Indexes
A statement of this form causes Oracle to invoke the ODCIIndexAlter() method, which takes
appropriate actions to rename the domain index.
Example 8-6 Renaming a Domain Index
ALTER INDEX ResumeTextIndex RENAME TO ResumeTIdx;
The same ODCIIndexAlter() routine is called as before, but with additional information about
the ALTER option.
When the end user executes an ALTER INDEX domain_index UPDATE BLOCK REFERENCES for
a domain index on an index-organized table (IOT), ODCIIndexAlter() is called with the
AlterIndexUpdBlockRefs bit set. This gives you the opportunity to update guesses as to the
block locations of rows that are stored in the domain index in logical rowids.
Example 8-7 Rebuilding a Domain Index
ALTER INDEX ResumeTextIndex REBUILD PARAMETERS (':Ignore off');
This results in Oracle calling the ODCIIndexDrop() method, passing it information about the
index.
Example 8-9 Dropping a Domain Index
DROP INDEX ResumeTextIndex;
8-11
Chapter 8
Domain Indexes
If the rowids are stored in a VARCHAR column instead, comparisons for textual equality
of a rowid from the base table and a rowid from your own table fail in some cases
where the rowids pick out the same row. This is because index-organized tables use
logical instead of physical rowids, and, unlike physical rowids, logical rowids for the
same row can have different textual representations. Two logical rowids are equivalent
when they have the same primary key, regardless of the guess data block addresses
stored with them.
A UROWID column can contain both physical and logical rowids. Storing rowids for an
IOT in a UROWID column ensures that the equality operator succeeds on two logical
rowids that have the same primary key information but different guess DBAs.
If you create an index storage table with a rowid column by performing a CREATE TABLE
AS SELECT from the IOT base table, then a UROWID column of the correct size is
created for you in your index table. If you create a table with a rowid column, then you
must explicitly declare your rowid column to be of type UROWID(x), where x is the size
of the UROWID column. The size chosen should be large enough to hold any rowid from
the base table; thus, it should be a function of the primary key from the base table.
Use the query demonstrated by Example 8-10 to determine a suitable size for the
UROWID column.
Doing an ALTER INDEX REBUILD on index storage tables raises the same issues as
doing a CREATE TABLE if you drop your storage tables and re-create them. If, on the
other hand, you reuse your storage tables, no additional work should be necessary if
your base table is an IOT.
8-12
Chapter 8
Domain Indexes
As with DML operations, setting up the define variable as a text string works well for both
physical and universal rowids. When physical rowids are fetched from the index table, you
can be sure that their length is 18 characters. Universal rowids, however, may be up to 5072
characters long, so a string length function must be used to determine the actual length of a
fetched universal rowid.
8-13
Chapter 8
Domain Indexes
anonymous PL/SQL blocks that are written into the dump file and executed on import.
If present, these anonymous PL/SQL blocks are executed immediately before the
creation of the associated domain index.
By default, secondary objects of the domain are not imported or exported. However, if
the interfaces ODCIIndexUtilGetTableNames() and ODCIIndexUtilCleanup() are
present, the system invokes them to determine if the secondary objects associated
with the domain indexes are part of the export/import operation.
See Also:
• ODCIIndexGetMetadata()
• ODCIIndexUtilCleanup()
• ODCIIndexUtilGetTableNames()
• Oracle Database Utilities for information about using Export/Import
See Also:
Oracle Database Administrator’s Guide for information about using
transportable tablespaces
8-14
Chapter 8
Object Dependencies, Drop Semantics, and Validation
Example 8-11 demonstrates how the USER_SECONDARY_OBJECTS view may be used to obtain
information on the ResumeTextIndex that was created in Example 8-4.
8-15
Chapter 8
Indextype, Domain Index, and Operator Privileges
2. Operators
3. Indextypes
Table 8-3 Default and Explicit Drop Options for Operators and Index Types
8-16
Chapter 8
Partitioned Domain Indexes
• To use the operators in queries or DML statements, you must have EXECUTE privilege on
the operator and the associated function, package, and indextype.
• To change the implementation type, you must have EXECUTE privilege on the new
implementation type.
Note:
You cannot convert a global domain index into a local domain index by using the
ALTER TABLE MODIFY PARTITION BY statement.
You also cannot use ALTER TABLE MODIFY PARTITION BY statement to modify the
partitioning scheme of a table with any domain index defined on it.
8-17
Chapter 8
Partitioned Domain Indexes
When you specify this clause at the top level of the syntax, the parameters become
the default parameters for the index partitions. If you specify this clause as part of the
LOCAL [PARTITION] clause, you override any default parameters with parameters for
the individual partition. The LOCAL [PARTITION] clause can specify multiple partitions.
8-18
Chapter 8
Partitioned Domain Indexes
See Also:
olink:SQLRF-GUID-BFA7E29C-7905-4811-9119-B20FD8EA18F2 for complete
syntax of SQL statements.
State Description
IN_PROGRESS The index or the index partition is in this state before and during the
execution of the ODCIIndex DDL interface routines. The state is
generally transitional and temporary. However, if the routine ends
prematurely, the index could remain marked IN_PROGRESS.
FAILED If the ODCIIndex interface routine doing DDL operations on the index
returns an error, the index or index partition is marked FAILED.
UNUSABLE Same as for regular indexes: An index on a partitioned table is marked
UNUSABLE as a result of certain partition maintenance operations. Note
that, for partitioned indexes, UNUSABLE is associated only with an index
partition, not with the index as a whole.
VALID An index is marked VALID if an object that the index directly or indirectly
depends upon is exists and is valid. This property is associated only
with an index, never with an index partition.
INVALID An index is marked INVALID if an object that the index directly or
indirectly depends upon is dropped or invalidated. This property is
associated only with an index, never with an index partition.
8-19
Chapter 8
Partitioned Domain Indexes
8-20
Chapter 8
Using System Partitioning
See Also:
Supporting SQL syntax in the Oracle Database SQL Language Reference
8-21
Chapter 8
Using System Partitioning
number of partitions as the base table. It follows that such a system-partitioned table
can be used to store index data for a domain index, with the following implications:
• Pruning follows the base table pruning rules: when a partition is accessed in the
base table, the corresponding partition can be accessed in the system-partitioned
table.
• DDLs of the base table can be duplicated on the system-partitioned table.
Therefore, if a partition is dropped on the base table, the corresponding partition
on the system-partitioned table is dropped automatically.
8-22
Chapter 8
Using System Partitioning
to unmaterialized partitions. Because the system partitioned table only has materialized
partitions, DATAOBJ_TO_PARTITION numbers can cause a mis-match between the partitions of
the base table and the partitions of the underlying system partitioned index storage tables.
The new function, DATAOBJ_TO_MAT_PARTITION, shown in Example 8-19, returns the
materialized partition number (as opposed to the absolute partition number) and helps keep
the two tables in sync. Indextypes planning to support local domain indexes on interval
partitioned tables should migrate to the use of this function.
Example 8-17 Inserting Data into System-Partitioned Tables
INSERT INTO SystemPartitionedTable PARTITION (p1) VALUES (4,5);
8-23
Chapter 8
Using System Partitioning
PartitionName|
DATAOBJ_TO_MAT_PARTITION(base_table, :physical_partid))
AS SubQuery
Note:
The ALTER TABLE EXCHANGE PARTITION CASCADE command is not allowed if
there is a local domain index on the reference partitioned table.
The ALTER TABLE EXCHANGE PARTITION routine invokes the following user-
implemented methods:
1. ODCIIndexExchangePartition() for the affected partition and index
2. ODCIStatsExchangePartition() for the affected partition and index if statistics are
collected for them
8-24
Chapter 8
Using System-Managed Domain Indexes
• three partitions
• a local domain index on one of its columns, IT1
• a table of corresponding metadata objects, MT1, which is the optional metadata table
created by the indextype to store information specific to each partition of the local domain
index
• a system-partitioned table, SPT1, created by the indextype to store index data
The structures shown in these tables (table T1, index IT1 and the system partitioned table
SPT1) have the same number of partitions, in a one-to-one relationship. The metadata table
MT1 has as many rows as the number of partitions in these tables.
Figure 8-1 Three-Partition Table with a Local Domain Index, and Associated
Structures System Partitioned Table (SPT1)
PartNum
PartNum
3
1
3
Local Index (IT1)
PartName
PartName
IP1
IP2
IP3
IP1
IP2
IP3
Metadata
Params1
Params2
Params3
PartNum
PartName PartId
Server Tables
101
102
103
Index Tables
Base Table (T1)
PartName
IP1
IP2
IP3
P1
P2
P3
Figure 8-2 illustrates what happens to T1 and its related structures after splitting one of its
partitions with the operation in Example 8-20:
8-25
Chapter 8
Designing System-Managed Domain Indexes
• the partition P2 in the base table T1 splits into P21 and P22
• in the local domain index, partition IP2 is dropped and two new partitions, IP21
and IP22, are created
• the indextype invokes the ODCIIndexUpdPartMetadata() method that makes the
necessary updates to the metadata table MT1
• in the system partitioned table SPT1, the partition that corresponds to partition IP2
is dropped and two new partitions are created
• index partitions are marked UNUSABLE as a result of the split operation; they must
be rebuilt to make them USABLE
Server Tables
Base Table (T1) Local Index (IT1)
P1 1 IP1 1
P21 2 IP21 2
P22 3 IP22 3
P3 4 IP3 4
Index Tables
Metadata Table (MT1) System Partitioned Table (SPT1)
8-26
Chapter 8
Designing System-Managed Domain Indexes
When a top-level DDL that affects a local system managed domain index is called, the
system invokes user-implemented ODCIIndexXXX() and ODCIStatsXXX() methods. Table 8-8
shows these methods. In summary, the following rules apply:
• For ODCIIndexXXX () DMLs and queries, both the index partition object identifier
(ODCIIndexInfo.IndexPartitionIden) and a base table partition physical identifier
(ODIIndexInfo.IndexCols(1).TablePartitionIden) are required. For ODCIIndexXXX ()
DDL routines, both the index partition object identifier and the index partition name are
supplied.
• The CREATE INDEX routine uses two calls to ODCIIndexCreate() (one at the beginning
and one at the end), and as many calls to ODCIIndexAlter() with
alter_option=AlterIndexRebuild as there are partitions.
• The TRUNCATE TABLE routine uses as many calls to ODCIIndexAlter() with
alter_option=AlterIndexRebuild as there are partitions.
• All partition maintenance operations invoke ODCIIndexUpdPartMetadata() so that the
indextype correctly updates its partition metadata table. The list of index partitions is
specified by the index partition name and the index partition object identifier, and is
supplied with information regarding addition or dropping of the partition. No DDLs are
allowed in these calls.With each partition maintenance operation, the system implicitly
transforms the system-partitioned storage tables that were created using domain
indexes. The names of the newly generated partitions correspond to the index partition
names.
• If the system-partitioned tables are used to store partition-level statistics, then the tables
and indexes created by ODCIStatsCollect() and dropped by ODCIStatsDelete() are
tracked by the system to ensure that they remain equipartitioned.
• If the application implements user-defined partition-level statistics, the system invokes
ODCIStatsUpdPartStatistics() with every partition maintenance operation. This ensure
that the statistics type updates its partition-level statistics, and (optionally) its aggregate
global statistics. No DDLs are allowed in these calls. If ODCIStatsUpdPartStatistics() is
not implemented, the system does not raise an exception but proceeds to the next
programmatic step.
• Methods for Non-Partitioned Domain Indexes
• Methods for Local System-Managed Domain Indexes
8-27
Chapter 8
Designing System-Managed Domain Indexes
GATHER_INDEX_STATS() ODCIStatsCollect()
in DBMS_STATS
DELETE_INDEX_STATS() ODCIStatsDelete()
in DBMS_STATS
(Force)
INSERT ODCIIndexInsert()
DELETE ODCIIndexDelete()
UPDATE ODCIIndexUpdate()
in DBMS_STATS
in DBMS_STATS
8-28
Chapter 8
Designing System-Managed Domain Indexes
in DBMS_STATS
INSERT ODCIIndexInsert()
DELETE ODCIIndexDelete()
UPDATE ODCIIndexUpdate()
8-29
Chapter 8
Creating Local Domain Indexes
8-30
Chapter 8
Maintaining Local Domain Indexes with INSERT, DELETE, and UPDATE
See Also:
• ODCIIndexAlter()
• ODCIIndexCreate()
8-31
Chapter 8
Restrictions of System-Managed Domain Indexing
8-32
Chapter 8
Migrating Local Partitioned Indexes
following actions. Note that the migration does not require re-generation of index data,
but involves only exchange operations.
• Create a system-partitioned table.
• For each of the n non-partitioned tables, call the ALTER TABLE EXCHANGE PARTITION
[INCLUDING INDEXES] routine to exchange a non-partitioned table for a partition of
the system-partitioned table.
• Drop all n non-partitioned tables.
3. Issue an ASSOCIATE STATISTICS command to associate a system-managed statistics
type with the system-managed indextype.
8-33
9
Defining Operators
You can define operators and use them with and without indextypes.
• User-Defined Operators
• Operators and Indextypes
• Operator Bindings
• Operator Privileges
• Creating Operators
• Dropping Operators
• Altering Operators
• Commenting Operators
• About Invoking Operators
See Also:
Oracle Database SQL Language Reference for detailed information on syntax and
privileges
9-1
Chapter 9
User-Defined Operators
in order of occurrence, and the function's return type. Operator bindings tell Oracle
which function to execute when the operator is invoked. An operator can be bound to
several functions if each function has a different signature. To be considered different,
functions must have different argument lists. Functions whose argument lists match,
but whose return data types do not match, are not considered different and cannot be
bound to the same operator.
Operators can be bound to:
• Standalone functions
• Package functions
• User-defined type member methods
Operators can be bound to functions and methods in any accessible schema. Each
operator must have at least one binding when you create it. If you attempt to specify
non-unique operator bindings, the Oracle server raises an error.
The default DROP behavior is DROP RESTRICT: if there are dependent indextypes or
ancillary operators for any of the operator bindings, then the DROP operation is
disallowed.
To override the default behavior, use the FORCE option. Example 9-3 drops the operator
and all its bindings and marks any dependent indextype objects and dependent
ancillary operators invalid.
9-2
Chapter 9
User-Defined Operators
• You can only issue ALTER OPERATOR statements that relate to existing operators.
• You can only add or drop one binding in each ALTER OPERATOR statement.
• You cannot drop an operator's only binding with ALTER OPERATOR; use the DROP OPERATOR
statement to drop the operator. An operator cannot exist without any bindings.
• If you add a binding to an operator associated with an indextype, the binding is not
associated to the indextype unless you also issue the ALTER INDEXTYPE ADD OPERATOR
statement
Comments on operators are available in the data dictionary through these views:
• USER_OPERATOR_COMMENTS
• ALL_OPERATOR_COMMENTS
• DBA_OPERATOR_COMMENTS
You can only comment operators in your own schema unless you have the COMMENT ANY
OPERATOR privilege.
9-3
Chapter 9
User-Defined Operators
Executing the statement in Example 9-8 raises an error because none of the operator
bindings satisfy the argument data types.
• Creating Contains() Operator
• Using Contains() Operator in a Query
• Using Contains() Operator Incorrectly
9-4
Chapter 9
Operators and Indextypes
9-5
Chapter 9
Operators and Indextypes
9-6
Chapter 9
Operators and Indextypes
See Also:
• ODCIIndexClose()
• ODCIIndexFetch()
• ODCIIndexStart()
9-7
Chapter 9
Operators and Indextypes
fetch(ctx1, ...);
...
close(ctx1);
close(ctx2);
Because functional implementations can make use of domain indexes, consider how
to write functions that use domain indexes and how they are invoked by the system.
Example 9-14 Using Operators Outside the WHERE Clause
SELECT Contains(resume, 'Oracle') FROM MyEmployees;
9-8
Chapter 9
Operators and Indextypes
• Scan flag: indicates whether the current call is the last invocation during which all cleanup
operations should be performed
The function TextContains() in Example 9-15 provides the index-based functional
implementation for the Contains() operator.
9-9
Chapter 9
Operators and Indextypes
The functional implementation of an ancillary operator can use either the domain index
or the state generated by the primary operator. When invoked, the functional
implementation is passed three extra arguments:
• The index context, which contains the domain index information
• The scan context, which provides access to the state generated by the primary
operator
• A scan flag to indicate whether the functional implementation is being invoked for
the last time
Consider how to define and invoke operators that modeling ancillary data.
9-10
Chapter 9
Operators and Indextypes
The ANCILLARY TO clause specifies that Score shares state with the primary operator binding
CONTAINS(VARCHAR2, VARCHAR2).
The ancillary operator binding is invoked with a single literal number argument, such as
Score(1), Score(2), and so on.
9-11
Chapter 9
Operators and Indextypes
Note:
Label arguments that are not of a numeric type or label arguments that are
not literals are not supported. Using a bind variable as the label argument to
an ancillary or primary operator results in an error, unless the bind variable
was introduced by the current setting of the CURSOR_SHARING parameter.
To determine the corresponding primary operator, Oracle matches the number passed
to the ancillary operator with the number passed as the last argument to the primary
operator. It is an error to find zero or more than one matching primary operator
invocation. After the matching primary operator invocation is found,
• The arguments to the primary operator become operands of the ancillary operator.
• The ancillary and primary operator executions are passed the same scan context.
For example, in the Example 9-17 query, the invocation of Score is determined to be
ancillary to Contains() based on the number argument 1, and the functional
implementation for Score gets the operands (resume, 'Oracle&Unix', indexctx,
scanctx, scanflg), where scanctx is shared with the invocation of Contains().
9-12
10
Using Extensible Optimizer
You can use the Oracle Database extensible optimizer to optimize SQL statement execution.
Optimization concepts, statistics, selectivity, cost analysis, the ordering of predicates, and the
dependency model of the optimizer are described.
• Overview of Query Optimization
• Defining Statistics, Selectivity, and Cost Functions
• Using User-Defined Statistics, Selectivity, and Cost
• Predicate Ordering
• Dependency Model
• Restrictions and Suggestions
10-1
Chapter 10
Overview of Query Optimization
Please note that only the cost-based optimizer has been enhanced; Oracle has not
altered the operation of the rule-based optimizer.
The optimizer generates an execution plan for SQL queries and DML statements
SELECT, INSERT, UPDATE, or DELETE. For simplicity, we describe the generation of an
execution plan in terms of a SELECT statement, but the process for DML statements is
similar.
An execution plan includes an access method for each table in the FROM clause, and
an ordering, called the join order, of the tables in the FROM clause. System-defined
access methods include indexes, hash clusters, and table scans. The optimizer
chooses a plan by generating a set of join orders, or permutations, by computing the
cost of each, and then by selecting the process with the lowest cost. For each table in
the join order, the optimizer computes the cost of each possible access method and
join method and chooses the one with the lowest cost. The cost of the join order is the
sum of the access method and join method costs. The costs are calculated using
algorithms that comprise the cost model. The cost model includes varying level of
detail about the physical environment in which the query is executed.
The optimizer uses statistics about the objects referenced in the query to compute the
selectivity and costs. The statistics are gathered using the DBMS_STATS package. The
selectivity of a predicate is the fraction of rows in a table that is chosen by the
predicate, and it is a number between 0 and 1.
The Extensible Indexing feature allows users to define new operators, indextypes, and
domain indexes. For user-defined operators and domain indexes, the Extensible
Optimizer feature enables you to control the three main components used by the
optimizer to select an execution plan statistics, selectivity, and cost.
• Statistics
• Selectivity
• Cost
See Also:
10.1.1 Statistics
Statistics for tables and indexes can be generated by using the DBMS_STATS package.
In general, the more accurate the statistics, the better the execution plan generated by
the optimizer.
• User-Defined Statistics
• User-Defined Statistics for Partitioned Objects
10-2
Chapter 10
Overview of Query Optimization
10.1.2 Selectivity
The optimizer uses statistics to calculate the selectivity of predicates. The selectivity is the
fraction of rows in a table or partition that is chosen by the predicate. It is a number between
0 and 1. The selectivity of a predicate is used to estimate the cost of a particular access
method; it is also used to determine the optimal join order. A poor choice of join order by the
optimizer could result in a very expensive execution plan.
Currently, the optimizer uses a standard algorithm to estimate the selectivity of selection and
join predicates. However, the algorithm does not always work well in cases in which
predicates contain functions or type methods. In addition, predicates can contain user-
defined operators about which the optimizer does not have any information. In that case the
optimizer cannot compute an accurate selectivity.
• User-Defined Selectivity
10-3
Chapter 10
Overview of Query Optimization
For such cases, users can define selectivity functions associated with operator(...).
The arguments to operator can be columns, constants, bind variables, or attribute
references. When optimizer encounters such a predicate, it calls the user-defined
selectivity function and passes the entire predicate as an argument (including the
operator, function, or type method and its arguments, the relational operator
relational_operator, and the constant expression or bind variable). The return value
of the user-defined selectivity function must be expressed as a percent, and be
between 0 and 100 inclusive; the optimizer ignores values outside this range.
Wherever possible, the optimizer uses user-defined selectivity values. However, this is
not possible in the following cases:
• The user-defined selectivity function returns an invalid value (less than 0 or greater
than 100).
• There is no user-defined selectivity function defined for the operator, function, or
method in the predicate.
• The predicate does not have one of the forms listed in Example 10-1; it may also
be of the form operator(...) + 3 relational_operator constant.
In each of these cases, the optimizer uses heuristics to estimate the selectivity.
Example 10-1 Three Predicate Forms that Trigger a Call to the Optimizer
operator(...) relational_operator constant
constant relational_operator operator(...)
operator(...) LIKE constant
where
• operator(...) is a user-defined operator, standalone function, package function,
or type method,
• relational_operator is one of {<, <=, =, >=, >}, and
• constant is a constant value expression or bind variable.
10.1.3 Cost
The optimizer estimates the cost of various access paths to choose an optimal plan.
For example, it computes the CPU and I/O cost of using an index and a full table scan
to choose between the two. However the optimizer does not know the internal storage
structure of domain indexes, and so it cannot compute a good estimate of the cost of a
domain index.
• User-Defined Cost
10-4
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
user-defined domain indexes and functions, such estimates can be very inaccurate and result
in the choice of a sub-optimal execution plan.
User-defined cost functions for domain indexes are called by the optimizer only if a domain
index is a valid access path for a user-defined operator. User-defined cost functions for
functions, methods and domain indexes are only called when a predicate has one of the
forms outlined in Example 10-1, which is identical to the conditions for user-defined selectivity
functions.
User-defined cost functions can return three cost values, each value representing the cost of
a single execution of a function or domain index implementation:
• CPU — the number of machine cycles executed by the function or domain index
implementation. This does not include the overhead of invoking the function.
• I/O — the number of data blocks read by the function or domain index implementation.
For a domain index, this does not include accesses to the Oracle table. The multiblock
I/O factor is not passed to the user-defined cost functions.
• NETWORK — the number of data blocks transmitted. This is valid for distributed queries,
functions, and domain index implementations. For Oracle, this cost component is not
used and is therefore ignored; however, the user is required to stipulate a value so
ensure backward compatibility.
The optimizer computes a composite cost from these cost values.
The package DBMS_ODCI contains a function estimate_cpu_units to help get the CPU and
I/O cost from input consisting of the elapsed time of a user function. estimate_cpu_units
measures CPU units by multiplying the elapsed time by the processor speed of the machine
and returns the approximate number of CPU instructions associated with the user function.
For a multiprocessor machine, estimate_cpu_units considers the speed of a single
processor.
The cost of a query is a function of the cost values. The settings of optimizer initialization
parameters determine which cost to minimize. If optimizer_mode is first_rows, the resource
cost of returning a single row is minimized, and the optimizer mode is passed to user-defined
cost functions. Otherwise, the resource cost of returning all rows is minimized.
The object type that you define, referred to as a statistics type, need not implement all the
functions from ODCIStats. User-defined statistics collection, selectivity, and cost functions are
10-5
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
optional, so a statistics type may contain only a subset of the functions in ODCIStats.
Table 10-1 summarizes the type methods and default statistics associated with
different kinds of schema objects.
Table 10-1 Statistics Methods and Default Statistics for Various Schema
Objects
The types of the parameters of statistics type methods are system-defined ODCI data
types. These are described in Extensible Optimizer Interface.
The selectivity and cost functions must not change any database or package state.
Consequently, no SQL DDL or DML operations are permitted in the selectivity and cost
functions. If such operations are present, the functions are not called by the optimizer.
• Defining a Statistics Type
• User-Defined Statistics Functions
• User-Defined Selectivity Functions
• User-Defined Cost Functions for Functions
• User-Defined Cost Functions for Domain Indexes
• Generating Statistics for System-Managed Domain Indexes
10-6
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
RETURN NUMBER,
10-7
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
See Also:
• ODCIStatsCollect()
• ODCIStatsDelete()
•
If, on the other hand, myFunction() is called with some non-literals arguments, such
as myFunction(Test_tab.col_a, 'TEST')> 5, where col_a is a column in table
Test_tab, then the selectivity function is called as outlined in Example 10-5.
In summary, the start, stop, and function argument values are passed to the selectivity
function only if they are literals; otherwise they are NULL. ODCIArgDescList describes
all the arguments that follow it.
10-8
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
See Also:
• ODCIStatsSelectivity()
• ODCIArgDescList
10-9
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
In summary, function argument values are passed to the cost function only if they are
literals; otherwise, they are NULL. ODCIArgDescList describes all the arguments that
follow it.
Example 10-6 Calling a Cost Function Using Literal Arguments
ODCIStatsFunctionCost(ODCIFuncInfo_constructor, cost,
ODCIArgDescList_constructor, 2, 'TEST', ODCIEnv_flag)
See Also:
• ODCIStatsFunctionCost()
10-10
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
In summary, the start, stop, and operator argument values are passed to the index cost
function only if they are literals; otherwise they are NULL. ODCIArgDescList describes all the
arguments that follow it.
Example 10-8 Defining an Operator
Contains(a_string VARCHAR2(2000), b_string VARCHAR2(10))
See Also:
ODCIStatsIndexCost()
10-11
Chapter 10
Defining Statistics, Selectivity, and Cost Functions
See Also:
• ODCIStatsCollect()
• ODCIStatsUpdPartStatistics()
10
5
22
10
5
15
5
IndexData
(value)
25
35
57
76
99
120
150
Partition
P1
P2
P3
10-12
Chapter 10
Using User-Defined Statistics, Selectivity, and Cost
Partition Statistics
P1 StatsP1_1
StatsP1_2
P2 StatsP2_1
StatsP2_2
StatsP2_3
P3 StatsP3_1
StatsP3_2
Statistics1
Statistics2
Statistics3
Statistics4
Statistics5
IndexPartition
IP1
IP2
IP1
IP2
IP3
Index
I1
I1
I2
I2
I2
Schema
U1
U1
U2
U2
U2
• User-Defined Statistics
• User-Defined Selectivity
• User-Defined Cost
• Declaring a NULL Association for an Index or Column
• How DDL Operations Affect Statistics
10-13
Chapter 10
Using User-Defined Statistics, Selectivity, and Cost
Individual column associations always have precedence over associations with types.
Thus, in the preceding example, if both ASSOCIATE STATISTICS commands are issued,
DBMS_STATS would use the statistics type stat (and not stat_typ1) to collect user-
defined statistics for column col_b. It is also important to note that standard statistics,
if possible, are collected along with user-defined statistics.
User-defined statistics are deleted using the ODCIStatsDelete() function from the
same statistics type that was used to collect the statistics.
Associations defined by the ASSOCIATE STATISTICS command are stored in a
dictionary table called ASSOCIATION$.
Only user-defined data types can have statistics types associated with them; you
cannot declare associations for standard SQL data types.
Example 10-10 Creating a Table with an Object Type Column
CREATE TABLE Test_tab (
col_a NUMBER,
col_b typ1,
10-14
Chapter 10
Using User-Defined Statistics, Selectivity, and Cost
col_c VARCHAR2(2000)
)
Example 10-12 Associating Statistics with Data Types for User-Defined Statistics
ASSOCIATE STATISTICS WITH TYPES typ1 USING stat_typ1
To drop index statistics, use the ODCIStatsDelete() method which is defined for the same
statistics type that defined the earlier ODCIStatsCollect() method.
Example 10-13 Creating an Indextype, an Index and an Operator for User-Defined
Statistics
CREATE INDEXTYPE indtype
FOR userOp(NUMBER)
USING imptype WITH SYSTEM MANAGED STORAGE TABLES;
10-15
Chapter 10
Using User-Defined Statistics, Selectivity, and Cost
• Default Selectivity
10-16
Chapter 10
Using User-Defined Statistics, Selectivity, and Cost
An association can be declared using either a statistics type or a default selectivity, but not
both. Thus, the following statement is illegal:
ASSOCIATE STATISTICS WITH FUNCTIONS myFunction USING stat_myFunction
DEFAULT SELECTIVITY 20
User-defined function costs do not influence the choice of access methods; they are only
used for ordering predicates, described in Extensible Optimizer Interface.
Example 10-21 Associating Statistics with Standalone Functions
ASSOCIATE STATISTICS WITH FUNCTIONS myFunction USING stat_myFunction;
10-17
Chapter 10
Using User-Defined Statistics, Selectivity, and Cost
10-18
Chapter 10
Using User-Defined Statistics, Selectivity, and Cost
benefit of a better plan may not outweigh the additional compilation time incurred by invoking
the cost or selectivity functions. For cases like this, you can use the ASSOCIATE command to
declare a NULL association for a column or index, as in Example 10-25.
If the NULL association is specified, the schema object does not inherit any statistics type from
the column type or the indextype. A NULL association also precludes default values.
Example 10-25 Declaring NULL Statistics Associations for Columns and Indexes
ASSOCIATE STATISTICS WITH COLUMNS columns NULL;
ASSOCIATE STATISTICS WITH INDEXES indexes NULL;
10-19
Chapter 10
Predicate Ordering
Command Action
DROP statistics_type FORCE Calls DISASSOCIATE FORCE for all objects associated
with the statistics_type; drops statistics_type.
10-20
Chapter 10
Restrictions and Suggestions
See Also:
ODCIStatsDelete()
10-21
Chapter 10
Restrictions and Suggestions
are deleted, while the aggregate object-level statistics are either adjusted to reflect the
operation or left "as is" for later recomputation.
The decision to adjust or recompute the aggregate statistics is made based on
_minimal_stats_aggregation parameter in the server. If the parameter is FALSE, the
aggregate statistics are recomputed. If the parameter is TRUE, the statistics are not
recomputed.
Example 10-26 demonstrates how to collect statistics for the TXT_IDX domain index on
the SCOTT schema by issuing a call to DBMS_STATS.GATHER_INDEX_STATS().
Example 10-27 shows how to delete statistics for the same index.
Example 10-28 demonstrates how to use the granularity argument to the
DBMS_STATS.GATHER_INDEX_STATS() to collect partition statistics from the PT_TXT_IDX
local domain index on the SCOTT schema.
Example 10-29 shows how to delete partition statistics for the same index. Example
10-30 and Example 10-31 demonstrate how to collect and delete statistics for a single
partition of a local domain index respectively.
10-22
Chapter 10
Restrictions and Suggestions
See Also:
Oracle Database PL/SQL Packages and Types Reference
10-23
Chapter 10
Restrictions and Suggestions
END;
/
When the local domain index is composite partitioned, it is possible to collect and
delete statistics for all subpartitions of a composite partition as shown in Example
10-32 and Example 10-33.
Example 10-34 and Example 10-35 demonstrate how to collect and delete statistics for
a single subpartition.
Additionally, if collection or deletion of statistics in all subpartitions is required, the
same calls shown in Example 10-28 and Example 10-29 must be used.
10-24
Chapter 10
Restrictions and Suggestions
10-25
Chapter 10
Restrictions and Suggestions
10.6.6 Performance
The cost of execution of the queries remains the same with the extensible optimizer if
the same plan is chosen. If a different plan is chosen, the execution time should be
better assuming that the user-defined cost, selectivity, and statistics collection
functions are accurate. In light of this, you are strongly encouraged to provide statistics
collection, selectivity, and cost functions for user-defined structures because the
optimizer defaults can be inaccurate and lead to an expensive execution plan.
10-26
11
Using Cartridge Services
Consider how to use cartridge services.
• Introduction to Cartridge Services
• Cartridge Handle
• Memory Services
• Maintaining Context
• Globalization Support
• Parameter Manager Interface
• File I/O
• String Formatting
Portability
Oracle Cartridge Services offers you the flexibility to work across different machine
architectures
Language Independence
The use of the Globalization Support services lets you internationalize your cartridge.
Language independence means that you can have different instances of your cartridge
operating in different language environments.
Guaranteed Compatibility
Oracle Database is a rapidly evolving technology and it is likely that your clients might be
operating with different releases of Oracle. The cartridge services operate with all versions of
Oracle Database.
11-1
Chapter 11
Cartridge Handle
Environment Handle
The environment handle is either OCIEnv or OCI_HTYPE_ENV. Various cartridge services
are required at the process level when no session is available. The OCIInitialize()
should use the OCI_OBJECT option for cartridge service.
11-2
Chapter 11
Memory Services
The client can also create a user duration. The client has to explicitly start and terminate a
user duration. Thus, the client can control the length of a user duration. Like the predefined
durations, a user duration can be used to specify the allocation duration (for example,
memory chunks are freed at the end of the user duration).
Each user duration has a parent duration. A user duration terminates implicitly when its
parent duration terminates. A parent duration can be call, statement, transaction, session or
any other user duration. Memory allocated in the user duration comes from the heap of its
parent duration.
11-3
Chapter 11
Maintaining Context
The Oracle RDBMS memory manager supports a variety of memory models. Currently
callouts support memory for the duration of that callout. With the extension of row
sources to support external indexing, there is a need for memory of durations greater
than a callout.
The following functionality is supported:
• Allocate (permanent and friable) memory of following durations
– call to agent process
– statement
– session
– shared attributes (metadata) for cartridges
• Ability to re-allocate memory
• Ability to create a subduration memory, a sub heap which gets freed up when the
parent heap gets freed up. Memory for this sub heap can be allocated and freed.
• Ability to specify zeroed memory
• Ability to allocate large contiguous memory
11.4.1 Durations
There are various predefined types of durations supported on memory and context
management calls. An additional parameter in all these calls is a context.
• OCI_DURATION_CALL. The duration of this operation is that of a callout.
• OCI_DURATION_STATEMENT. The duration of this operation is the external row
source.
11-4
Chapter 11
Globalization Support
11-5
Chapter 11
Parameter Manager Interface
A value can be a string, integer, OCINumber, or Boolean. A boolean value starting with
'y' or 't' maps to TRUE and a boolean value starting with 'n' or 'f' maps to FALSE. The
matching for boolean values is case insensitive.
The parameter manager views certain characters as special characters which are not
parsed literally. The special characters and their meanings are indicated in Table 11-1.
Character Description
# Comment (only for files)
( Start a list of values
) End a list of values
" Start or end of quoted string
' Start or end of quoted string
= Separator of keyword and value
\ Escape character
11-6
Chapter 11
Parameter Manager Interface
If a special character must be treated literally, then it must either be prefaced by the escape
character or the entire string must be surrounded by single or double quotes.
A key string can contain alphanumeric characters only. A value can contain any characters.
However, the value cannot contain special characters unless they are quoted or escaped.
It is possible to retrieve all parameters at the same time. The function OCIExtractToList()
must first be called to generate a list of parameters that is created from the parameter
structures stored in memory. OCIExtractToList() returns the number of unique keys stored
in memory, and then OCIExtractFromList() can be called to return the list of values
associated with each key.
11-7
Chapter 11
File I/O
11-8
12
Using User-Defined Aggregate Functions
User-defined aggregate functions may be used both singly and in parallel; consider large
aggregation contexts and materialized views.
See Also:
User-Defined Aggregate Functions Interface for a detailed description of the
ODCIAggregate interface.
12-1
Chapter 12
Overview of User-Defined Aggregate Functions
To perform this computation, the aggregate function AVG() goes through these steps:
1. Initializes the computation by initializing the aggregation context, or the rows over
which aggregation is performed:
runningSum = 0; runningCount = 0;
2. Iteratively processes each successive input value and updates the context:
runningSum += inputval; runningCount++;
3. [Optional] Merge by combining the two aggregation contexts and return a single
context. This operation combines the results of aggregation over subsets to obtain
the aggregate over the entire set. This extra step can be required during either
serial or parallel evaluation of an aggregate. If needed, it is performed before step
4:
runningSum = runningSum1 + runningSum2;
runningCount = runningCount1 + runningCount2
12-2
Chapter 12
Creating a User-Defined Aggregate
return (runningSum/runningCount);
If AVG() were a user-defined function, the object type that embodies it would implement a
method for a corresponding ODCIAggregate routine for each of these steps. The variables
runningSum and runningCount, which determine the state of the aggregation in the example,
would be attributes of that object type.
12-3
Chapter 12
Using a User-Defined Aggregate
See Also:
Oracle Database Data Warehousing Guide for information about GROUP BY
extensions such as ROLLUP, CUBE and grouping sets
12-4
Chapter 12
Evaluating User-Defined Aggregates in Parallel
Initialize Iterate
Merge
You should note that the aggregate function must be declared to be parallel-enabled, as
shown in Example 12-6:
Example 12-6 Parallel-Enabling a User-Defined Aggregate Function
CREATE FUNCTION MyUDAG(...) RETURN ...
PARALLEL_ENABLE AGGREGATE USING MyAggrRoutines;
Usually you should use ODCIAggregateInitialize() to allocate the memory to hold the
aggregation context and store the reference to it in the implementation type instance. In
subsequent calls, the external memory and the aggregation context that it contains can be
accessed using the reference. The external memory should usually be freed in
ODCIAggregateTerminate(). ODCIAggregateMerge() should free the external memory used
12-5
Chapter 12
Handling Large Aggregation Contexts
to store the merged context (the second argument of ODCIAggregateMerge() after the
merge is finished.
See Also:
• ODCIAggregateInitialize()
• ODCIAggregateTerminate()
• ODCIAggregateMerge()
12-6
Chapter 12
Handling Large Aggregation Contexts
Note:
ODCIAggregateWrapContext()
12-7
Chapter 12
Handling Large Aggregation Contexts
If the new aggregation context is a superset of the old one, then it contains all the rows
from the old context and no rows must be deleted. Oracle then reuses the old context
even if ODCIAggregateDelete() is not implemented.
See Also:
12-8
Chapter 12
Using Materialized Views with User-Defined Aggregates
See Also:
• ODCIAggregateInitialize()
• ODCIAggregateMerge()
• ODCIAggregateTerminate()
See Also:
Oracle Database Data Warehousing Guide for information about materialized views
12-9
Chapter 12
Creating and Using a User-Defined Aggregate Function
12-10
Chapter 12
Creating and Using a User-Defined Aggregate Function
4. Use SecondMax():
SELECT SecondMax(salary), department_id
FROM MyEmployees
GROUP BY department_id
HAVING SecondMax(salary) > 9000;
12-11
13
Using Pipelined and Parallel Table Functions
The table functions and the generic datatypes ANYTYPE, ANYDATA, and ANYDATASET are often
used with table functions.
• Overview of Table Functions
• Table Function Concepts
• Pipelined Table Functions
• Parallel Table Functions
• Input Data Streaming for Table Functions
• Creating Domain Indexes in Parallel
• Transient and Generic Types
A table function can take a collection of rows as input. An input collection parameter can be
either a collection type or a REF CURSOR.
Table function may be executed in parallel, and returned rows can be streamed directly to the
next process without intermediate staging. Rows from a collection returned by a table
function can also be pipelined; this means that they are iteratively returned as they are
produced, instead of being returned in a single batch after all processing of the table
function's input is completed.
Streaming, pipelining, and parallel execution of table functions can improve performance in
the following manner:
• By enabling multithreaded, concurrent execution of table functions
• By eliminating intermediate staging between processes
• By improving query response time: With non-pipelined table functions, the entire
collection returned by a table function must be constructed and returned to the server
before the query can return a single result row. Pipelining enables rows to be returned
iteratively, as they are produced. This also reduces the memory that a table function
requires, as the object cache does not have to materialize the entire collection.
• By iteratively providing result rows from the collection returned by a table function as the
rows are produced instead of waiting until the entire collection is staged in tables or
memory and then returning the entire collection
Figure 13-1 shows a typical data-processing scenario in which data goes through several (in
this case, three) transformations, implemented by table functions, before finally being loaded
into a database. In this scenario, the table functions are not run in parallel, and the entire
result collection must be staged after each transformation.
13-1
Chapter 13
Table Function Concepts
DSS
T3
Stage 2
T2
Stage 1
T1
OLTP
By contrast, Figure 13-2 shows how streaming and parallel execution can streamline
the same scenario.
T1 T2 T3
OLTP T1 T2 T3 Data
Warehouse
T1 T2 T3
The query in Example 13-4 returns all the catalogs and their corresponding book
listings.
13-2
Chapter 13
Table Function Concepts
13-3
Chapter 13
Table Function Concepts
Input data for the table function might come from a source such as table StockTable:
CREATE TABLE StockTable (
ticker VARCHAR(4),
openprice NUMBER,
closeprice NUMBER
);
In the preceding query, the pipelined table function StockPivot fetches rows from the
CURSOR subquery SELECT * FROM StockTable, performs the transformation, and
pipelines the results back to the user as a table. The function produces two output
rows (collection elements) for each input row.
Note that when a CURSOR subquery is passed from SQL to a REF CURSOR function
argument as in the preceding example, the referenced cursor is open when the
function begins executing.
Note also that cursor operations are not allowed for REF CURSOR variables based on
table functions: SELECT FOR UPDATE, and WHERE CURRENT OF.
See Also:
Pipelined Table Functions: Interface Approach Example for a complete
implementation of this table function using the interface approach, in both C
and Java.
13-4
Chapter 13
Table Function Concepts
);
Example 13-6 Using a Pipelined Table Function with REF CURSOR Arguments
SELECT * FROM TABLE(StockPivot(CURSOR(SELECT * FROM StockTable)));
is parallelized if f is a pure function. The SQL executed by a child scan process is similar to:
SELECT f(col1) FROM tab WHERE ROWID BETWEEN :b1 AND :b2;
Each child scan operates on a range of rowids and applies function f to each contained row.
Function f is then executed by the scan processes; it does not run independently of them.
Unlike a function that appears in the SELECT list, a table function is called in the FROM clause
and returns a collection. This affects the way that table function input data is partitioned
among child scans because the partitioning approach must be appropriate for the operation
that the table function performs. (For example, an ORDER BY operation requires input to be
range-partitioned, whereas a GROUP BY operation requires input to be hash partitioned.)
A table function itself specifies in its declaration the partitioning approach that is appropriate
for it, as described in "Input Data Partitioning". The function is then executed in a two-stage
operation. First, one set of child processes partitions the data as directed in the function's
declaration; then a second set of child scans executes the table function in parallel on the
partitioned data. The table function in the following query has a REF CURSOR parameter:
SELECT * FROM TABLE(f(CURSOR(SELECT * FROM tab)));
The scan is performed by one set of child processes, which redistributes the rows (based on
the partitioning method specified in the function declaration) to a second set of child
processes that actually executes function f in parallel.
13-5
Chapter 13
Pipelined Table Functions
Example 13-7 shows declarations of pipelined table functions implemented using the
interface approach. The interface routines for functions GetBooks and StockPivot
have been implemented in the types BookMethods and StockPivotImpl, respectively.
Example 13-8 shows declarations of the same table functions implemented using the
native PL/SQL approach.
13-6
Chapter 13
Pipelined Table Functions
Example 13-7 Declaring Pipelined Table Functions for the Interface Approach
CREATE FUNCTION GetBooks(cat CLOB) RETURN BookSet_t PIPELINED USING BookMethods;
Example 13-8 Declaring Pipelined Table Functions for the Native PL/SQL Approach
CREATE FUNCTION GetBooks(cat CLOB) RETURN BookSet_t PIPELINED IS ...;
Example 13-9 Implementing a Pipelined Table Function for the Native PL/SQL
Approach
CREATE FUNCTION StockPivot(p refcur_pkg.refcur_t) RETURN TickerTypeSet
PIPELINED IS
out_rec TickerType := TickerType(NULL,NULL,NULL);
in_rec p%ROWTYPE;
BEGIN
LOOP
FETCH p INTO in_rec;
EXIT WHEN p%NOTFOUND;
-- first row
out_rec.ticker := in_rec.Ticker;
out_rec.PriceType := 'O';
out_rec.price := in_rec.OpenPrice;
PIPE ROW(out_rec);
-- second row
out_rec.PriceType := 'C';
out_rec.Price := in_rec.ClosePrice;
PIPE ROW(out_rec);
END LOOP;
CLOSE p;
RETURN;
END;
/
13-7
Chapter 13
Pipelined Table Functions
Parallel execution works similarly, except that each function executes in a different
process or set of processes.
Example 13-10 Pipelining Function Results from One Function to Another
SELECT * FROM TABLE(f(CURSOR(SELECT * FROM TABLE(g()))));
• ODCITableDescribe() determines the structure of the data type the table function
returns, in situations where this cannot be defined in a static manner.
• ODCITablePrepare() initializes the scan context parameter. If this method is
implemented, the scan context it prepares is passed to the ODCITableStart()
routine, and the context is maintained between restarts of the table function. It also
provides projection information and supports the return of transient anonymous
types.
• Scan Context
• Start Routine
13-8
Chapter 13
Pipelined Table Functions
• Fetch Routine
• Close Routine
• Describing Returned Data Sructures; Describe Method
• Preparing a Query for Execution; Prepare Method
See Also:
• ODCITableClose()
• ODCITableDescribe()
• ODCITableFetch()
• ODCITablePrepare()
• ODCITableStart()
Note that any REF CURSOR arguments of a table function must be declared as SYS_REFCURSOR
type in the declaration of the ODCITableStart(). Ordinary REF CURSOR types cannot be used
as formal argument types in ODCITableStart(). Ordinary REF CURSOR types can only be
declared in a package, and types defined in a package cannot be used as formal argument
types in a type method. To use a REF CURSOR type in ODCITableStart(), you must use the
system-defined SYS_REFCURSOR type.
See Also:
• ODCITablePrepare()
• ODCITableStart()
13-9
Chapter 13
Pipelined Table Functions
See Also:
ODCITableFetch()
13-10
Chapter 13
Pipelined Table Functions
ODCITableStart
ODCITableFetch
Yes Is result
null?
No
Process Result
ODCITableClose
The following example shows a table function declared to return an AnyDataSet collection
whose structure is not fixed at function creation time:
CREATE FUNCTION AnyDocuments(VARCHAR2) RETURN ANYDATASET
PIPELINED USING DocumentMethods;
You can implement a ODCITableDescribe() routine to determine the format of the elements
in the result collection when the format depends on the actual parameters to the table
function. ODCITableDescribe() is invoked by Oracle at query compilation time to retrieve the
specific type information. Typically, the routine uses the user arguments to determine the
shape of the return rows. The format of elements in the returned collection is conveyed to
Oracle by returning an instance of AnyType.
The AnyType instance specifies the actual structure of the returned rows of the specific query.
Like AnyDataSet, AnyType has an associated set of PL/SQL and C interfaces with which to
construct and access the metadata information.
The query in Example 13-11, for an AnyDocuments function, returns information on either
books or magazines.
Example 13-12 is an implementation of the ODCITableDescribe() method, which consults the
DTD of the XML documents at the specified location to return the appropriate AnyType value,
13-11
Chapter 13
Pipelined Table Functions
Because the ODCITableDescribe() method is called at compile time, the table function
should have at least one argument that has a value at compile time, like a constant. By
using the table function with different arguments, you can get different return types
from the function, as demonstrated in Example 13-13.
The ODCITableDescribe() functionality is available only if the table function is
implemented using the interface approach. A native PL/SQL implementation of a table
function that returns ANYDATASET returns rows whose structure is opaque to the server.
13-12
Chapter 13
Pipelined Table Functions
See Also:
• ODCITableDescribe()
• "Transient and Generic Types" for information on AnyDataSet and AnyType
13-13
Chapter 13
Pipelined Table Functions
See Also:
• ODCITableClose()
• ODCITablePrepare()
• ODCITableStart()
13-14
Chapter 13
Pipelined Table Functions
However, the SQL optimizer does not optimize across PL/SQL statements; therefore,
Example 13-19 runs better than Example 13-18.
Additionally, Example 13-18 is slower because of the overhead associated with executing two
SQL statements, and because it does not take advantage of efficiencies realized by pipelining
results between two functions, as Example 13-19 does.
Example 13-17 Defining REF CURSOR Variables for Table Function Queries
OPEN c FOR SELECT * FROM TABLE(f(...));
However, you can create a view over a table function and use INSTEAD OF triggers to update
it, as in Example 13-21.
Example 13-22 demonstrates how an INSTEAD OF trigger is fired when the user inserts a row
into the BookTable view:.
INSTEAD OF triggers can be defined for all DML operations on a view built on a table function.
13-15
Chapter 13
Parallel Table Functions
13-16
Chapter 13
Parallel Table Functions
You can pass table function return values to other table functions by creating a REF CURSOR
that iterates over the returned data, as demonstrated in Example 13-27.
Example 13-25 Passing a Set of Rows to a PL/SQL Function Through REF CURSOR
CREATE FUNCTION g(p1 pkg.refcur_t1, p2 pkg.refcur_t2) RETURN...
PIPELINED ... ;
Example 13-26 Invoking a Function that Uses Several REF CURSOR Parameters
SELECT * FROM TABLE(g(CURSOR(SELECT empno FROM tab),
CURSOR(SELECT * FROM emp));
Example 13-27 Using REF CURSOR to Pass Return Values Between Table Functions
SELECT * FROM TABLE(f(CURSOR(SELECT * FROM TABLE(g(...)))));
13-17
Chapter 13
Parallel Table Functions
converted back to a REF CURSOR on the way out. (The inbound and outbound
statement handles may be different.)
If a REF CURSOR type is used as an OUT argument, or a return type to a call, then the
call must return the statement handle, which are converted to a REF CURSOR for the
caller, as demonstrated in Example 13-28.
If the function is written as a Java call, the IN REF CURSOR argument is automatically
converted to an instance of the Java ResultSet class. The IN REF CURSOR to
ResultSet mapping is available only if you use a thick JDBC driver based on OCI. This
mapping is not available for a thin JDBC driver. As with an executed statement handle
in a C call, when a REF CURSOR is either an IN OUT argument, an OUT argument, or a
return type for the function, a Java ResultSet is converted back to a PL/SQL REF
CURSOR on its way out to the caller.
13-18
Chapter 13
Parallel Table Functions
/* Fetch loop */
while ((status = OCIStmtFetch(*stmthp, errhp, (ub4) 1, (ub4) OCI_FETCH_NEXT,
(ub4) OCI_DEFAULT)) == OCI_SUCCESS ||
status == OCI_SUCCESS_WITH_INFO)
{
printf("val=%lf\n",num);
}
return 0;
}
The ANY keyword enables you to indicate that the function behavior is independent of the
partitioning of the input data. When this keyword is used, the run-time system randomly
partitions the data among the slaves. This keyword is appropriate for use with functions that
take in one row, manipulate its columns, and generate output row(s) based on the columns of
this row only.
To demonstrate, the pivot-like function StockPivot() in Example 13-31 takes as input a row
of the type (Ticker varchar(4), OpenPrice number, ClosePrice number), and generates
rows of the type (Ticker varchar(4), PriceType varchar(1), Price number). Thus, the
row ("ORCL", 41, 42) generates two rows: ("ORCL", "O", 41) and ("ORCL", "C", 42).
You can use directly TickerTypeSet, as created in Example 13-5, instead of rec_tab_type or
define it the same way.
The function f() may be used to generate another table from Stocks table, as shown in
Example 13-32.
If StockTable is scanned in parallel and partitioned on OpenPrice, then the function
StockPivot() is combined with the data-flow operator that scans StockTable and therefore
sees the same partitioning.
If StockTable is not partitioned, and the scan on it does not execute in parallel, the insert into
AlternateStockTable also runs sequentially, as demonstrated in Example 13-33.
If function g() runs in parallel and is partitioned by ANY, then the parallel insert can belong in
the same data-flow operator as g().
Whenever the ANY keyword is specified, the data is partitioned randomly among the slaves.
This effectively means that the function is executed in the same child set which does the scan
associated with the input parameter.
13-19
Chapter 13
Parallel Table Functions
No redistribution or repartitioning of the data is required here. In the case, when the
cursor p itself is not parallel-enabled, the incoming data is randomly partitioned on the
columns in the column list. The round-robin table queue is used for this partitioning.
If you create an XMLIndex index on a partitioned XMLType table, or a partitioned table
with an XMLType column using range, list, or hash partitioning, that index is
equipartitioned with the base table.
Example 13-30 Specifying Data Partitioning for a REF CURSOR Parameter
CREATE FUNCTION f(p ref_cursor_type) RETURN rec_tab_type PIPELINED
PARALLEL_ENABLE(PARTITION p BY [{HASH | RANGE} (column_list) | ANY ]) IS
BEGIN ... END;
BEGIN
FOR rec IN p LOOP
ret_rec.Ticker := rec.Ticker;
ret_rec.PriceType := "O";
ret_rec.Price := rec.OpenPrice;
PIPE ROW(ret_rec);
END;
Example 13-32 Using a REF CURSOR to Generate a Table from Another Table
INSERT INTO AlternateStockTable
SELECT * FROM
TABLE(StockPivot(CURSOR(SELECT * FROM StockTable)));
13-20
Chapter 13
Input Data Streaming for Table Functions
For example, the following function reads a set of external files in parallel and returns the
records they contain. To provide work for a REF CURSOR, you might first create a table and
populate it with the filenames. A REF CURSOR over this table can then be passed as a
parameter to the table function readfiles(), as demonstrated by Example 13-34.
Example 13-35 illustrates the syntax for ordering the input stream. In the example, function
f() takes in rows of the kind (Region, Sales) and returns rows of the form (Region,
AvgSales), showing average sales for each region.
13-21
Chapter 13
Input Data Streaming for Table Functions
13-22
Chapter 13
Creating Domain Indexes in Parallel
memory into SmallSum[department_id]. On the other hand, if the number of unique values of
department_id were very large, you would want to use clustering to compute department
aggregates and write them to disk one department_id at a time.
The par_degree value can be explicitly specified; otherwise, it is derived from the parallel
degree of the base table.
The function IndexMerge(), defined in Example 13-38, is needed to merge the results from
the several instances of IndexLoad().
1. Create metadata structures for the index (tables to store the index data).
2. Explicitly commit the transaction so that the IndexLoad() function can access the
committed data.
3. Invoke IndexLoad() in parallel, as shown in the following code example.
Invoking the Merging of Parallel Domain Index Loads
status := ODCIIndexMerge(CURSOR(
SELECT * FROM TABLE(ODCIIndexLoad(ia, parms, CURSOR(
SELECT key_cols, ROWID FROM basetable)))))
13-23
Chapter 13
Transient and Generic Types
See Also:
ODCIIndexCreate()
Example 13-38 Merging the Results from Parallel Domain Index Loads
CREATE FUNCTION IndexMerge(p refcur-type)
RETURN NUMBER
IS
BEGIN
FOR rec IN p LOOP
IF (rec != ODCIConst.Success)
RETURN Error;
END LOOP;
RETURN Success;
END;
13-24
Chapter 13
Transient and Generic Types
Type Description
SYS.ANYTYPE A type description type. A SYS.ANYTYPE can contain a type description
of any SQL type, named or unnamed, including object types and
collection types.
An ANYTYPE can contain a type description of a persistent type, but an
ANYTYPE itself is transient: the value in an ANYTYPE itself is not
automatically stored in the database. To create a persistent type, use a
CREATE TYPE statement from SQL.
SYS.ANYDATA A self-describing data instance type. A SYS.ANYDATA contains an
instance of a given type, with data, plus a description of the type. In this
sense, a SYS.ANYDATA is self-describing. An ANYDATA can be
persistently stored in the database.
SYS.ANYDATASET A self-describing data set type. A SYS.ANYDATASET type contains a
description of a given type plus a set of data instances of that type. An
ANYDATASET can be persistently stored in the database.
Each of these three types can be used with any built-in type native to the database with
object types and collection types, both named and unnamed. The types provide a generic
way to work dynamically with type descriptions, lone instances, and sets of instances of other
types. Using the APIs, you can create a transient ANYTYPE description of any kind of type.
Similarly, you can create or convert (cast) a data value of any SQL type to an ANYDATA and
can convert an ANYDATA (back) to a SQL type. And similarly again with sets of values and
ANYDATASET.
The generic types simplify working with stored procedures. You can use the generic types to
encapsulate descriptions and data of standard types and pass the encapsulated information
into parameters of the generic types. In the body of the procedure, you can detail how to
handle the encapsulated data and type descriptions of whatever type.
You can also store encapsulated data of a variety of underlying types in one table column of
type ANYDATA or ANYDATASET. For example, you can use ANYDATA with advanced queuing to
model queues of heterogeneous types of data. You can query the data of the underlying data
types like any other data.
Note, however, that ANYDATA and ANYDATASET objects that were created on a transient type,
such as unnamed type constructed through an ANYTYPE API, cannot be stored persistently in
an ANYDATA or ANYDATASET table column.
Corresponding to the three generic SQL types are three OCI types that model them. Each
has a set of functions for creating and accessing the respective type:
• OCIType, corresponding to SYS.ANYTYPE
• OCIAnyData, corresponding to SYS.ANYDATA
• OCIAnyDataSet, corresponding to SYS.ANYDATASET
13-25
Chapter 13
Transient and Generic Types
See Also:
13-26
14
Designing Data Cartridges
There are various items for you to consider when designing data cartridges.
• Choosing the Programming Language
• Invoker's Rights
• Callouts and LOBs
• Saving and Passing State
• Designing Indexes
• Designing Operators
• Designing for the Extensible Optimizer
• Designing for Maintenance
• Enabling Cartridge Installation
• Designing for Portability
14-1
Chapter 14
Callouts and LOBs
See Also:
Oracle Database PL/SQL Language Reference
14-2
Chapter 14
Designing Indexes
The external index structure gives you the most flexibility in representing your index. An
external index structure is particularly useful if you have invested in the development of in-
memory indexing structures. For example, an operating system file may store index data,
which is read into a memory mapped file at run time. Such a case can be handled as a BFILE
in the external index routines.
External index structures may also provide superior performance, although this gain comes at
some cost. Index structures external to the database do not participate in the transaction
semantics of the database, which, in the case of index structures inside the database, make
data and concomitant index updates atomic. This means that if an update to the data causes
14-3
Chapter 14
Designing Operators
an update for the external index to be invoked through the extensible indexing
interface, failures can cause the data updates to be rolled back but not the index
updates. The database can only roll back what is internal to it: external index
structures cannot be rolled back in synchronization with a database rollback. External
index structures are perhaps most useful for read-only access. Their semantics
become complex if updates to data are involved.
As the cartridge designer, you are in the best position to make a judgement regarding
the number of rows to be returned. For example, if in the index 1500 rowids are stored
together, and nrows = 2000, then it may be optimal to return 1500 rows instead of 2000
rows. Otherwise, the user would have to retrieve 3000 rowids, return 2000 of them, and
note which 1000 rowids were not returned.
If you do not have any specific optimization in mind, you can use the value of nrows to
determine the number of rows to be returned. Currently the value of nrows has been
set to 2000.
If you implement indexes that use callouts, use multirow fetch to fetch the largest
number of rows back to the server. This offsets the cost of making the callout.
See Also:
ODCIIndexFetch()
14-4
Chapter 14
Designing for the Extensible Optimizer
14-5
Chapter 14
Designing for Maintenance
See Also:
Oracle Database Advanced Application Developer's Guide for information on
setting up the listener and extproc
14-6
Chapter 14
Designing for Portability
14-7
Part III
Scenarios and Examples
Consider the following examples that illustrate the techniques described in Building Data
Cartridges:
• Power Demand Cartridge Example
• PSBTREE: Extensible Indexing Example
• Pipelined Table Functions: Interface Approach Example
• Power Demand Cartridge Example
• PSBTREE: Extensible Indexing Example
• Pipelined Table Functions: Interface Approach Example
15
Power Demand Cartridge Example
The power demand cartridge in this example includes a user-defined object type, extensible
indexing, and optimization. The entire cartridge definition is available online in file
extdemo1.sql in the Oracle demo directory.
• Feature Requirements
• Modeling the Application
• Queries and Extensible Indexing
• Creating the Domain Index
• Defining Types and Methods for Extensible Optimizing
• Testing the Domain Index
See Also:
15-1
Chapter 15
Feature Requirements
Vicksburg Clinton
This region may be surrounded by other regions some of whose power needs are
supplied by other utilities. As pictured, every region is composed of geographic
quadrants, called cells, on a 10x10 grid. There are several ways of identifying cells —
by spatial coordinates (longitude/latitude), by a matrix numbering (1,1; 1,2;...), and by
numbering them sequentially, as illustrated in Figure 15-2.
15-2
Chapter 15
Feature Requirements
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
Within the area represented by each cell, the power used by consumers in that area is
recorded each hour. For example, the power demand readings for a particular hour might be
represented by Table 15-1 (cells here represented on a matrix).
- 1 2 3 4 5 6 7 8 9 10
1 23 21 25 23 24 25 27 32 31 30
2 33 32 31 33 34 32 23 22 21 34
3 45 44 43 33 44 43 42 41 45 46
4 44 45 45 43 42 26 19 44 33 43
5 45 44 43 42 41 44 45 46 47 44
6 43 45 98 55 54 43 44 33 34 44
7 33 45 44 43 33 44 34 55 46 34
8 87 34 33 32 31 34 35 38 33 39
9 30 40 43 42 33 43 34 32 34 46
10 43 42 34 12 43 45 48 45 43 32
The power stations also receives reports from two other sources:
• Sensors on the ground provide temperature readings for every cell
15-3
Chapter 15
Feature Requirements
By analyzing the correlation between historical power demand from cells and the
temperature readings for those regions, the utility is able to determine with a close
approximation the expected demand, given specific temperatures.
• Satellite cameras provide images regarding current conditions that are converted
into grayscale images that match the grid illustrated in Figure 15-3.
These images are designed so that lighter is colder. Thus, the image shows a cold
front moving into the region from the south-west. By correlating the data provided by
the grayscale images with temperature readings taken at the same time, the utility has
been able to determine what the power demand is given weather conditions viewed
from the stratosphere.
The reason that this is important is that a crucial part of this modeling has to do with
noting the rapidity and degree of change in the incoming reports as weather changes
and power is deployed. The following diagram shows same cold front at a second
recording, illustrated in Figure 15-4.
15-4
Chapter 15
Feature Requirements
By analyzing the extent and speed of the cold front, the utility is able to project what the
conditions are likely to be in the short and medium term, as in Figure 15-5.
15-5
Chapter 15
Feature Requirements
By combing the data about these conditions and other anomalous situations (such as
the failure of a substation), the utility must be able to organize the most optimal
deployment of its resources. Figure 15-6 reflects the distribution of substations across
the region.
The distribution of power stations means that the utility can redirect its deployment of
electricity to the areas of greatest need. Figure 15-7 gives a pictorial representation of
the overlap between three stations.
15-6
Chapter 15
Modeling the Application
Depending on fluctuating requirements, the utility must be able to decide how to deploy its
resources, and even whether to purchase power from another utility in the event of shortfall.
15-7
Chapter 15
Modeling the Application
Meter
1
has
1
Hourly Demand Status
isEqualToSpecificCell
getMaxCellDemand
getMinCellDemand
reads
TotalGridDemand
isEqualToAnyCell
MaxCellDemand
MinCellDemand
Grid Coordinate
demand
has for has
1
100
2
Demand
CellNo
CellNo
Time
Date
Cell
x
y
*
1
is associated with
retrieves, stores
reads
temperature
for
provides
matching
image
has
1
Cell Temperature Reading
1
1
Power Cartridge User
GreyScaleValue
Satellite Image
Temperature
Regional Grid
Grid No
Locator
NW
SW
NE
SE
photographs
1 senses 1
1
*
Camera
• Sample Queries Sensor
15-8
Chapter 15
Modeling the Application
Meter
has
Hourly Demand Status
isEqualToSpecificCell
getMaxCellDemand
getMinCellDemand
reads
TotalGridDemand
isEqualToAnyCell
MaxCellDemand
MinCellDemand
Grid Coordinate
demand
has for has
1
1
100
2
Demand
CellNo
CellNo
Time
Date
Cell
x
y
*
*
is associated with
retrieves, stores
has
1
1
Power Cartridge User
Regional Grid
Grid No
Locator
NW
SW
NE
SE
15-9
Chapter 15
Queries and Extensible Indexing
The utility receives ongoing reports from weather centers about current conditions and
from power stations about ongoing power utilization for specific geographical areas
(represented by cells on a 10x10 grid). It then compares this information to historical
data so it may predict demand for power in the different geographic areas for given
time periods.
Each service area for the utility is considered as a 10x10 grid of cells, where each
cell's boundaries are associated with spatial coordinates (longitude/latitude). The
geographical areas represented by the cells can be uniform or can have different
shapes and sizes. Within the area represented by each cell, the power used by
consumers in that area is recorded each hour. For example, the power demand
readings for a particular hour might be represented by Table 15-2.
- 1 2 3 4 5 6 7 8 9 10
1 23 21 25 23 24 25 27 32 31 30
2 33 32 31 33 34 32 23 22 21 34
3 45 44 43 33 44 43 42 41 45 46
4 44 45 45 43 42 26 19 44 33 43
5 45 44 43 42 41 44 45 46 47 44
6 43 45 98 55 54 43 44 33 34 44
7 33 45 44 43 33 44 34 55 46 34
8 87 34 33 32 31 34 35 38 33 39
9 30 40 43 42 33 43 34 32 34 46
10 43 42 34 12 43 45 48 45 43 32
The numbers in each cell reflect power demand (in some unit of measurement
determined by the electric utility) for the hour for that area. For example, the demand
for the first cell (1,1) was 23, the demand for the second cell (1,2) was 21, and so on.
The demand for the last cell (10, 10) was 32.
The utility uses this data for many monitoring and analytical applications. Readings for
individual cells are monitored for unusual surges or decreases in demand. For
example, the readings of 98 for (6,3) and 87 for (8,1) might be unusually high, and the
readings of 19 for (4,7) and 12 for (10,4) might be unusually low. Trends are also
analyzed, such as significant increases or decreases in demand for each
neighborhood, for each station, and overall, over time.
15-10
Chapter 15
Queries and Extensible Indexing
Examples of queries that would not benefit from extensible indexing (using the power
demand cartridge) include:
• Find the cell with the highest power demand for a specific time.
• Find the time when the total grid power demand was highest.
• Find all cells where the power demand is greater than a specified value.
• Find the times for which the average cell demand or the median cell demand was greater
than a specified value.
To make this query run efficiently, define two additional columns in the PowerDemand_Typ
object type (AverageCellDemand and MedianCellDemand), and create functions to set the
values of these columns. (For example, AverageCellDemand is TotGridDemand divided by
100.) Then, create b-tree indexes on the AverageCellDemand and MedianCellDemand
columns.
15-11
Chapter 15
Creating the Domain Index
Example 15-1 Creating a Database User for the Power Demand Cartridge
set echo on
connect sys/knl_test7 as sysdba;
drop user PowerCartUser cascade;
create user PowerCartUser identified by PowerCartUser;
-------------------------------------------------------------------
-- INITIAL SET-UP
-------------------------------------------------------------------
-- grant privileges --
grant connect, resource to PowerCartUser;
grant create operator to PowerCartUser;
grant create indextype to PowerCartUser;
grant create table to PowerCartUser;
15-12
Chapter 15
Creating the Domain Index
15-13
Chapter 15
Creating the Domain Index
15-14
Chapter 15
Creating the Domain Index
Power_EqualsSpecific() Power_EqualsSpecific_Func()
Power_EqualsAny() Power_EqualsAny_Func()
Power_LessThanSpecific() Power_LessThanSpecific_Func()
Power_LessThanAny() Power_LessThanAny_Func()
Power_GreaterThanSpecific() Power_GreaterThanSpecific_Func()
Power_GreaterThanAny() Power_GreaterThanAny_Func()
Each function and operator returns a numeric value of 1 if the condition is true (for example, if
the specified cell is equal to the specified value), 0 if the condition is not true, or null if the
specified cell number is invalid.
The statements in Example 15-4 create the implementing functions, Power_xxx_Func(), first
the specific and then the any implementations.
15-15
Chapter 15
Creating the Domain Index
The statements in Example 15-5 create the operators (Power_xxx). Each statement
specifies an implementing function.
• Creating Functions and Operators
15-16
Chapter 15
Creating the Domain Index
/
CREATE FUNCTION Power_EqualsAny_Func(
object PowerDemand_Typ, value NUMBER)
RETURN NUMBER AS
idx NUMBER;
BEGIN
FOR idx IN object.CellDemandValues.FIRST..object.CellDemandValues.LAST LOOP
IF (object.CellDemandValues(idx) = value) THEN
RETURN 1;
END IF;
END LOOP;
RETURN 0;
END;
/
CREATE FUNCTION Power_GreaterThanAny_Func(
object PowerDemand_Typ, value NUMBER)
RETURN NUMBER AS
idx NUMBER;
BEGIN
FOR idx IN object.CellDemandValues.FIRST..object.CellDemandValues.LAST LOOP
IF (object.CellDemandValues(idx) > value) THEN
RETURN 1;
END IF;
END LOOP;
RETURN 0;
END;
/
CREATE FUNCTION Power_LessThanAny_Func(
object PowerDemand_Typ, value NUMBER)
RETURN NUMBER AS
idx NUMBER;
BEGIN
FOR idx IN object.CellDemandValues.FIRST..object.CellDemandValues.LAST LOOP
IF (object.CellDemandValues(idx) < value) THEN
RETURN 1;
END IF;
END LOOP;
RETURN 0;
END;
/
15-17
Chapter 15
Creating the Domain Index
Method Description
ODCIGetInterfaces() Returns the list interface names implemented by the type.
ODCIIndexCreate() Creates a table to store index data. If the base table containing
data to be indexed is not empty, this method builds the index for
existing data.
This method is called when a CREATE INDEX statement is
issued that refers to the indextype. Upon invocation, any
parameters specified in the PARAMETERS clause are passed in
along with a description of the index.
ODCIIndexDrop() Drops the table that stores the index data. This method is called
when a DROP INDEX statement specifies the index.
ODCIIndexStart() Initializes the scan of the index for the operator predicate. This
method is invoked when a query is submitted involving an
operator that can be executed using the domain index.
ODCIIndexFetch() Returns the ROWID of each row that satisfies the operator
predicate.
ODCIIndexClose() Ends the current use of the index. This method can perform any
necessary clean-up.
ODCIIndexInsert() Maintains the index structure when a record is inserted in a
table that contains columns or object attributes indexed by the
indextype.
ODCIIndexDelete() Maintains the index structure when a record is deleted from a
table that contains columns or object attributes indexed by the
indextype.
ODCIIndexUpdate() Maintains the index structure when a record is updated
(modified) in a table that contains columns or object attributes
indexed by the indextype.
ODCIIndexGetMetadata() Allows the export and import of implementation-specific
metadata associated with the index.
15-18
Chapter 15
Creating the Domain Index
The CREATE TYPE statement is followed by a CREATE TYPE BODY statement that specifies the
implementation for each member function:
CREATE OR REPLACE TYPE BODY power_idxtype_im
IS
...
All the method definitions (except for ODCIIndexGetMetadata(), which returns a VARCHAR2
string) have the following general form:
STATIC FUNCTION function-name (...)
RETURN NUMBER
IS
...
END;
Example 15-6 Creating power_idxtype_im Object Type for Power Demand Cartridge
CREATE OR REPLACE TYPE power_idxtype_im AS OBJECT
(
curnum NUMBER,
STATIC FUNCTION ODCIGetInterfaces(ifclist OUT sys.ODCIObjectList)
RETURN NUMBER,
STATIC FUNCTION ODCIIndexCreate (ia sys.ODCIIndexInfo, parms VARCHAR2,
env sys.ODCIEnv) RETURN NUMBER,
STATIC FUNCTION ODCIIndexDrop(ia sys.ODCIIndexInfo, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIIndexStart(sctx IN OUT power_idxtype_im,
ia sys.ODCIIndexInfo,
op sys.ODCIPredInfo, qi sys.ODCIQueryInfo,
strt NUMBER, stop NUMBER,
cmppos NUMBER, cmpval NUMBER, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIIndexStart(sctx IN OUT power_idxtype_im,
ia sys.ODCIIndexInfo,
op sys.ODCIPredInfo, qi sys.ODCIQueryInfo,
strt NUMBER, stop NUMBER,
cmpval NUMBER, env sys.ODCIEnv)
RETURN NUMBER,
MEMBER FUNCTION ODCIIndexFetch(nrows NUMBER, rids OUT sys.ODCIRidList,
env sys.ODCIEnv) RETURN NUMBER,
MEMBER FUNCTION ODCIIndexClose (env sys.ODCIEnv) RETURN NUMBER,
STATIC FUNCTION ODCIIndexInsert(ia sys.ODCIIndexInfo, rid VARCHAR2,
newval PowerDemand_Typ, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIIndexDelete(ia sys.ODCIIndexInfo, rid VARCHAR2,
oldval PowerDemand_Typ, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIIndexUpdate(ia sys.ODCIIndexInfo, rid VARCHAR2,
oldval PowerDemand_Typ,
newval PowerDemand_Typ, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIIndexGetMetadata(ia sys.ODCIIndexInfo,
expversion VARCHAR2,
newblock OUT PLS_INTEGER,
env sys.ODCIEnv)
RETURN VARCHAR2
);
/
• ODCIGetInterfaces()
15-19
Chapter 15
Creating the Domain Index
• ODCIIndexCreate()
• ODCIIndexDrop()
• ODCIIndexStart(); Specific Queries
• ODCIIndexStart(); Any Queries
• ODCIIndexFetch()
• ODCIIndexClose()
• ODCIIndexInsert()
• ODCIIndexDelete()
• ODCIIndexUpdate()
• ODCIIndexGetMetadata()
15.4.6.1 ODCIGetInterfaces()
The ODCIGetInterfaces() function returns the list of names of the interfaces
implemented by the type. To specify the current version of these interfaces, the
ODCIGetInterfaces() routine must return'SYS.ODCIINDEX2' in the OUT parameter, as
demonstrated in Example 15-7.
Example 15-7 Registering Interface and Index Functions in Power Demand
Cartridge
STATIC FUNCTION ODCIGetInterfaces(
ifclist OUT sys.ODCIObjectList)
RETURN NUMBER IS
BEGIN
ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCIINDEX2'));
return ODCIConst.Success;
END ODCIGetInterfaces;
15.4.6.2 ODCIIndexCreate()
The ODCIIndexCreate() function creates the table to store index data. If the base table
containing data to be indexed is not empty, this method inserts the index data entries
for existing data.
The function takes the index information as an object parameter whose type is
SYS.ODCIINDEXINFO. The type attributes include the index name, owner name, and so
forth. The PARAMETERS string specified in the CREATE INDEX statement is also passed in
as a parameter to the function, as demonstrated in Example 15-8.
Example 15-8 Registering ODCIIndexCreate() for Power Demand Cartridge
STATIC FUNCTION ODCIIndexCreate (
ia sys.ODCIIndexInfo,
parms VARCHAR2,
env sys.ODCIEnv)
RETURN NUMBER IS
i INTEGER;
r ROWID;
p NUMBER;
v NUMBER;
stmt1 VARCHAR2(1000);
stmt2 VARCHAR2(1000);
15-20
Chapter 15
Creating the Domain Index
stmt3 VARCHAR2(1000);
cnum1 INTEGER;
cnum2 INTEGER;
cnum3 INTEGER;
junk NUMBER;
The SQL statement to create the table for the index data is constructed and executed. The
table includes the ROWID of the base table, r, the cell position number (cpos) in the grid from 1
to 100, and the power demand value in that cell (cval).
BEGIN
-- Construct the SQL statement.
stmt1 := 'CREATE TABLE ' || ia.IndexSchema || '.' || ia.IndexName ||'_pidx' ||
'( r ROWID, cpos NUMBER, cval NUMBER)';
The function populates the index by inserting rows into the table. The function "unnests" the
VARRAY attribute and inserts a row for each cell into the table. Thus, each 10 X 10 grid (10
rows, 10 values for each row) becomes 100 rows in the table (one row for each cell).
-- Now populate the table.
stmt2 := ' INSERT INTO '|| ia.IndexSchema || '.' || ia.IndexName || '_pidx' ||
' SELECT :rr, ROWNUM, column_value FROM THE' || ' (SELECT CAST (P.'||
ia.IndexCols(1).ColName||'.CellDemandValues AS NumTab_Typ)'|| ' FROM ' ||
ia.IndexCols(1).TableSchema || '.' || ia.IndexCols(1).TableName || ' P' ||
' WHERE P.ROWID = :rr)';
The function concludes by closing the cursors and returning a success status.
15-21
Chapter 15
Creating the Domain Index
dbms_sql.close_cursor(cnum2);
dbms_sql.close_cursor(cnum3);
RETURN ODCICONST.SUCCESS;
END ODCIInexCreate;
15.4.6.3 ODCIIndexDrop()
The ODCIIndexDrop() function drops the table that stores the index data, as
demonstrated in Example 15-9. This method is called when a DROP INDEX statement is
issued.
Example 15-9 Registering ODCIIndexDrop() for Power Demand Cartridge
STATIC FUNCTION ODCIIndexDrop(ia sys.ODCIIndexInfo, env sys.ODCIEnv)
RETURN NUMBER IS
stmt VARCHAR2(1000);
cnum INTEGER;
junk INTEGER;
BEGIN
-- Construct the SQL statement.
stmt := 'drop table ' || ia.IndexSchema || '.' || ia.IndexName || '_pidx';
dbms_output.put_line('ODCIIndexDrop>>>>>');
sys.ODCIIndexInfoDump(ia);
dbms_output.put_line('ODCIIndexDrop>>>>>'||stmt);
RETURN ODCICONST.SUCCESS;
END ODCIIndexDrop;
The self parameter is the context that is shared with the ODCIIndexFetch() and
ODCIIndexClose() functions. The ia parameter contains the index information as an
object instance of type SYS.ODCIINDEXINFO, and the op parameter contains the
operator information as an object instance of type SYS.ODCIOPERINFO. The strt and
stop parameters are the lower and upper boundary points for the operator return
value. The cmppos parameter is the cell position and cmpval is the value in the cell
specified by the operator Power_XxxxxSpecific(). This is demonstrated in
Example 15-10.
Example 15-10 Registering ODCIIndexStart() for Power Demand Cartridge
STATIC FUNCTION ODCIIndexStart(
sctx IN OUT power_idxtype_im,
ia sys.ODCIIndexInfo,
15-22
Chapter 15
Creating the Domain Index
op sys.ODCIPredInfo,
qi sys.ODCIQueryInfo,
strt NUMBER, stop NUMBER,
cmppos NUMBER,
cmpval NUMBER,
env sys.ODCIEnv )
RETURN NUMBER IS
cnum INTEGER;
rid ROWID;
nrows INTEGER;
relop VARCHAR2(2);
stmt VARCHAR2(1000);
BEGIN
dbms_output.put_line('ODCIIndexStart>>>>>');
sys.ODCIIndexInfoDump(ia);
sys.ODCIPredInfoDump(op);
dbms_output.put_line('start key : '||strt);
dbms_output.put_line('stop key : '||stop);
dbms_output.put_line('compare position : '||cmppos);
dbms_output.put_line('compare value : '||cmpval);
The function generates the SQL statement to be executed. It determines the operator name
and the lower and upper index value bounds (the start and stop keys). The start and stop
keys can both be 1 (= TRUE) or both be 0 (= FALSE).
-- Generate the SQL statement to be executed.
-- First, figure out the relational operator needed for the statement.
-- Take into account the operator name and the start and stop keys. For now,
-- the start and stop keys can both be 1 (= TRUE) or both be 0 (= FALSE).
if op.ObjectName = 'POWER_EQUALS' then
if strt = 1 then
relop := '=';
else
relop := '!=';
end if;
elsif op.ObjectName = 'POWER_LESSTHAN' then
if strt = 1 then
relop := '<';
else
relop := '>=';
end if;
elsif op.ObjectName = 'POWER_GREATERTHAN' then
if strt = 1 then
relop := '>';
else
relop := '<=';
end if;
else
15-23
Chapter 15
Creating the Domain Index
dbms_output.put_line('ODCIIndexStart>>>>>' || stmt);
cnum := dbms_sql.open_cursor;
dbms_sql.parse(cnum, stmt, dbms_sql.native);
dbms_sql.define_column_rowid(cnum, 1, rid);
nrows := dbms_sql.execute(cnum);
The function stores the cursor number in the context, which is used by the
ODCIIndexFetch function, and sets a success return status.
-- Set context as the cursor number.
stcx := power_idxtype_im(cnum);
-- Return success.
RETURN ODCICONST.SUCCESS;
END ODCIIndexStart;
See Also:
• ODCIIndexClose()
• ODCIIndexFetch()
• ODCIIndexStart()
• ODCIIndexStart(); Any Queries
The self parameter is the context that is shared with the ODCIIndexFetch() and
ODCIIndexClose() functions. The ia parameter contains the index information as an
object instance of type SYS.ODCIINDEXINFO, and the op parameter contains the
operator information as an object instance of type SYS.ODCIOPERINFO. The strt and
stop parameters are the lower and upper boundary points for the operator return
value. The cmpval parameter is the value in the cell specified by the operator
Power_Xxxx().
15-24
Chapter 15
Creating the Domain Index
ia sys.ODCIIndexInfo,
op sys.ODCIPredInfo,
qi sys.ODCIQueryInfo,
strt NUMBER,
stop NUMBER,
cmpval NUMBER,
env sys.ODCIEnv )
RETURN NUMBER IS
cnum INTEGER;
rid ROWID;
nrows INTEGER;
relop VARCHAR2(2);
stmt VARCHAR2(1000);
BEGIN
dbms_output.put_line('ODCIIndexStart>>>>>');
sys.ODCIIndexInfoDump(ia);
sys.ODCIPredInfoDump(op);
dbms_output.put_line('start key : '||strt);
dbms_output.put_line('stop key : '||stop);
dbms_output.put_line('compare value : '||cmpval);
The function generates the SQL statement to be executed. It determines the operator name
and the lower and upper index value bounds (the start and stop keys). The start and stop
keys can both be 1 (= TRUE) or both be 0 (= FALSE).
-- Generate the SQL statement to be executed.
-- First, figure out the relational operator needed for the statement.
-- Take into account the operator name and the start and stop keys. For now,
-- the start and stop keys can both be 1 (= TRUE) or both be 0 (= FALSE).
if op.ObjectName = 'POWER_EQUALSANY' then
relop := '=';
elsif op.ObjectName = 'POWER_LESSTHANANY' then
relop := '<';
elsif op.ObjectName = 'POWER_GREATERTHANANY' then
relop := '>';
else
raise_application_error(-20101, 'Unsupported operator');
end if;
-- This statement returns the qualifying rows for the TRUE case.
stmt := 'select distinct r from '||ia.IndexSchema||'.'||ia.IndexName||'_pidx'||'
where cval '||relop||''''||cmpval||'''';
-- In the FALSE case, we must find the complement of the rows.
if (strt = 0) then
stmt := 'select distinct r from '||ia.IndexSchema||'.'||ia.IndexName||
'_pidx'||' minus '||stmt;
end if;
15-25
Chapter 15
Creating the Domain Index
dbms_output.put_line('ODCIIndexStart>>>>>' || stmt);
cnum := dbms_sql.open_cursor;
dbms_sql.parse(cnum, stmt, dbms_sql.native);
dbms_sql.define_column_rowid(cnum, 1, rid);
nrows := dbms_sql.execute(cnum);
The function stores the cursor number in the context, which is used by the
ODCIIndexFetch() function, and sets a success return status.
-- Set context as the cursor number.
self := power_idxtype_im(cnum);
-- Return success.
RETURN ODCICONST.SUCCESS;
END ODCIIndexStart;
See Also:
• ODCIIndexClose()
• ODCIIndexFetch()
• ODCIIndexStart()
• ODCIIndexStart(); Specific Queries
15.4.6.6 ODCIIndexFetch()
The ODCIIndexFetch() function, demonstrated in Example 15-12 returns a batch of
ROWIDs for the rows that satisfy the operator predicate. Each time ODCIIndexFetch() is
invoked, it returns the next batch of rows (rids parameter, a collection of type
SYS.ODCIRIDLIST) that satisfy the operator predicate. The maximum number of rows
that can be returned on each invocation is specified by the nrows parameter.
Oracle invokes ODCIIndexFetch() repeatedly until all rows that satisfy the operator
predicate have been returned.
Example 15-12 Registering ODCIIndexFetch() for Power Demand Cartridge
MEMBER FUNCTION ODCIIndexFetch(
nrows NUMBER,
rids OUT sys.ODCIRidList,
env sys.ODCIEnv)
RETURN NUMBER IS
cnum INTEGER;
idx INTEGER := 1;
rlist sys.ODCIRidList := sys.ODCIRidList();
done boolean := FALSE;
The function loops through the collection of rows selected by the ODCIIndexStart()
function, using the same cursor number, cnum, as in the ODCIIndexStart() function,
and returns the ROWIDs.
BEGIN
dbms_output.put_line('ODCIIndexFetch>>>>>');
dbms_output.put_line('Nrows : '||round(nrows));
15-26
Chapter 15
Creating the Domain Index
cnum := self.curnum;
rids := rlist;
RETURN ODCICONST.SUCCESS;
END ODCIIndexFetch;
See Also:
• ODCIIndexFetch()
• ODCIIndexStart()
15.4.6.7 ODCIIndexClose()
The ODCIIndexClose() function, demonstrated in Example 15-13, closes the cursor used by
the ODCIIndexStart() and ODCIIndexFetch() functions.
Example 15-13 Registering ODCIIndexStart() for Power Demand Cartridge
MEMBER FUNCTION ODCIIndexClose (env sys.ODCIEnv)
RETURN NUMBER IS
cnum INTEGER;
BEGIN
dbms_output.put_line('ODCIIndexClose>>>>>');
cnum := self.curnum;
dbms_sql.close_cursor(cnum);
RETURN ODCICONST.SUCCESS;
END ODCIIndexClose;
15.4.6.8 ODCIIndexInsert()
The ODCIIndexInsert() function, demonstrated in Example 15-14, is called when a record is
inserted in a table that contains columns or OBJECT attributes indexed by the indextype. The
new values in the indexed columns are passed in as arguments along with the corresponding
row identifier.
15-27
Chapter 15
Creating the Domain Index
dbms_sql.close_cursor(cid);
RETURN ODCICONST.SUCCESS;
END ODCIIndexInsert;
15.4.6.9 ODCIIndexDelete()
The ODCIIndexDelete() function, demonstrated in Example 15-15, is called when a
record is deleted from a table that contains columns or object attributes indexed by the
indextype. The old values in the indexed columns are passed in as arguments along
with the corresponding row identifier.
Example 15-15 Registering ODCIIndexDelete() for Power Demand Cartridge
STATIC FUNCTION ODCIIndexDelete(
ia sys.ODCIIndexInfo,
rid VARCHAR2,
oldval PowerDemand_Typ,
env sys.ODCIEnv)
15-28
Chapter 15
Creating the Domain Index
RETURN NUMBER AS
cid INTEGER;
stmt VARCHAR2(1000);
nrows INTEGER;
BEGIN
dbms_output.put_line(' ');
dbms_output.put_line('ODCIIndexDelete>>>>>'||' TotGridDemand= '||
oldval.TotGridDemand ||' MaxCellDemand= '||oldval.MaxCellDemand ||
' MinCellDemand= '||oldval.MinCellDemand) ;
sys.ODCIIndexInfoDump(ia);
RETURN ODCICONST.SUCCESS;
END ODCIIndexDelete;
15.4.6.10 ODCIIndexUpdate()
The ODCIIndexUpdate() function, demonstrated in Example 15-16, is called when a record is
updated in a table that contains columns or object attributes indexed by the indextype. The
old and new values in the indexed columns are passed in as arguments along with the row
identifier.
Example 15-16 Registering ODCIIndexUpdate() for Power Demand Cartridge
STATIC FUNCTION ODCIIndexUpdate(
ia sys.ODCIIndexInfo,
rid VARCHAR2,
oldval PowerDemand_Typ,
newval PowerDemand_Typ,
env sys.ODCIEnv)
RETURN NUMBER AS
cid INTEGER;
cid2 INTEGER;
stmt VARCHAR2(1000);
stmt2 VARCHAR2(1000);
nrows INTEGER;
i NUMBER;
BEGIN
dbms_output.put_line(' ');
dbms_output.put_line('ODCIIndexUpdate>>>>> Old'||' TotGridDemand= '||
oldval.TotGridDemand||' MaxCellDemand= '||oldval.MaxCellDemand ||
' MinCellDemand= '||oldval.MinCellDemand) ;
dbms_output.put_line('ODCIIndexUpdate>>>>> New'||' TotGridDemand= '||
newval.TotGridDemand ||' MaxCellDemand= '||newval.MaxCellDemand ||
' MinCellDemand= '||newval.MinCellDemand) ;
sys.ODCIIndexInfoDump(ia);
15-29
Chapter 15
Creating the Domain Index
dbms_output.put_line('ODCIIndexUpdate>>>>>'||stmt);
RETURN ODCICONST.SUCCESS;
END ODCIIndexUpdate;
ODCIIndexUpdate is the last method defined in the CREATE TYPE BODY statement, which
ends as follows:
END;
/
15.4.6.11 ODCIIndexGetMetadata()
The optional ODCIIndexGetMetadata() function, as demonstrated in Example 15-17, if
present, is called by the Export utility to write implementation-specific metadata (which
is not stored in the system catalogs) into the export dump file. This metadata might be
policy information, version information, user settings, and so on. This metadata is
written to the dump file as anonymous PL/SQL blocks that are executed at import time,
immediately before the associated index is created.
This method returns strings to the Export utility that comprise the code of the PL/SQL
blocks. The Export utility repeatedly calls this method until a zero-length string is
returned, thus allowing the creation of any number of PL/SQL blocks of arbitrary
complexity. Normally, this method calls functions within a PL/SQL package to make
use of package-level variables, such as cursors and iteration counters, that maintain
state across multiple calls by Export.
In the power demand cartridge, the only metadata that is passed is a version string of
V1.0, identifying the current format of the index-organized table that underlies the
15-30
Chapter 15
Creating the Domain Index
BEGIN
-- Let getversion do all the work since it has to maintain state across calls.
EXCEPTION
WHEN OTHERS THEN
RAISE;
END ODCIIndexGetMetaData;
Example 15-18 Creating Package power_pkg for the Power Demand Cartridge
CREATE OR REPLACE PACKAGE power_pkg AS
FUNCTION getversion(
idxschema IN VARCHAR2,
idxname IN VARCHAR2,
newblock OUT PLS_INTEGER)
RETURN VARCHAR2;
PROCEDURE checkversion (
version IN VARCHAR2);
END power_pkg;
/
SHOW ERRORS;
iterate NUMBER := 0;
FUNCTION getversion(
idxschema IN VARCHAR2,
idxname IN VARCHAR2,
newblock OUT PLS_INTEGER)
RETURN VARCHAR2 IS
BEGIN
-- We are generating only one PL/SQL block consisting of one line of code.
newblock := 1;
IF iterate = 0 THEN
-- Increment iterate so we'll know we're done next time we're called.
15-31
Chapter 15
Defining Types and Methods for Extensible Optimizing
iterate := iterate + 1;
RETURN 'power_pkg.checkversion(''V1.0'');';
ELSE
-- reset iterate for next index
iterate := 0;
-- Return a 0-length string; we won't be called again for this index.
RETURN '';
END IF;
END getversion;
BEGIN
IF version != 'V1.0' THEN
RAISE wrong_version;
END IF;
END checkversion;
END power_pkg;
See Also:
Oracle Database Utilities for information about the Export and Import utilities
15-32
Chapter 15
Defining Types and Methods for Extensible Optimizing
Method Description
ODCIGetInterfaces() Returns the list of names of the interfaces implemented by the type.
15-33
Chapter 15
Defining Types and Methods for Extensible Optimizing
Method Description
ODCIStatsCollect() Collects statistics for columns of type PowerDemand_Typ or domain
indexes of indextype power_idxtype.
This method is called when a statement that refers either to a column
of the PowerDemand_Typ type or to an index of the power_idxtype
indextype is issued. Upon invocation, any specified options are
passed in along with a description of the column or index.
ODCIStatsDelete() Deletes statistics for columns of type PowerDemand_Typ or domain
indexes of indextype power_idxtype.
This method is called when a statement to delete statistics for a
column of the appropriate type or an index of the appropriate
indextype is issued.
ODCIStatsSelectivity() Computes the selectivity of a predicate involving an operator or its
functional implementation.
Called by the optimizer when a predicate of the appropriate type
appears in the WHERE clause of a query.
ODCIStatsIndexCost() Computes the cost of a domain index access path.
Called by the optimizer to get the cost of a domain index access
path, assuming the index can be used for the query.
ODCIStatsFunctionCost() Computes the cost of a function.
Called by the optimizer to get the cost of executing a function. The
function need not necessarily be an implementation of an operator.
The CREATE TYPE statement is followed by a CREATE TYPE BODY statement that
specifies the implementation for each member function:
15-34
Chapter 15
Defining Types and Methods for Extensible Optimizing
Example 15-21 Creating power_statistics Object Type Definition for Power Demand
Cartridge
CREATE OR REPLACE TYPE power_statistics AS OBJECT
(
curnum NUMBER,
STATIC FUNCTION ODCIGetInterfaces(ifclist OUT sys.ODCIObjectList)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsCollect(col sys.ODCIColInfo,
options sys.ODCIStatsOptions, rawstats OUT RAW, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsDelete(col sys.ODCIColInfo, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsCollect(ia sys.ODCIIndexInfo,
options sys.ODCIStatsOptions, rawstats OUT RAW, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsDelete(ia sys.ODCIIndexInfo, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsSelectivity(pred sys.ODCIPredInfo,
sel OUT NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER,
object PowerDemand_Typ, cell NUMBER, value NUMBER, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsSelectivity(pred sys.ODCIPredInfo, sel OUT NUMBER,
args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, object PowerDemand_Typ,
value NUMBER, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER,
cost OUT sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo,
args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, cmppos NUMBER,
cmpval NUMBER, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER,
cost OUT sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo,
args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, cmpval NUMBER,
env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo,
cost OUT sys.ODCICost, args sys.ODCIArgDescList, object PowerDemand_Typ,
cell NUMBER, value NUMBER, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo,
cost OUT sys.ODCICost, args sys.ODCIArgDescList, object PowerDemand_Typ,
value NUMBER, env sys.ODCIEnv)
RETURN NUMBER,
STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo,
cost OUT sys.ODCICost, args sys.ODCIArgDescList, object PowerDemand_Typ,
cell NUMBER, value NUMBER, env sys.ODCIEnv)
RETURN NUMBER
);
/
15-35
Chapter 15
Defining Types and Methods for Extensible Optimizing
15.5.2.2 ODCIGetInterfaces()
The ODCIGetInterfaces() function, demonstrated in Example 15-22, returns the list of
names of the interfaces implemented by the type. There is only one set of the
extensible optimizer interface routines, called SYS.ODCISTATS, but the server supports
multiple versions of them for backward compatibility. To specify the current version of
the routines, function ODCIGetInterfaces() must specify SYS.ODCISTATS2 in the OUT,
ODCIObjectList parameter.
BEGIN
15-36
Chapter 15
Defining Types and Methods for Extensible Optimizing
sys.ODCIColInfoDump(col);
sys.ODCIStatsOptionsDump(options);
dbms_output.put_line('ODCIStatsCollect>>>>>');
dbms_output.put_line('**** Analyzing column '||col.TableSchema|| '.' ||
col.TableName|| '.' || col.ColName);
The function checks whether statistics for this column exist. If so, it initializes them to NULL;
otherwise, it creates statistics for each of the 100 cells and initializes them to NULL.
IF not statsexists THEN
-- column statistics don't exist; create entries for each of the 100 cells
cnum := dbms_sql.open_cursor;
FOR i in 1..100 LOOP
stmt := 'INSERT INTO PowerCartUserStats VALUES( '||''''|| col.TableName ||
''', '||''''||colname||''', '||to_char(i)||', '||'NULL, NULL, NULL)';
dbms_sql.parse(cnum, stmt, dbms_sql.native);
junk := dbms_sql.execute(cnum);
END LOOP;
dbms_sql.close_cursor(cnum);
ELSE
-- column statistics exist; initialize to NULL
cnum := dbms_sql.open_cursor;
stmt := 'UPDATE PowerCartUserStats'||
' SET lo = NULL, hi = NULL, nrows = NULL'||' WHERE tab = '||
col.TableName||' AND col = '||colname;
dbms_sql.parse(cnum, stmt, dbms_sql.native);
junk := dbms_sql.execute(cnum);
dbms_sql.close_cursor(cnum);
END IF;
The function collects statistics for the column by reading rows from the table that is being
analyzed. This is done by constructing and executing a SQL statement.
-- For each cell position, the following statistics are collected:
-- maximum value
-- minimum value
-- number of rows (excluding NULLs)
cnum := dbms_sql.open_cursor;
FOR i in 1..100 LOOP
FOR pdemands IN c2 LOOP
IF i BETWEEN pdemands.sample.CellDemandValues.FIRST AND
pdemands.sample.CellDemandValues.LAST THEN
cval := pdemands.sample.CellDemandValues(i);
stmt := 'UPDATE PowerCartUserStats SET '|| 'lo = least(' || 'NVL(' ||
to_char(cval)||', lo), '||'NVL('||'lo, '||to_char(cval)||')), '||
'hi = greatest('||'NVL('||to_char(cval)||', hi), '||'NVL('||
'hi, '||to_char(cval)||')), '||
'nrows = decode(nrows, NULL, decode('||to_char(cval)||
', NULL, NULL, 1), decode('||to_char(cval)||
15-37
Chapter 15
Defining Types and Methods for Extensible Optimizing
The function concludes by closing the cursor and returning a success status.
dbms_sql.close_cursor(cnum);
rawstats := NULL;
return ODCIConst.Success;
END ODCIStatsCollect;
dbms_output.put_line('ODCIStatsDelete>>>>>');
dbms_output.put_line('**** Analyzing (delete) column '|| col.TableSchema||
'.' ||col.TableName||'.'||col.ColName);
The function verifies that statistics for the column exist by checking the statistics table.
If statistics were not collected, then there is nothing to be done. If, however, statistics
are present, it constructs and executes a SQL statement to delete the relevant rows
from the statistics table.
-- Check if statistics exist for this column
FOR user_defined_stats IN c1(col.TableName, colname) LOOP
statsexists := TRUE;
15-38
Chapter 15
Defining Types and Methods for Extensible Optimizing
EXIT;
END LOOP;
RETURN ODCIConst.Success;
END ODCStatsDelete;
BEGIN
-- To analyze a domain index, analyze the table that implements the index
sys.ODCIIndexInfoDump(ia);
sys.ODCIStatsOptionsDump(options);
stmt := 'dbms_stats.gather_table_stats('
|| '''' || ia.IndexSchema || ''', '
|| '''' || ia.IndexName || '_pidx' || ''');';
dbms_output.put_line('**** Analyzing index '
|| ia.IndexSchema || '.' || ia.IndexName);
dbms_output.put_line('SQL Statement: ' || stmt);
EXECUTE IMMEDIATE 'BEGIN ' || stmt || ' END;';
rawstats := NULL;
RETURN ODCIConst.Success;
END ODCIStatsCollect;
15-39
Chapter 15
Defining Types and Methods for Extensible Optimizing
The selectivity is estimated by using a technique similar to that used for simple range
predicates. For example, a simple estimate for the selectivity of a predicate like
c > v
15-40
Chapter 15
Defining Types and Methods for Extensible Optimizing
is (M-v)/(M-m) where m and M are the minimum and maximum values, respectively, for the
column c (as determined from the column statistics), provided the value v lies between m and
M.
-- col != <value>
IF relop = '!=' THEN
IF value between lo and hi THEN
sel := 1 - 1/ndv;
ELSE
sel := 1;
END IF;
-- col = <value>
ELSIF relop = '=' THEN
IF value between lo and hi THEN
sel := 1/ndv;
ELSE
sel := 0;
END IF;
15-41
Chapter 15
Defining Types and Methods for Extensible Optimizing
ELSE
sel := 0;
END IF;
END IF;
END;
/
15-42
Chapter 15
Defining Types and Methods for Extensible Optimizing
the first argument of the function in the predicate must be a column of type PowerDemand_Typ
and the remaining arguments must be constants.
Example 15-28 Registering ODCIStatsSelectivity() for Queries for Power Demand
Cartridge
STATIC FUNCTION ODCIStatsSelectivity(pred sys.ODCIPredInfo,
sel OUT NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER,
object PowerDemand_Typ, cell NUMBER, value NUMBER, env sys.ODCIEnv)
RETURN NUMBER IS
fname varchar2(30);
relop varchar2(2);
lo NUMBER;
hi NUMBER;
nrows NUMBER;
colname VARCHAR2(30);
statsexists BOOLEAN := FALSE;
stats PowerCartUserStats%ROWTYPE;
CURSOR c1(cell NUMBER, tname VARCHAR2, cname VARCHAR2) IS
SELECT * FROM PowerCartUserStats
WHERE cpos = cell
AND tab = tname
AND col = cname;
BEGIN
-- compute selectivity only when predicate is of the form:
-- fn(col, <cell>, <value>) <relop> <val>
-- In all other cases, return an error and let the optimizer
-- make a guess. We also assume that the function "fn" has
-- a return value of 0, 1, or NULL.
-- start value
IF (args(1).ArgType != ODCIConst.ArgLit AND
args(1).ArgType != ODCIConst.ArgNull) THEN
RETURN ODCIConst.Error;
END IF;
-- stop value
IF (args(2).ArgType != ODCIConst.ArgLit AND
args(2).ArgType != ODCIConst.ArgNull) THEN
RETURN ODCIConst.Error;
END IF;
15-43
Chapter 15
Defining Types and Methods for Extensible Optimizing
The first (column) argument of the function in the predicate must have statistics
collected for it. If statistics have not been collected, ODCIStatsSelectivity() returns an
error status.
-- Check if the statistics table exists (we are using a
-- user-defined table to store the user-defined statistics).
-- Get user-defined statistics: MIN, MAX, NROWS
FOR stats IN c1(cell, args(3).TableName, colname) LOOP
-- Get user-defined statistics: MIN, MAX, NROWS
lo := stats.lo;
hi := stats.hi;
nrows := stats.nrows;
statsexists := TRUE;
EXIT;
END LOOP;
The ODCIStatsSelectivity() function finds the corresponding range predicates for each
Specific function predicate. There are several boundary cases where the selectivity
can be immediately determined.
-- selectivity is 0 for "fn(col, <cell>, <value>) < 0"
IF (stop = 0 AND
bitand(pred.Flags, ODCIConst.PredIncludeStop) = 0) THEN
sel := 0;
RETURN ODCIConst.Success;
END IF;
15-44
Chapter 15
Defining Types and Methods for Extensible Optimizing
15-45
Chapter 15
Defining Types and Methods for Extensible Optimizing
(strt = 1 OR
(strt = 0 AND
bitand(pred.Flags, ODCIConst.PredIncludeStart) = 0))) THEN
relop := '<';
ELSE
RETURN ODCIConst.Error;
END IF;
After the Specific function predicate is transformed into a simple range predicate,
ODCIStatsSelectivity() calls get_selectivity to compute the selectivity for the range
predicate (and thus, equivalently, for the Specific function predicate). It returns with a
success status.
sel := get_selectivity(relop, value, lo, hi, nrows);
RETURN ODCIConst.Success;
END;
In the power demand cartridge, the domain index cost for Specific queries is identical
to the domain index cost for Any queries, so this version of the ODCIStatsIndexCost()
function simply calls the second definition of the function, described in
ODCIStatsIndexCost() Method for Any Queries.
15-46
Chapter 15
Defining Types and Methods for Extensible Optimizing
See Also:
• ODCIStatsIndexCost()
• ODCIStatsIndexCost() Method for Any Queries
• ODCIStatsSelectivity()
The index cost is estimated as the number of blocks in the index-organized table
implementing the index multiplied by the selectivity of the operator predicate times a constant
factor.
Example 15-30 Registering ODCIStatsIndexCost() for Any Queries for Power Demand
Cartridge
STATIC FUNCTION ODCIStatsIndexCost(ia sys.ODCIIndexInfo,
sel NUMBER, cost OUT sys.ODCICost, qi sys.ODCIQueryInfo,
pred sys.ODCIPredInfo, args sys.ODCIArgDescList,
strt NUMBER, stop NUMBER, cmpval NUMBER, env sys.ODCIEnv)
15-47
Chapter 15
Defining Types and Methods for Extensible Optimizing
RETURN NUMBER IS
ixtable VARCHAR2(40);
numblocks NUMBER := NULL;
get_table user_tables%ROWTYPE;
CURSOR c1(tab VARCHAR2) IS
SELECT * FROM user_tables WHERE table_name = tab;
BEGIN
-- This is the cost for queries on any cell.
cost.CPUCost := ceil(400*(sel/100)*numblocks);
cost.IOCost := ceil(1.5*(sel/100)*numblocks);
RETURN ODCIConst.Success;
END;
See Also:
• ODCIStatsIndexCost()
• ODCIStatsIndexCost() Method for Specific Queries
• ODCIStatsSelectivity()
The func parameter contains the function information; this parameter is an object
instance of type SYS.ODCIFUNCINFO. The estimated cost is returned in the output cost
parameter. The args parameter as an object instance of type SYS.ODCIARGDESCLIST
15-48
Chapter 15
Defining Types and Methods for Extensible Optimizing
contains a descriptor for each argument of the function. If the function contains a literal of
type PowerDemand_Typ as its first argument, the object parameter contains the value in the
form of an object constructor. The value parameter is the value in the cell specified by the
function PowerXxxxxSpecific_Func() or Power_XxxxxAny_Func().
The function cost is simply estimated as some default value depending on the function name.
Since the functions do not read any data from disk, the I/O cost is set to zero.
Example 15-31 Registering ODCIStatsFunctionCost() for Power Demand Cartridge
STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo,
cost OUT sys.ODCICost, args sys.ODCIArgDescList,
object PowerDemand_Typ, value NUMBER, env sys.ODCIEnv)
RETURN NUMBER IS
fname VARCHAR2(30);
BEGIN
cost := sys.ODCICost(NULL, NULL, NULL, NULL);
15-49
Chapter 15
Testing the Domain Index
Power_LessThanSpecific_Func,
Power_EqualsAny_Func,
Power_GreaterThanAny_Func,
Power_LessThanAny_Func
USING power_statistics;
15-50
Chapter 15
Testing the Domain Index
Finally, the values for TotGridDemand, MaxCellDemand, and MinCellDemand are computed and
set for each of the newly inserted rows, and these values are displayed, as demonstrated in
Example 15-36.
Example 15-34 Creating PowerDemand_Tab Table for Power Demand Cartridge
CREATE TABLE PowerDemand_Tab (
-- Region for which these power demand readings apply
region NUMBER,
-- Values for each "sampling" time (for a given hour)
sample PowerDemand_Typ
);
15-51
Chapter 15
Testing the Domain Index
Example 15-36 Computing Grid and Cell Demands for Power Demand
Cartridge
DECLARE
CURSOR c1 IS SELECT Sample, Region FROM PowerDemand_Tab FOR UPDATE;
s PowerDemand_Typ;
r NUMBER;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO s,r;
EXIT WHEN c1%NOTFOUND;
s.SetTotalDemand;
s.SetMaxDemand;
s.SetMinDemand;
dbms_output.put_line(s.TotGridDemand);
dbms_output.put_line(s.MaxCellDemand);
dbms_output.put_line(s.MinCellDemand);
UPDATE PowerDemand_Tab SET Sample = s WHERE CURRENT OF c1;
END LOOP;
CLOSE c1;
END;
/
15-52
Chapter 15
Testing the Domain Index
15-53
Chapter 15
Testing the Domain Index
15-54
Chapter 15
Testing the Domain Index
15-55
Chapter 15
Testing the Domain Index
2 49 16 5
2 53 20 5
7 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
Index name : POWERINDEX
Table owner : POWERCARTUSER
Table name : POWERDEMAND_TAB
Indexed column : "SAMPLE"
Indexed column type :POWERDEMAND_TYP
Indexed column type schema:POWERCARTUSER
ODCIPredInfo
Object owner : POWERCARTUSER
Object name : POWER_EQUALS
Method name :
Predicate bounds flag :
Exact Match
Include Start Key
Include Stop Key
start key : 1
stop key : 1
compare position : 2
compare value : 8
ODCIIndexStart>>>>>select r from POWERCARTUSER.POWERINDEX_pidx where cpos ='2'
and cval ='8'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>
SQLPLUS>
SQLPLUS> explain plan for
2> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
3> P.Sample.MinCellDemand
4> FROM PowerDemand_Tab P
5> WHERE Power_EqualsAny(P.Sample,9) = 1;
Statement processed.
SQLPLUS> @tkoqxpll
SQLPLUS> set echo off
Echo OFF
Charwidth 15
OPERATIONS OPTIONS OBJECT_NAME
--------------- --------------- ---------------
SELECT STATEMEN
TABLE ACCESS BY ROWID POWERDEMAND_TAB
DOMAIN INDEX POWERINDEX
3 rows selected.
Statement processed.
Echo ON
SQLPLUS>
SQLPLUS> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
2> P.Sample.MinCellDemand
3> FROM PowerDemand_Tab P
4> WHERE Power_EqualsAny(P.Sample,9) = 1;
REGION SAMPLE.TOT SAMPLE.MAX SAMPLE.MIN
---------- ---------- ---------- ----------
1 90 55 5
1 89 56 3
1 88 55 3
1 87 54 3
1 86 54 3
2 49 16 5
15-56
Chapter 15
Testing the Domain Index
2 53 20 5
7 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
Index name : POWERINDEX
Table owner : POWERCARTUSER
Table name : POWERDEMAND_TAB
Indexed column : "SAMPLE"
Indexed column type :POWERDEMAND_TYP
Indexed column type schema:POWERCARTUSER
ODCIPredInfo
Object owner : POWERCARTUSER
Object name : POWER_EQUALSANY
Method name :
Predicate bounds flag :
Exact Match
Include Start Key
Include Stop Key
start key : 1
stop key : 1
compare value : 9
ODCIIndexStart>>>>>select distinct r from POWERCARTUSER.POWERINDEX_pidx where cval ='9'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>
SQLPLUS>
SQLPLUS> explain plan for
2> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
3> P.Sample.MinCellDemand
4> FROM PowerDemand_Tab P
5> WHERE Power_GreaterThanAny(P.Sample,50) = 1;
Statement processed.
SQLPLUS> @tkoqxpll
SQLPLUS> set echo off
Echo OFF
Charwidth 15
OPERATIONS OPTIONS OBJECT_NAME
--------------- --------------- ---------------
SELECT STATEMEN
TABLE ACCESS BY ROWID POWERDEMAND_TAB
DOMAIN INDEX POWERINDEX
3 rows selected.
Statement processed.
Echo ON
SQLPLUS>
SQLPLUS> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
2> P.Sample.MinCellDemand
3> FROM PowerDemand_Tab P
4> WHERE Power_GreaterThanAny(P.Sample,50) = 1;
REGION SAMPLE.TOT SAMPLE.MAX SAMPLE.MIN
---------- ---------- ---------- ----------
1 90 55 5
1 89 56 3
1 88 55 3
1 87 54 3
1 86 54 3
5 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
15-57
Chapter 15
Testing the Domain Index
15-58
Chapter 15
Testing the Domain Index
Method name :
Predicate bounds flag :
Exact Match
Include Start Key
Include Stop Key
start key : 0
stop key : 0
compare value : 50
ODCIIndexStart>>>>>select distinct r from POWERCARTUSER.POWERINDEX_pidx minus se
lect distinct r from POWERCARTUSER.POWERINDEX_pidx where cval <'50'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>
15-59
16
PSBTREE: Extensible Indexing Example
This an example of extensible indexing, with C-language implementation of ODCIIndex
interface routines.
• About the PSBTREE Example
• Design of the Indextype
• Implementing Operators
• Implementing the ODCIIndex Interfaces
• Using PSBTREE
The index data consists of records of the form <key, rid> where key is the value of the
indexed column and rid is the row identifier of the corresponding row. To simplify the
implementation of the indextype, the index data is stored in an system-partitioned table.
When an index is a system-managed local domain index, one partition in a system-
partitioned table is created for each partition to store the index data for that partition. Thus,
the index manipulation routines merely translate operations on the PSBtree into operations on
the table partition that stores the index data.
When a user creates a PSBtree index (a local index), n table partitions are created consisting
of the indexed column and a rowid column, where n is the number of partitions in the base
table. Inserts into the base table cause appropriate insertions into the affected index table
partition. Deletes and updates are handled similarly. When the PSBtree is queried based on a
user-defined operator (one of gt, lt and eq), an appropriate query is issued against the index
table partitions to retrieve all the satisfying rows. Appropriate partition pruning occurs, and
only the index table partitions that correspond to the relevant, or "interesting", partitions are
accessed.
16-1
Chapter 16
Implementing Operators
16-2
Chapter 16
Implementing the ODCIIndex Interfaces
16.3.2 Operators
To create the operator, you must specify the signature of the operator along with its return
type and its functional implementation. Example 16-1 shows how to create eq (equals),
Example 16-2 shows how to create lt (less than), and Example 16-3 shows how to create gt
(greater than) operators.
Example 16-1 Creating the EQUALS Operator
CREATE OPERATOR eq
BINDING (VARCHAR2, VARCHAR2) RETURN NUMBER
USING bt_eq;
16-3
Chapter 16
Implementing the ODCIIndex Interfaces
16-4
Chapter 16
Implementing the ODCIIndex Interfaces
16-5
Chapter 16
Implementing the ODCIIndex Interfaces
ELSE
FOR c1 in cur1(ia.IndexName) LOOP
IF (i > 1) THEN
stmt := stmt || ',';
END IF;
stmt := stmt || 'partition ' || c2.subpartition_name;
i := i + 1;
END LOOP;
END IF;
stmt := 'create table ' || ia.IndexSchema || '.' || ia.IndexName ||
'_sbtree (f1 VARCHAR2(1000), f2 rowid) partition by system ' ||
'(' || stmt || ')';
dbms_output.put_line('Create');
dbms_output.put_line(stmt);
RETURN ODCIConst.Success;
END ODCIIndexCreate;
16-6
Chapter 16
Implementing the ODCIIndex Interfaces
END IF;
RETURN ODCIConst.Success;
END ODCIIndexDrop;
dbms_output.put_line('Alter');
IF ((altopt = ODCIConst.AlterIndexRebuild) OR (altopt = ODCIConst.AlterIndexRename))
THEN
dbms_output.put_line(stmt);
execute immediate stmt;
END IF;
RETURN ODCIConst.Success;
END ODCIIndexAlter;
16-7
Chapter 16
Implementing the ODCIIndex Interfaces
dbms_output.put_line(stmt);
execute immediate stmt;
ELSE
-- composite partition exchange
stmt := 'create table temp_exch (f1 VARCHAR2(1000), f2 rowid)';
dbms_output.put_line(stmt);
execute immediate stmt;
16-8
Chapter 16
Implementing the ODCIIndex Interfaces
RETURN ODCIConst.Success;
END ODCIIndexExchangePartition;
16-9
Chapter 16
Implementing the ODCIIndex Interfaces
ia,
ia indicator struct,
rid,
rid indicator,
newval,
newval indicator,
env,
env indicator struct,
return OCINumber
);
16-10
Chapter 16
Implementing the ODCIIndex Interfaces
newval indicator,
env,
env indicator struct,
return OCINumber
);
16-11
Chapter 16
Implementing the ODCIIndex Interfaces
nrows,
nrows indicator,
rids,
rids indicator,
env,
env indicator struct,
return OCINumber
);
Example 16-6 Defining Mappings for the Object Type and Its Null Value
We have defined a C struct, qxiqtim, as a mapping for the object type. There is an
additional C struct, qxiqtin, for the corresponding null object. The C structs for the
object type and its null object can be generated from the Object Type Translator
(OTT).
/* The index implementation type is an object type with a single RAW attribute
* used to store the context key value.
* C mapping of the implementation type : */
struct qxiqtim{
OCIRaw *sctx_qxiqtim;
};
typedef struct qxiqtim qxiqtim;
struct qxiqtin{
short atomic_qxiqtin;
short scind_qxiqtin;
16-12
Chapter 16
Implementing the ODCIIndex Interfaces
};
typedef struct qxiqtin qxiqtin;
switch (status)
{
case OCI_SUCCESS:
16-13
Chapter 16
Implementing the ODCIIndex Interfaces
rc = 0;
break;
case OCI_ERROR:
(void) OCIErrorGet((dvoid *)errhp, (ub4)1, (text *)NULL, &errcode,
errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
/* Raise exception */
OCIExtProcRaiseExcpWithMsg(ctx, errnum, errbuf, strlen((char *)errbuf));
rc = 1;
break;
default:
(void) sprintf((char *)errbuf, "Warning - some error\n");
/* Raise exception */
OCIExtProcRaiseExcpWithMsg(ctx, errnum, errbuf, strlen((char *)errbuf));
rc = 1;
break;
}
return (rc);
}
16-14
Chapter 16
Implementing the ODCIIndex Interfaces
/*****************************
* Construct insert Statement *
******************************/
if ((idxflag & ODCI_INDEX_RANGE_PARTN) != ODCI_INDEX_RANGE_PARTN)
(void)sprintf(insstmt, "INSERT into %s.%s_sbtree values (:newval, :mrid)",
OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName));
else
{
if (qxiqtce(ctx, errhp, OCICollGetElem(envhp, errhp, (OCIColl *)ix->IndexCols,
(sb4)0, &exists, (void **) &colinfo, (void **) &colinfo_ind)))
return(rval);
(void)sprintf(insstmt,
"INSERT into %s.%s_sbtree partition (DATAOBJ_TO_PARTITION(%s, :partiden))
VALUES (:newval, :mrid)",
OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName),
OCIStringPtr(envhp, colinfo->TableName));
}
/***************************************
* Parse and Execute Create Statement *
****************************************/
16-15
Chapter 16
Implementing the ODCIIndex Interfaces
(ub2)SQLT_STR, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0,
(ub4)OCI_DEFAULT)))
return(rval);
/* Execute statement */
if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4)1,
(ub4)0, (OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT)))
return(rval);
return(rval);
}
16-16
Chapter 16
Implementing the ODCIIndex Interfaces
/*****************************
* Construct delete Statement *
******************************/
if ((idxflag & ODCI_INDEX_RANGE_PARTN) != ODCI_INDEX_RANGE_PARTN)
(void)sprintf(delstmt, "DELETE FROM %s.%s_sbtree WHERE f2 = :rr",
OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName));
else
{
if (qxiqtce(ctx, errhp, OCICollGetElem(envhp, errhp, (OCIColl *)ix->IndexCols,
(sb4)0, &exists, (void **) &colinfo, (void **) &colinfo_ind)))
return(rval);
(void)sprintf(delstmt,
"DELETE FROM %s.%s_sbtree partition (DATAOBJ_TO_PARTITION(%s, :partiden))
WHERE f2 = :rr",
OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName),
OCIStringPtr(envhp, colinfo->TableName));
}
/***************************************
* Parse and Execute delete Statement *
****************************************/
16-17
Chapter 16
Implementing the ODCIIndex Interfaces
(dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT)))
return(rval);
/* Execute statement */
if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4)0,
(OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT)))
return(rval);
return(rval);
}
16-18
Chapter 16
Implementing the ODCIIndex Interfaces
/*****************************
* Construct update Statement *
******************************/
if ( (idxflag & ODCI_INDEX_RANGE_PARTN) != ODCI_INDEX_RANGE_PARTN)
(void)sprintf(updstmt, "UPDATE %s.%s_sbtree SET f1 = :newval WHERE f2 = :rr",
OCIStringPtr(envhp, ix->IndexSchema), OCIStringPtr(envhp, ix->IndexName));
else
{
if (qxiqtce(ctx, errhp, OCICollGetElem(envhp, errhp, OCIColl *)ix->IndexCols,
(sb4)0, &exists, (void **) &colinfo, (void **) &colinfo_ind)))
return(rval);
/****************************************
* Parse and Execute Create Statement *
****************************************/
16-19
Chapter 16
Implementing the ODCIIndex Interfaces
/* Execute statement */
if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4)1,
ub4)0, (OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT)))
return(rval);
return(rval);
}
This function sets up a cursor that scans the index table. The scan retrieves the stored
rowids for the rows in the index table that satisfy the specified predicate. The predicate
for the index table is generated based on the operator predicate information that is
passed in as parameters. For example, if the operator predicate is of the form eq(col,
'joe') = 1, then the predicate on the index table is set up to be f1 = 'joe'.
This function uses the structs qxiqtim, qxiqtin, and qxiqtcx, which were
demonstrated in Example 16-6 and Example 16-7.
OCINumber *qxiqtbsps(
OCIExtProcContext *ctx,
qxiqtim *sctx,
qxiqtin *sctx_ind,
ODCIIndexInfo *ix,
ODCIIndexInfo_ind *ix_ind,
ODCIPredInfo *pr,
ODCIPredInfo_ind *pr_ind,
ODCIQueryInfo *qy,
ODCIQueryInfo_ind *qy_ind,
OCINumber *strt,
short strt_ind,
OCINumber *stop,
short stop_ind,
char *cmpval,
short cmpval_ind,
ODCIEnv *env,
ODCIEnv_ind *env_ind)
{
sword status;
OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */
OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */
OCIError *errhp = (OCIError *) 0; /* error handle */
OCISession *usrhp = (OCISession *) 0; /* user handle */
qxiqtcx *icx = (qxiqtcx *) 0; /* state to be saved for later calls */
16-20
Chapter 16
Implementing the ODCIIndex Interfaces
/**********************************************/
/* Allocate memory to hold index scan context */
/**********************************************/
if (sctx_ind ->atomic_qxiqtin == OCI_IND_NULL ||
sctx_ind ->scind_qxiqtin == OCI_IND_NULL)
{
if (qxiqtce(ctx, errhp, OCIMemoryAlloc((dvoid *)usrhp, errhp, (dvoid **)&icx,
OCI_DURATION_STATEMENT, (ub4)(sizeof(qxiqtcx)), OCI_MEMORY_CLEARED)))
return(rval);
else
{
/*************************/
/* Retrieve scan context */
/*************************/
rkey = OCIRawPtr(envhp, sctx->sctx_qxiqtim);
rkeylen = OCIRawSize(envhp, sctx->sctx_qxiqtim);
16-21
Chapter 16
Implementing the ODCIIndex Interfaces
/***********************************/
/* Check that the bounds are valid */
/***********************************/
/* convert from oci numbers to native numbers */
if (qxiqtce(ctx, errhp, OCINumberToInt(errhp, strt, sizeof(strtval),
OCI_NUMBER_SIGNED, (dvoid *)&strtval)))
return(rval);
/*********************************************/
/* Generate the SQL statement to be executed */
/*********************************************/
if (memcmp((dvoid *)OCIStringPtr(envhp, pr->ObjectName), (dvoid *)"EQ", 2) ==
0)
if (strtval == 1)
strcpy(relop, (char *)"=");
else
strcpy(relop, (char *)"!=");
else if
(memcmp((dvoid *)OCIStringPtr(envhp, pr->ObjectName), (dvoid *)"LT",2) ==
0)
if (strtval == 1)
strcpy(relop, (char *)"<");
else
strcpy(relop, (char *)">=");
else
if (strtval == 1)
strcpy(relop, (char *)">");
else
strcpy(relop, (char *)"<=");
16-22
Chapter 16
Implementing the ODCIIndex Interfaces
/***********************************/
/* Parse, bind, define and execute */
/***********************************/
if (sctx_ind ->atomic_qxiqtin == OCI_IND_NULL ||
sctx_ind ->scind_qxiqtin == OCI_IND_NULL)
{
/* allocate stmt handle */
if (qxiqtce(ctx, errhp, OCIHandleAlloc((dvoid *)envhp,
(dvoid **)&(icx->stmthp), (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0)))
return(rval);
}
/* Set up define */
if (qxiqtce(ctx, errhp, OCIDefineByPos(icx->stmthp, &(icx->defnp), errhp,
(ub4)1, (dvoid *)(icx->ridp), (sb4) sizeof(icx->ridp), (ub2)SQLT_STR,
(dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)OCI_DEFAULT)))
return(rval);
/* execute */
if (qxiqtce(ctx, errhp, OCIStmtExecute(svchp, icx->stmthp, errhp, (ub4)0,
(ub4)0, (OCISnapshot *)NULL, (OCISnapshot *)NULL, (ub4)OCI_DEFAULT)))
return(rval);
/************************************/
/* Set index context to be returned */
/************************************/
if (sctx_ind ->atomic_qxiqtin == OCI_IND_NULL ||
sctx_ind ->scind_qxiqtin == OCI_IND_NULL)
{
/* generate a key */
if (qxiqtce(ctx, errhp, OCIContextGenerateKey((dvoid *)usrhp, errhp, &key)))
return(rval);
16-23
Chapter 16
Implementing the ODCIIndex Interfaces
sctx_ind->atomic_qxiqtin = OCI_IND_NOTNULL;
sctx_ind->scind_qxiqtin = OCI_IND_NOTNULL;
return(rval);
}
return(rval);
}
int idx = 1;
int nrowsval;
16-24
Chapter 16
Implementing the ODCIIndex Interfaces
int done = 0;
int retval = (int)ODCI_SUCCESS;
OCINumber *rval = (OCINumber *)0;
/*******************/
/* Get OCI handles */
/*******************/
if (qxiqtce(ctx, errhp, OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp)))
return(rval);
/********************************/
/* Retrieve context from key */
/********************************/
key = OCIRawPtr(envhp, self->sctx_qxiqtim);
keylen = OCIRawSize(envhp, self->sctx_qxiqtim);
/****************/
/* Fetch rowids */
/****************/
while (!done)
{
if (idx > nrowsval)
done = 1;
else
{
status =OCIStmtFetch(icx->stmthp, errhp, (ub4)1, (ub2) 0, (ub4)OCI_DEFAULT);
if (status == OCI_NO_DATA)
{
short col_ind = OCI_IND_NULL;
/* have to create dummy oci string */
OCIStringAssignText(envhp, errhp, (text *)"dummy", (ub2)5, &ridstr);
/* append null element to collection */
if (qxiqtce(ctx, errhp, OCICollAppend(envhp, errhp, (dvoid *)ridstr,
(dvoid *)&col_ind, (OCIColl *)ridarrp)))
return(rval);
done = 1;
16-25
Chapter 16
Implementing the ODCIIndex Interfaces
}
else if (status == OCI_SUCCESS)
{
OCIStringAssignText(envhp, errhp, (text *)icx->ridp, (ub2)18,
OCIString **)&ridstr);
/* append rowid to collection */
if (qxiqtce(ctx, errhp, OCICollAppend(envhp, errhp, (dvoid *)ridstr,
(dvoid *)0, (OCIColl *)ridarrp)))
return(rval);
idx++;
}
else if (qxiqtce(ctx, errhp, status))
return(rval);
}
}
*rids_ind = OCI_IND_NOTNULL;
return(rval);
}
This function closes and frees all the OCI handles. It also frees the memory that was
allocated in the start routine.
OCINumber *qxiqtbspc(
OCIExtProcContext *ctx,
qxiqtim *self,
qxiqtin *self_ind,
ODCIEnv *env,
ODCIEnv_ind *env_ind)
{
sword status;
OCIEnv *envhp = (OCIEnv *) 0; /* env. handle */
OCISvcCtx *svchp = (OCISvcCtx *) 0; /* service handle */
OCIError *errhp = (OCIError *) 0; /* error handle */
OCISession *usrhp = (OCISession *) 0; /* user handle */
qxiqtcx *icx = (qxiqtcx *) 0; /* state to be saved for later calls */
16-26
Chapter 16
Using PSBTREE
return(rval);
}
16-27
Chapter 16
Using PSBTREE
One typical usage scenario is to create a range partitioned table and populate it, as
demonstrated in the Creating and Populating a Partitioned Table for PSBTREE
section.
You can then create a psbtree index on column f2. The CREATE INDEX statement
specifies the indextype that should be used, as demonstrated in the Creating a
PSBTREE Index on a Column section.
To execute a query that uses one of the psbtree operators, use the code in the Using
PSBTREE Operators in a Query section.
• Creating and Populating a Partitioned Table for PSBTREE
• Creating a PSBTREE Index on a Column
• Using PSBTREE Operators in a Query
The explain plan output for this query should look like this:
OPERATION OPTIONS PARTITION_START PARTITION_STOP
--------------------------------------------------------------------------------
SELECT STATEMENT
PARTITION RANGE ITERATOR 2 4
TABLE ACCESS BY LOCAL INDEX ROWID 2 4
DOMAIN INDEX
16-28
17
Pipelined Table Functions: Interface Approach
Example
Two complete implementations of the StockPivot pipelined table function using the interface
approach are described. One implementation is in C, and the other is in Java.
The function StockPivot converts a row of the type (Ticker, OpenPrice, ClosePrice) into
two rows of the form (Ticker, PriceType, Price). For example, from an input row
("ORCL", 41, 42), the table function returns the two rows ("ORCL", "O", 41) and ("ORCL",
"C", 42).
Tip:
Consider the table functions described in Using Pipelined and Parallel Table
Functions .
17-1
Chapter 17
Pipelined Table Functions Example: C Implementation
ticker VARCHAR2(4),
PriceType VARCHAR2(1),
price NUMBER
);
/
17-2
Chapter 17
Pipelined Table Functions Example: C Implementation
/*---------------------------------------------------------------------------
PRIVATE TYPES AND CONSTANTS
---------------------------------------------------------------------------*/
struct StoredCtx
{
OCIStmt* stmthp;
};
typedef struct StoredCtx StoredCtx;
/* OCI Handles */
struct Handles_t
{
OCIExtProcContext* extProcCtx;
OCIEnv* envhp;
OCISvcCtx* svchp;
OCIError* errhp;
OCISession* usrhp;
};
typedef struct Handles_t Handles_t;
struct StockPivotImpl
{
OCIRaw* key;
};
typedef struct StockPivotImpl StockPivotImpl;
struct StockPivotImpl_ind
{
short _atomic;
short key;
};
typedef struct StockPivotImpl_ind StockPivotImpl_ind;
struct TickerType
{
OCIString* ticker;
OCIString* PriceType;
17-3
Chapter 17
Pipelined Table Functions Example: C Implementation
OCINumber price;
};
typedef struct TickerType TickerType;
struct TickerType_ind
{
short _atomic;
short ticker;
short PriceType;
short price;
};
typedef struct TickerType_ind TickerType_ind;
/*--------------------------------------------------------------------------*/
/* Static Functions */
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/* Functions definitions */
/*--------------------------------------------------------------------------*/
/* generate a key */
if (checkerr(&handles, OCIContextGenerateKey((dvoid*) handles.usrhp,
handles.errhp, &key)))
return ODCI_ERROR;
17-4
Chapter 17
Pipelined Table Functions Example: C Implementation
*cur=(OCIStmt *)0;
return ODCI_SUCCESS;
}
/***********************************************************************/
while (nrowsval>0)
{
17-5
Chapter 17
Pipelined Table Functions Example: C Implementation
sword status;
char ticker[5];
float openprice;
float closeprice;
char PriceType[2];
elem_ind._atomic=OCI_IND_NOTNULL;
elem_ind.ticker=OCI_IND_NOTNULL;
elem_ind.PriceType=OCI_IND_NOTNULL;
elem_ind.price=OCI_IND_NOTNULL;
17-6
Chapter 17
Pipelined Table Functions Example: C Implementation
elem.PriceType=NULL;
sprintf(PriceType,"O");
if (checkerr(&handles, OCIStringAssignText(handles.envhp, handles.errhp,
(text*) PriceType,
(ub2) strlen(PriceType),
&elem.PriceType)))
return ODCI_ERROR;
nrowsval-=2;
}
return ODCI_SUCCESS;
}
/***********************************************************************/
17-7
Chapter 17
Pipelined Table Functions Example: C Implementation
storedCtx=GetStoredCtx(&handles,self,self_ind);
if (!storedCtx) return ODCI_ERROR;
return ODCI_SUCCESS;
}
/***********************************************************************/
/* Get the stored context using the key in the scan context */
return storedCtx;
}
/***********************************************************************/
17-8
Chapter 17
Pipelined Table Functions Example: Java Implementation
return 0;
}
/***********************************************************************/
switch (status)
{
case OCI_SUCCESS:
case OCI_SUCCESS_WITH_INFO:
return 0;
case OCI_ERROR:
OCIErrorGet ((dvoid*) handles->errhp, (ub4) 1, (text *) NULL, &errcode,
errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR);
sprintf((char*)errbuf, "OCI ERROR code %d",errcode);
break;
default:
sprintf((char*)errbuf, "Warning - error status %d",status);
break;
}
return -1;
}
17-9
Chapter 17
Pipelined Table Functions Example: Java Implementation
/
show errors
);
/
show errors
// implementation type
17-10
Chapter 17
Pipelined Table Functions Example: Java Implementation
String sql_type;
public String getSQLTypeName() throws SQLException
{
return sql_type;
}
return SUCCESS;
}
17-11
Chapter 17
Pipelined Table Functions Example: Java Implementation
StructDescriptor outDesc =
StructDescriptor.createDescriptor("TICKERTYPE", conn);
Object[] out_attr = new Object[3];
i+=2;
nrowsval-=2;
}
return SUCCESS;
}
// retrieve stored context using the key, and remove from ContextManager
StoredCtx ctx;
try {
ctx=(StoredCtx)ContextManager.clearContext(key.intValue());
} catch (InvalidKeyException ik ) {
return ERROR;
}
return SUCCESS;
}
17-12
Part IV
Reference
This part contains reference information on cartridge-related APIs.
• Cartridge Services Using C_ C++ and Java
• Extensibility Constants_ Types_ and Mappings
• Extensible Indexing Interface
• Extensible Optimizer Interface
• User-Defined Aggregate Functions Interface
• Pipelined and Parallel Table Functions
• Cartridge Services Using C, C++ and Java
• Extensibility Constants, Types, and Mappings
• Extensible Indexing Interface
• Extensible Optimizer Interface
• User-Defined Aggregate Functions Interface
• Pipelined and Parallel Table Functions
18
Cartridge Services Using C, C++ and Java
Cartridge services are available to programmers using C/C++ and Java.
See Also:
Oracle Call Interface Programmer's Guide for more details on cartridge services
using C
Consider how service routines use context information. For more information and examples
of usage, see Oracle Database Advanced Application Developer's Guide.
• OCIExtProcAllocCallMemory
• OCIExtProcRaiseExcp
• OCIExtProcRaiseExcpWithMsg
• OCIExtProcGetEnv
18.1.1 OCIExtProcAllocCallMemory
This service routine allocates n bytes of memory for the duration of the external procedure
call. Any memory allocated by the function is freed as soon as control returns to PL/SQL.
Do not use any other function to allocate or free memory.
The C prototype for this function follows:
void *OCIExtProcAllocCallMemory(
OCIExtProcContext *with_context,
size_t amount);
18-1
Chapter 18
OCI Access Functions for External Procedures
The parameters with_context and amount are the context pointer and number of bytes
to allocate, respectively. The function returns an untyped pointer to the allocated
memory. A return value of zero indicates failure.
18.1.2 OCIExtProcRaiseExcp
This service routine raises a predefined exception, which must have a valid Oracle
error number in the range 1 to 32767. After doing any necessary cleanup, the external
procedure must return immediately. (No values are assigned to OUT or IN OUT
parameters.) The C prototype for this function follows:
int OCIExtProcRaiseExcp(
OCIExtProcContext *with_context,
size_t error_number);
The parameters with_context and error_number are the context pointer and Oracle
error number. The return values OCIEXTPROC_SUCCESS and OCIEXTPROC_ERROR indicate
success or failure.
18.1.3 OCIExtProcRaiseExcpWithMsg
This service routine raises a user-defined exception and returns a user-defined error
message. The C prototype for this function follows:
int OCIExtProcRaiseExcpWithMsg(
OCIExtProcContext *with_context,
size_t error_number,
text *error_message,
size_t len);
18.1.4 OCIExtProcGetEnv
This service routine enables OCI callbacks to the database during an external
procedure call. Use the OCI handles obtained by this function only for callbacks. If you
use them for standard OCI calls, the handles establish a new connection to the
database and cannot be used for callbacks in the same transaction. In other words,
during an external procedure call, you can use OCI handles for callbacks or a new
connection but not for both.
The C prototype for this function follows:
sword OCIExtProcGetEnv(
OCIExtProcContext *with_context,
OCIEnv **envh,
OCISvcCtx **svch,
OCIError **errh);
The parameter with_context is the context pointer, and the parameters envh, svch, and
errh are the OCI environment, service, and error handles, respectively. The return
values OCIEXTPROC_SUCCESS and OCIEXTPROC_ERROR indicate success or failure.
18-2
Chapter 18
Installing Java Cartridge Services Files
"Using Callbacks" shows how OCIExtProcGetEnv might be used in callbacks. For a working
example, see the script extproc.sql in the PL/SQL demo directory. (For the location of this
directory, see your Oracle installation or user's guide.) This script demonstrates the calling of
an external procedure. The companion file extproc.c contains the C source code for the
external procedure. To run the demo, follow the instructions in extproc.sql. You must use an
account that has CREATE LIBRARY privileges.
3. Use the server-side loadjava command to install the classes and create the synonyms in
the SYSTEM schema.
SQL> call dbms_java.loadjava('-resolve -synonym -grant public
-verbose vobs/jilip/Cartridge Services.jar');
SQL> call dbms_java.loadjava('-resolve -synonym -grant public
-verbose vobs/jlib/ODCI.jar');
See Oracle Database Upgrade Guide for further details on installing the jar files.
18.3.1 ContextManager
ContextManager is a Constructor in class Oracle that extends Object.
18-3
Chapter 18
Cartridge Services-Maintaining Context
Class Interface
public static Hashtable ctx extends Object
Variable
ctx public static Hashtable ctx
Constructors
ContextManager public ContextManager()
Methods
The following methods are available:
setContext (static method in class oracle)
getContext (static method in class oracle)
clearContext (static method in class oracle)
18.3.2 CountException()
Constructor that extends Exception.
Class oracle.CartridgeServices.CountException
18.3.3 CountException(String)
Constructor that extends Exception.
public CountException(String s)
18.3.4 InvalidKeyException()
Constructor that extends Exception.
public InvalidKeyException(String s)
18.3.5 InvalidKeyException(String)
Constructor that extends Exception.
public InvalidKeyException(String s)
18-4
19
Extensibility Constants, Types, and Mappings
System Defined Constants and System Defined Types apply generically to all supported
languages, as well as mappings that are specific to the PL/SQL, C, and Java languages.
• System Defined Constants
• System-Defined Types
• Mappings of Constants and Types
19-1
Chapter 19
System Defined Constants
Name Description
Name Description
19-2
Chapter 19
System Defined Constants
Name Description
ListPartn CONSTANT INTEGER := 512; For a local domain index, indicates that
the base table is list-partitioned. Is set
only in conjunction with the Local bit.
19-3
Chapter 19
System Defined Constants
RefPartn CONSTANT INTEGER := 2048; For a local domain index, indicates that
the base table is reference-partitioned. Is
set only in conjunction with the Local
bit.
CompPartn CONSTANT INTEGER := 8192; For a local domain index, indicates that
the base table is composite partitioned.
Is set in conjunction with the Local bit
and the RangePartn, ListPartn, or
HashPartn bits.
SubPartn CONSTANT INTEGER := 16384 For a local domain index, indicates that
the partition information for the current
call refers to a subpartition. Is set in
conjunction with the CompPartn bit.
Name Description
Name Description
19-4
Chapter 19
System Defined Constants
Name Description
Name Description
Name Description
19-5
Chapter 19
System Defined Constants
Name Description
Name Description
Name Description
Fatal Indicates that all dictionary entries of the index are cleaned up,
and that the CREATE INDEX operation is rolled back
19-6
Chapter 19
System-Defined Types
Name Description
19-7
Chapter 19
System-Defined Types
• ODCINumberList
• ODCIRawList
• ODCIVarchar2List
• ODCIFuncCallInfo
19.2.1 ODCIArgDesc
Object type. Stores function or operator arguments.
19.2.2 ODCIArgDescList
Contains a list of argument descriptors
Data Type
VARRAY(32767) of ODCIArgDesc
19.2.3 ODCIRidList
Stores list of rowids. The rowids are stored in their character format.
Data Type
VARRAY(32767) OF VARCHAR2("M_URID_SZ")
19.2.4 ODCIColInfo
Stores column related information.
19-8
Chapter 19
System-Defined Types
Data Type
Object type.
19.2.5 ODCIColInfoList
Stores information related to a list of columns.
Data Type
VARRAY(32) OF ODCIColInfo
19.2.6 ODCICost
Object type. Stores cost information.
I/O cost
IOCost NUMBER
Communication cost
NetworkCost NUMBER
19-9
Chapter 19
System-Defined Types
19.2.7 ODCIEnv
Object type. Contains general information about the environment in which the
extensibility routines are executing.
Usage Notes
CallProperty is used only for CREATE INDEX, DROP INDEX, TRUNCATE TABLE, and for
some extensible optimizer-related calls. In all other cases, including DML and query
routines for local domain indexes, it is set to 0.
19.2.8 ODCIFuncInfo
Object type. Stores functional information.
19-10
Chapter 19
System-Defined Types
Function/package/type name
ObjectName VARCHAR2(30)
19.2.9 ODCIIndexInfo
Object type. Stores the metadata information related to a domain index. It is passed as a
parameter to all ODCIIndex routines.
19-11
Chapter 19
System-Defined Types
19.2.10 ODCIIndexCtx
Object type. Stores the index context, including the domain index metadata and the
rowid. It is passed as parameter to the functional implementation of an operator that
expects index context.
19.2.11 ODCIObject
Object type. Stores information about a schema object.
Name of object
ObjectName VARCHAR2(30)
19.2.12 ODCIObjectList
Stores information about a list of schema objects.
Data Type
VARRAY(32) OF ODCIObject
19.2.13 ODCIPartInfo
Object type. Contains the names of both the table partition and the index partition.
19-12
Chapter 19
System-Defined Types
19.2.14 ODCIPartInfoList
Stores information related to a list of partitions.
Data Type
VARRAY(64000) OF ODCIPartInfo
19.2.15 ODCIPredInfo
Object type. Stores the metadata information related to a predicate containing a user-defined
operator or function. It is also passed as a parameter to the ODCIIndexStart() query routine.
Name of operator/function
ObjectName VARCHAR2(30)
19.2.16 ODCIQueryInfo
Object type. Stores information about the context of a query. It is passed as a parameter to
the ODCIIndexStart() routine.
19-13
Chapter 19
System-Defined Types
19.2.17 ODCIStatsOptions
Object type. Stores options information for DBMS_STATS.
19.2.18 ODCITabFuncStats
Object type. Stores cardinality information for a table function.
19.2.19 ODCITabStats
Stores table statistics for a table function.
Data Type
NUMBER
19-14
Chapter 19
System-Defined Types
19.2.20 ODCIBFileList
Stores varrays of BFILEs.
Data Type
VARRAY(32767) OF BFILE
19.2.21 ODCITabFuncInfo
Object type. Stores information on which attributes of user-defined types in a collection must
be set by a table function.
19.2.22 ODCIDateList
Stores varrays of DATEs.
Data Type
VARRAY(32767) OF DATE
19.2.23 ODCINumberList
Stores varrays of NUMBERs.
Data Type
VARRAY(32767) OF NUMBER
19.2.24 ODCIRawList
Stores varrays of Raws.
Data Type
VARRAY(32767) OF Raw(2000)
19-15
Chapter 19
Mappings of Constants and Types
19.2.25 ODCIVarchar2List
Stores varrays of VARCHAR2s
Data Type
VARRAY(32767) OF VARCHAR2(4000)
19.2.26 ODCIFuncCallInfo
Object type. Stores information about the functional implementation of an operator.
Usage Notes
A functional implementation can be defined with this parameter only if the operator
binding is declared WITH COLUMN CONTEXT. This is useful if the functional
implementation requires information about the column it was invoked on, and there is
no domain index defined on the column. This argument is only populated in the
function invocation if the first argument of the operator invocation is a column and
there is no domain index defined on that column.
19.3.2 Mappings in C
Mappings of constants and types are defined for C in the public header file odci.h.
Each C structure to which a type is mapped has a corresponding indicator structure
called structname_ind and a reference definition called structname_ref.
19-16
20
Extensible Indexing Interface
Various Oracle Data Cartridge Interface extensible indexing parameters are described.
• Extensible Indexing - System-Defined Interface Routines
Caution:
These routines are invoked by Oracle at the appropriate times based on SQL
statements executed by the end user. Do not invoke these routines directly as this
may result in corruption of index data.
Routine Description
ODCIGetInterfaces() Invoked when an INDEXTYPE is created by a CREATE
INDEXTYPE... statement or is altered.
ODCIIndexAlter() Invoked when a domain index or a domain index partition is
altered using an ALTER INDEX, an ALTER INDEX PARTITION, a
TRUNCATE TABLE, a RENAME TABLE, an ALTER TABLE RENAME
COLUMN, or an ALTER TABLE [ADD|TRUNCATE|SPLIT|MERGE]
PARTITION statement.
ODCIIndexClose() Invoked to end the processing of an operator.
ODCIIndexCreate() Invoked when a domain index is created by a CREATE
INDEX...INDEXTYPE IS...PARAMETERS... statement issued
by the user.
ODCIIndexDelete() Invoked when a row is deleted from a table that has a domain
index defined on one or more of its columns.
ODCIIndexDrop() Invoked when a domain index is dropped explicitly using a DROP
INDEX statement, or implicitly through a DROP TABLE or DROP
USER statement.
ODCIIndexExchangePartition() Invoked when an ALTER TABLE EXCHANGE
PARTITION...INCLUDING INDEXES is issued on a partitioned
table on which a local domain index is defined.
ODCIIndexFetch() Invoked repeatedly to retrieve the rows satisfying the operator
predicate.
ODCIIndexGetMetadata() Returns a series of strings of PL/SQL code that comprise the
non-dictionary metadata associated with the index.
20-1
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Routine Description
ODCIIndexInsert() Invoked when a row or a set of rows is inserted into a table that
has a domain index defined on one or more of its columns.
ODCIIndexStart() Invoked to start the evaluation of an operator on an indexed
column.
ODCIIndexUpdate() Invoked when a row is updated in a table and the updated
column has a domain index defined on.
ODCIIndexUpdPartMetadata() Invoked during partition maintenance operations. Patches the
indextype metadata tables to correctly reflect the partition
maintenance operation.
ODCIIndexUtilCleanup() Cleans up temporary states created by
ODCIIndexUtilGetTableNames().
ODCIIndexUtilGetTableNames() Determines if the secondary tables storing the index data should
be transported.
• ODCIGetInterfaces()
• ODCIIndexAlter()
• ODCIIndexClose()
• ODCIIndexCreate()
• ODCIIndexDelete()
• ODCIIndexDrop()
• ODCIIndexExchangePartition()
• ODCIIndexFetch()
• ODCIIndexGetMetadata()
• ODCIIndexInsert()
• ODCIIndexStart()
• ODCIIndexUpdate()
• ODCIIndexUpdPartMetadata()
• ODCIIndexUtilCleanup()
• ODCIIndexUtilGetTableNames()
20.1.1 ODCIGetInterfaces()
Invoked when an INDEXTYPE is created by a CREATE INDEXTYPE... statement or is
altered.
Syntax
FUNCTION ODCIGetInterfaces(
ifclist OUT ODCIObjectList)
RETURN NUMBER
20-2
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Parameter Description
Returns
ODCIConst.Success on success or ODCIConst.Error on error
Usage Notes
This function should be implemented as a static type method.
This function must return SYS.ODCIINDEX2 in the ODCIObjectList if the indextype uses the
second version of the ODCIIndex interface.
20.1.2 ODCIIndexAlter()
Invoked when a domain index or a domain index partition is altered using one of the following
methods:
• ALTER INDEX
• ALTER INDEX PARTITION
• TRUNCATE TABLE table_name
• RENAME TABLE
• ALTER TABLE...[ADD|TRUNCATE|SPLIT|MERGE]...PARTITION
• ALTER TABLE RENAME
• ALTER TABLE RENAME COLUMN
To populate the index partitions when creating local domain indexes, this method is invoked
for each partition of the base table.
Syntax
STATIC FUNCTION ODCIIndexAlter(
ia ODCIIndexInfo,
parms IN OUT VARCHAR2,
alter_option NUMBER,
env ODCIEnv)
RETURN NUMBER
Parameter Description
20-3
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Parameter Description
Returns
ODCIConst.Success on success, ODCIConst.Error on error, or ODCIConst.Warning
otherwise. When invoked to rebuild local index partitions, may also return
ODCIConst.ErrContinue.
Usage Notes
• This function should be implemented as a static type method.
• An ALTER INDEX statement can be invoked for domain indexes in multiple ways.
ALTER INDEX index_name
PARAMETERS (parms);
or
ALTER INDEX index_name
REBUILD PARAMETERS (parms);
The precise behavior in these two cases is defined by the implementation. One
possibility is that the first statement would merely reorganize the index based on
the parameters while the second would rebuild it from scratch.
• The maximum length of the input parameters string is 1000 characters. The OUT
value of the parms argument can be set to resultant parameters string to be stored
in the system catalogs.
20-4
Chapter 20
Extensible Indexing - System-Defined Interface Routines
• The ALTER INDEX statement can also be used to rename a domain index in the following
way:
ALTER INDEX index_name
RENAME TO new_index_name
• When the name of the table on which a domain index is created changes,
ODCIIndexAlter() is invoked with alter_option=AlterIndexRenameTab, and
new_table_name is passed to the parms argument:
ALTER TABLE table_name
RENAME new_table_name
or
RENAME table_name
TO new_table_name
• When the name of the column on which a domain index is created changes,
ODCIIndexAlter() is invoked with alter_option=AlterIndexRenameCol, and
new_column_name is passed to the parms argument:
ALTER TABLE table_name
RENAME COLUMN column_name
TO new_column_name
• If the PARALLEL clause is omitted, then the domain index or local domain index partition is
rebuilt sequentially.
• If the PARALLEL clause is specified, the parallel degree is passed to the
ODCIIndexAlter() invocation in the IndexParaDegree attribute of ODCIIndexInfo, and
the Parallel bit of the IndexInfoFlags attribute is set. The parallel degree is determined
as follows:
– If PARALLEL DEGREE deg is specified, deg is passed.
– If only PARALLEL is specified, then a constant is passed to indicate that the default
degree of parallelism was specified.
• If the ODCIIndexAlter routine returns with the ODCIConst.Success, the index is valid and
usable. If the ODCIIndexAlter() routine returns with ODCIConst.Warning, the index is
valid and usable but a warning message is returned to the user. If ODCIIndexAlter()
returns with an error (or exception), the domain index is marked FAILED.
• When the ODCIIndexAlter() routine is being executed, the domain index is marked
LOADING.
• Every SQL statement executed by ODCIIndexAlter() is treated as an independent
operation. The changes made by ODCIIndexCreate() are not guaranteed to be atomic.
• The AlterIndexUpdBlockRefs alter option applies only to domain indexes on index-
organized tables. When the end user executes an ALTER INDEX domain_index UPDATE
BLOCK REFERENCES, ODCIIndexAlter() is called with the AlterIndexUpdBlockRefs bit set
to give the cartridge developer the opportunity to update guesses as to the block
locations of rows, stored in logical rowids.
• The AlterIndexMigrate alter options applies only to migration of user-managed domain
indexes to system-managed domain indexes. When the user-managed domain index is
marked INVALID, but its indextype is system-managed, you must make an ALTER INDEX
domain_index COMPILE call to re-validate the domain index. This calls the
20-5
Chapter 20
Extensible Indexing - System-Defined Interface Routines
See Also:
• ODCIIndexAlter()
• ODCIIndexCreate()
20.1.3 ODCIIndexClose()
Invoked to end the processing of an operator.
Syntax
FUNCTION ODCIIndexClose(
self IN impltype,
env ODCIEnv)
RETURN NUMBER
Parameter Description
Returns
• ODCIConst.Success on success
• ODCIConst.Error on error
Usage Notes
The index implementor can perform any appropriate actions to finish up the processing
of an domain index scan, such as freeing memory and other resources.
20.1.4 ODCIIndexCreate()
Invoked when a domain index is created by a CREATE INDEX...INDEXTYPE
IS...PARAMETERS... statement issued by the user. The domain index can be either a
non-partitioned index or a local partitioned domain index. The local partitioned domain
index can be created in either a system- or a user-managed scheme.
Syntax
FUNCTION ODCIIndexCreate(
ia ODCIIndexInfo,
parms VARCHAR2,
env ODCIEnv)
RETURN NUMBER
20-6
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Parameter Description
parms The PARAMETERS string passed in not interpreted by Oracle. The maximum
size of the parameter string is 1,000 characters.
Returns
ODCIConst.Success , ODCIConst.Error, ODCIConst.Warning, ODCIConst.ErrContinue if the
method is invoked at the partition level for creation of a local partitioned index, to continue to
the next partition even in case of an error, or ODCIConst.Fatal to signify that all dictionary
entries for the index are cleaned up and that the CREATE INDEX operation is rolled back.
Returning this status code assumes that the cartridge code has not created any objects (or
cleaned up any objects created).
Usage Notes
• This function should be implemented as a STATIC type method.
• Creates objects (such as tables) to store the index data, generate the index data, and
store the data in the index data tables.
• This procedure should handle creation of indexes on both empty and non-empty tables. If
the base table is not empty, the procedure can scan the entire table and generate index
data.
• When the ODCIIndexCreate() routine is running, the domain index is marked LOADING.
• Every SQL statement executed by ODCIIndexCreate() is treated as an independent
operation. The changes made by ODCIIndexCreate() are not guaranteed to be atomic.
• To create a non-partitioned domain index, the ODCIIndexCreate() method is invoked,
and the only valid return codes are ODCIConst.Success, ODCIConst.Warning,
ODCIConst.Error, or ODCIConst.Fatal. If the operation returns ODCIConst.Fatal, the
CREATE INDEX statement is rolled back by the server.
• In a non-partitioned domain index, the IndexPartition, TablePartition name, and the
callProperty should be NULL.
• For a non-partitioned domain index, the parallel degree is passed to the
ODCIIndexCreate() invocation in the IndexParaDegree attribute of ODCIIndexInfo, and
the Parallel bit of the IndexInfoFlags is set. The parallel degree is determined as
follows:
– If PARALLEL DEGREE deg is specified, deg is passed.
– If only PARALLEL is specified, then a constant indicating that the default degree of
parallelism was specified, is passed.
– If the PARALLEL clause is omitted entirely, the operation is performed sequentially.
• If the ODCIIndexCreate() routine returns with the ODCIConst.Success, the index is valid
and usable. If the ODCIIndexCreate() routine returns with ODCIConst.Warning, the index
is valid and usable but a warning message is returned to the user. If the
20-7
Chapter 20
Extensible Indexing - System-Defined Interface Routines
20-8
Chapter 20
Extensible Indexing - System-Defined Interface Routines
• All the tables left after the invocation of ODCIIndexCreate() or ODCIIndexAlter() are
supposed to be system-managed, and the server takes appropriate actions on them
during drop, truncate, or the partition maintenance operations.
• Since this routine handles multiple things, such as creation of a non-partitioned index or
creation of a local index, you must take special care to code it appropriately.
See Also:
• ODCIIndexAlter()
• ODCIIndexCreate()
20.1.5 ODCIIndexDelete()
Invoked when a row is deleted from a table that has a domain index defined on one or more
of its columns.
Syntax
FUNCTION ODCIIndexDelete(
ia ODCIIndexInfo,
rid VARCHAR2,
oldval icoltype,
env ODCIEnv)
RETURN NUMBER
Parameter Description
oldval The value of the indexed column in the deleted row. The data type is identical
to that of the indexed column.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
Usage Notes
• This function should be implemented as a STATIC type method.
• This method should delete index data corresponding to the deleted row from the
appropriate tables or files storing index data.
• Note that the index partition object identifier ODCIIndexInfo.IndexPartitionIden and
the base table partition physical identifier
ODCIIndexInfo.IndexCols(1).TablePartitionIden is passed in for local domain index.
The indextype must use the new DML syntax using the partition number and the provided
20-9
Chapter 20
Extensible Indexing - System-Defined Interface Routines
20.1.6 ODCIIndexDrop()
The ODCIIndexDrop() procedure is invoked when a domain index is dropped explicitly
using a DROP INDEX statement, or implicitly through a DROP TABLE or DROP USER
statement.
Syntax
FUNCTION ODCIIndexDrop(
ia ODCIIndexInfo,
env ODCIEnv)
RETURN NUMBER
Parameter Description
Returns
ODCIConst.Success on success, or ODCIConst.Error on error, or ODCIConst.Warning.
Usage Notes
• This method should be implemented as a static type method.
• This method should drop the tables storing the domain index data.
• For both a non-partitioned domain index and system managed local domain index,
the ODCIIndexDrop() method is invoked only one time. The user need not drop the
index storage tables if the system-managed approach is used. This is done
automatically by the kernel after the call is completed.
• Since it is possible that the domain index is marked FAILED (due to nonstandard
termination of some DDL routine), the ODCIIndexDrop() routine should be capable
of cleaning up partially created domain indexes. When the ODCIIndexDrop()
routine is being executed, the domain index is marked LOADING.
• Note that if the ODCIIndexDrop() routine returns with an ODCIConst.Error or
exception, the DROP INDEX statement fails and the index is marked FAILED. In that
case, there is no mechanism to get rid of the domain index except by using the
FORCE option. If the ODCIIndexDrop() routine returns with ODCIConst.Warning in
the case of an explicit DROP INDEX statement, the operation succeeds but a
warning message is returned to the user.
20-10
Chapter 20
Extensible Indexing - System-Defined Interface Routines
See Also:
ODCIIndexDrop()
20.1.7 ODCIIndexExchangePartition()
This method is invoked when an ALTER TABLE EXCHANGE PARTITION...INCLUDING INDEXES
command is issued on a partitioned table that has a defined local domain index.
Syntax
FUNCTION ODCIIndexExchangePartition(
ia ODCIIndexInfo,
ia1 ODCIIndexInfo,
env ODCIEnv)
RETURN NUMBER
Parameter Description
ia Contains information about the domain index partition to exchange.
ia1 Contains information about the non-partitioned domain index.
env The environment handle passed to the routine
Returns
ODCIConst.Success on success, or ODCIConst.Error on error, or ODCIConst.Warning.
Usage Notes
• The function should be implemented as a STATIC type method.
• This method should handle both converting a partition of a domain index into a non-
partitioned domain index and converting a non-partitioned index to a partition of a
partitioned domain index.
• When exchanging composite partitions of a composite partitioned table, a call is made to
ODCIIndexExchangePartition for each subpartition of the partition to exchange.
20.1.8 ODCIIndexFetch()
This procedure is invoked repeatedly to retrieve the rows satisfying the operator predicate.
20-11
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Syntax
FUNCTION ODCIIndexFetch(
self IN [OUT] impltype,
nrows IN NUMBER,
rids OUT ODCIRidList,
env ODCIEnv)
RETURN NUMBER
Parameter Description
Is the value of the context returned by the previous call (to
self(IN)
ODCIIndexFetch or to ODCIIndexStart() if this is the first time fetch
is being called for this operator instance
The context that is passed to the next query-time call. Note that this
self(OUT)
parameter does not have to be defined as OUT if the value is not
modified in this routine.
Is the maximum number of result rows that can be returned to Oracle in
nrows
this call
Is the array of row identifiers for the result rows being returned by this
rids
call
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
Usage Notes
• ODCIIndexFetch() returns rows satisfying the operator predicate. That is, it returns
the row identifiers of all the rows for which the operator return value falls within the
specified bounds.
• Each call to ODCIIndexFetch() can return a maximum of nrows number of rows.
The value of nrows passed in is decided by Oracle based on some internal factors.
However, the ODCIIndexFetch() routine can return lesser than nrows number of
rows. The row identifiers are returned through the output rids array. A NULL ROWID
(as an element of the rids array) indicates that all satisfying rows have been
returned.
Assume that there are 3000 rows which satisfy the operator predicate, and that the
value of nrows = 2000. The first invocation of ODCIIndexFetch() can return the
first 2000 rows. The second invocation can return a rid list consisting of the
remaining 1000 rows followed by a NULL element. The NULL value in rid list
indicates that all satisfying rows have now been returned.
• If the context value is changed within this call, the new value is passed in to
subsequent query-time calls.
20-12
Chapter 20
Extensible Indexing - System-Defined Interface Routines
See Also:
• ODCIIndexFetch()
• ODCIIndexStart()
20.1.9 ODCIIndexGetMetadata()
Returns a series of strings of PL/SQL code that comprise the non-dictionary metadata
associated with the index in ia. The routine can pass whatever information is required at
import time. For example, policy, version, preferences, and so on. This method is optional
unless implementation-specific metadata is required.
Syntax
FUNCTION ODCIIndexGetMetadata(
ia IN ODCIIndexInfo,
version IN VARCHAR2,
new_block OUT PLS_INTEGER,
env ODCIEnv)
RETURN VARCHAR2;
Parameter Description
Specifies the index on which export is currently working
ia
Returns
• A null-terminated string containing a piece of an opaque block of PL/SQL code
• A zero-length string indicates no more data; export stops calling the routine
Usage Notes
• This function should be implemented as a static type method.
• The routine is called repeatedly until the return string length is 0. If an index has no
metadata to be exported using PL/SQL, it should return an empty string upon first call.
• This routine can be used to build one or more blocks of anonymous PL/SQL code for
execution by import. Each returned block is invoked independently by import. That is, if a
block fails for any reason at import time, subsequent blocks are still invoked. Therefore
any dependent code should be incorporated within a single block. The size of an
20-13
Chapter 20
Extensible Indexing - System-Defined Interface Routines
individual block of PL/SQL code is limited only by the size of import's read buffer
controlled by its BUFFER parameter.
• The execution of these PL/SQL blocks at import time is considered part of the
associated domain index's creation. Therefore, their execution is dependent upon
the successful import of the index's underlying base table and user's setting of
import's INDEXES=Y/N parameter, as is the creation of the index.
• The routine should not pass back the BEGIN/END strings that open and close the
individual blocks of PL/SQL code; export adds these to mark the individual units of
execution.
• The parameter version is the version number of the currently executing export
client. Since export and import can be used to downgrade a database to the
previous functional point release, it also represents the minimum server version
you can expect to find at import time; it may be higher, but never lower.
• The cartridge developer can use this information to determine what version of
information should be written to the dump file. For example, assume the current
server version is 11.2.0.1.0, but the export version handed in is 11.1.0.1.0. If a
cartridge's metadata changed formats these version, it would know to write the
data to the dump file in 11.1 format, anticipating an import into an 11.2 system.
• The data contained within the strings handed back to export must be completely
platform-independent. That is, they should contain no binary information that may
reflect the endian nature of the export platform, which may be different from the
import platform. Binary information may be passed as hex strings and converted
through RAWTOHEX and HEXTORAW.
• The strings are translated from the export server to export client character set and
are written to the dump file as such. At import time, they are translated from export
client character set to import client character set, then from import client char set
to import server character set when handed over the UPI interface.
• Specifying a target schema in the execution of any of the PL/SQL blocks must be
avoided because it frequently causes an error if you use import's FROMUSER ->
TOUSER schema replication feature. For example, a procedure prototype such as:
PROCEDURE AQ_CREATE ( schema IN VARCHAR2, que_name IN VARCHAR2) ...
20-14
Chapter 20
Extensible Indexing - System-Defined Interface Routines
• Case on database object names must be preserved; that is, objects named 'Foo' and 'FOO'
are distinct objects. Database object names should be enclosed within double quotes ("")
to preserve case.
Error Handling
Any unrecoverable error should raise an exception allowing it to propagate back to
get_domain_index_metadata and thence back to export. This causes export to terminate the
creation of the current index's DDL in the dump file and to move on to the next index.
At import time, failure of the execution of any metadata PL/SQL block causes the associated
index not to be created under the assumption that the metadata creation is an integral part of
the index creation.
20.1.10 ODCIIndexInsert()
Invoked when a row or a set of rows is inserted into a table that has a domain index defined
on one or more of its columns.
Syntax Description
Parameter Description
ridlist A varray (maximum size 32767) containing the list of rowids for the rows
being inserted into the base table
newvallist A varray (maximum size 32767) containing the list of values being inserted
into the indexed column in the base table; these entries have a one-to-one
correspondence with the entries in ridlist
20-15
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
Usage Notes
• This function should be implemented as a STATIC type method.
• This method should insert index data corresponding to the row or set of rows
passed in into the appropriate tables or files storing index data. A NULL value in
ridlist indicates the end of the varray.
• If the indextype is defined WITH ARRAY DML, a batch of rows can be inserted into
the table. In this case, ODCIIndexInsert() is invoked using the second of the two
syntax synopses. Otherwise, the single-row syntax is used.
• Note that the index partition object identifier ODCIIndexInfo.IndexPartitionIden
and the base table partition physical identifier
ODCIIndexInfo.IndexCols(1).TablePartitionIden is passed in for local domain
index. The indextype must use the new DML syntax using the partition number
and the provided DATAOBJ_TO_MAT_PARTITION function to insert into the storage
system partitioned table:
INSERT INTO SP PARTITION (
DATAOBJ_TO_MAT_PARTITION(
base_table_name,
:tab_physical_partid))
VALUES(…);
20.1.11 ODCIIndexStart()
Invoked to start the evaluation of an operator on an indexed column.
Syntax
FUNCTION ODCIIndexStart(
sctx IN OUT <impltype>,
ia ODCIIndexInfo,
pi ODCIPredInfo,
qi ODCIQueryInfo,
strt <opbndtype>,
stop <opbndtype>,
<valargs>,
env ODCIEnv)
RETURN NUMBER
Parameter Description
sctx(IN) The value of the scan context returned by some previous related query-
time call (such as the corresponding ancillary operator, if invoked before
the primary operator); NULL otherwise
sctx(OUT) The context that is passed to the next query-time call; the next query-
time call is to ODCIIndexFetch()
20-16
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Parameter Description
strt The start value of the bounds on the operator return value. The data
type is identical to that of the operator's return value
stop The stop value of the bounds on the operator return value. The data
type is identical to that of the operator's return value.
valargs The value arguments of the operator invocation. The number and data
types of these arguments are identical to those of the value arguments
to the operator.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
Usage Notes
• The function should be implemented as a static method.
• ODCIIndexStart() is invoked to begin the evaluation of an operator on an indexed
column. In particular, the following conditions hold:
– The first argument to the operator is a column which has a domain index defined on
it.
– The indextype of the domain index (specified in ODCIIndexInfo parameter) supports
the current operator.
– All other arguments to the operator are value arguments (literals) which are passed in
through the <valargs> parameters.
• The ODCIIndexStart() method should initialize the index scan as needed (using the
operator-related information in the pi argument) and prepare for the subsequent
invocations of ODCIIndexFetch().
• The strt and stop parameters, with the bndflg value in ODCIPredInfo parameter,
specify the range of values within which the operator return value should lie.
• Bounds for operator return values are specified as follows:
– If the predicate to be evaluated is of the form op LIKE val, the
ODCIIndexPrefixMatch flag is set. In this case, the start key contains the value <val>
and the stop key value is irrelevant.
– If the predicate to be evaluated is of the form op = val, the ODCIIndexExactMatch flag
is set. In this case, the start key contains the value <val> and the stop key value is
irrelevant.
– If the predicate to be evaluated is of the form op > val, startkey contains the value
<val> and stop key value is set to NULL. If the predicate is of the form op >= <val>,
the flag ODCIIndexIncludeStart is also set.
20-17
Chapter 20
Extensible Indexing - System-Defined Interface Routines
– If the predicate to be evaluated is of the form op < val, stop key contains the
value <val> and the start key value is set to NULL. If the predicate is of the form
op <= val, the flag ODCIIndexIncludeStop is also set.
• A context value can be returned to Oracle (through the SELF argument) which is
then passed back to the next query-time call. The next call is to ODCIIndexFetch()
if the evaluation continues, or to ODCIIndexStart() if the evaluation is restarted.
The context value can be used to store the entire evaluation state or just a handle
to the memory containing the state.
• Note that if the same indextype supports multiple operators with different
signatures, multiple ODCIIndexStart() methods must be implemented, one for
each distinct combination of value argument data types. For example, if an
indextype supports three operators:
1. op1(number, number)
2. op1(varchar2, varchar2)
3. op2(number, number)
two ODCIIndexStart routines must be implemented:
– ODCIIndexStart(...., NUMBER)— handles cases (1) and (3) which has a
NUMBER value argument
– ODCIIndexStart(...., VARCHAR2)— handles case (2) which has a VARCHAR2
value argument
• The query information in qi parameter can be used to optimize the domain index
scan, if possible. The query information includes hints that have been specified for
the query and the list of relevant ancillary operators referenced in the query block.
• The index partition object identifier ODCIIndexInfo.IndexPartitionIden and the
base table partition physical identifier
ODCIIndexInfo.IndexCols(1).TablePartitionIden is passed in for local domain
index. The indextype must use the new SQL syntax using the partition number and
the provided DATAOBJ_TO_MAT_PARTITION function to query the corresponding
partition of the storage system partitioned table:
SELECT FROM SP PARTITION(
DATAOBJ_TO_MAT_PARTITION(
base_table_name,
:tab_physical_partid))
WHERE ...;
See Also:
• ODCIIndexFetch()
• ODCIIndexStart()
20.1.12 ODCIIndexUpdate()
Invoked when a row is updated in a table that has a defined domain index on one or
more of its columns.
20-18
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Syntax
FUNCTION ODCIIndexUpdate(
ia ODCIIndexInfo,
rid VARCHAR2,
oldval icoltype,
newval icoltype,
env ODCIEnv)
RETURN NUMBER
Parameter Description
oldval The value of the indexed column before the update. The data type is identical
to that of the indexed column.
newval The value of the indexed column after the update. The data type is identical
to that of the indexed column.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
Usage Notes
• The function should be implemented as a static type method.
• This method should update the tables or files storing the index data for the updated row.
• In addition to a SQL UPDATE statement, a LOB value can be updated through a variety of
WRITE interfaces (see Oracle Database SecureFiles and Large Objects Developer's
Guide). If a domain index is defined on a LOB column or an object type containing a LOB
attribute, the ODCIIndexUpdate routine is called when a LOB locator is implicitly or
explicitly closed after one or more write operations.
• The index partition object identifier, ODCIIndexInfo.IndexPartitionIden, and the base
table partition physical identifier, ODCIIndexInfo.IndexCols(1).TablePartitionIden, is
passed in for local domain indexes. The indextype must use the new DML syntax with the
partition number, and the provided DATAOBJ_TO_MAT_PARTITION() function to update data
in the storage system partitioned table:
UPDATE SP PARTITION
(DATAOBJ_TO_MAT_PARTITION(
base_table_name, :tab_physical_partid))
VALUES(…) SET val = :newval WHERE rowid + :rowid;
20.1.13 ODCIIndexUpdPartMetadata()
Invoked during partition maintenance operations. Patches the indextype metadata tables to
correctly reflect the partition maintenance operation.
20-19
Chapter 20
Extensible Indexing - System-Defined Interface Routines
Syntax
FUNCTION ODCIIndexUpdPartMetadata(
ia ODCIIndexInfo,
palist ODCIPartInfoList,
env ODCIEnv)
Parameter Description
ia The information about the domain index; does not contain partition-
specific information
palist The information about the dropped or added partitions
env The environment handle
Usage Notes
• This method should be implemented as a STATIC type method.
• When an indextype is specified with the SYSTEM MANAGED approach, this method is
invoked on the local domain index of this indextype during partition management
operations.
• SQL DDLs are not allowed in this method.
• The indextype should update its metadata mapping specific to the partitions, if any.
• The palist argument contains a list of partitions that should be dropped or added.
For example, if the base table operation is ALTER TABLE SPLIT PARTITTION P1
INTO P11 AND P12, then the palist would have information about 3 partitions: P1
(drop), P11(add) and P12(add), along with their index partition names and index
partition object identifiers.
• If the ODCIIndexUpdPartMetadata() call raises or returns an error, then the
partition management operation on the base table is rolled back.
20.1.14 ODCIIndexUtilCleanup()
Cleans up temporary states created by ODCIIndexUtilGetTableNames().
Syntax
FUNCTION ODCIIndexUtilCleanup (
context PLS_INTEGER)
Parameter Description
Usage Notes
• The procedure should be implemented as a static type method.
• ODCIIndexUtilCleanup() deletes any temporary state associated with the
parameter context.
• Exceptions raised by ODCIIndexUtilCleanup() are ignored by its caller.
20-20
Chapter 20
Extensible Indexing - System-Defined Interface Routines
See Also:
• ODCIIndexUtilGetTableNames()
• ODCIIndexUtilCleanup()
20.1.15 ODCIIndexUtilGetTableNames()
Determines if the secondary tables of the domain index should be exported/imported. By
default, secondary objects of the domain are not imported or exported. However, if this
interface and ODCIIndexUtilCleanup() are present, the system invokes them.
If this interface is implemented, your application can also invoke it for transportable
tablespace operations.
Syntax
FUNCTION ODCIIndexUtilGetTableNames(
ia sys.odciindexinfo,
read_only PLS_INTEGER,
version varchar2,
context OUT PLS_INTEGER)
RETURN BOOLEAN
Parameter Description
Returns
TRUE if the domain indexes' secondary tables should be exported/imported. Otherwise, the
function returns FALSE.
Usage Notes
• This function should be implemented as a static type method.
• This function should return TRUE or FALSE based on whether the secondary tables should
be exported/imported.
• This function should return TRUE or FALSE based on whether the secondary tables should
be transported. Secondary objects other than tables do not participate in transportable
tablespaces. They must be recreated on the import side when the ODCIIndexCreate()
method is invoked with the ODCI_INDEX_TRANS_TBLSPC bit set in the
ODCIIndexInfo.IndexInfoFlags.
20-21
Chapter 20
Extensible Indexing - System-Defined Interface Routines
See Also:
• ODCIIndexUtilCleanup()
• ODCIIndexCreate()
• ODCIIndexUtilGetTableNames()
20-22
21
Extensible Optimizer Interface
Consider the functions and procedures that comprise the interface to the extensible optimizer.
• Extensible Optimizer Interface
• User-Defined ODCIStats Functions
21-1
Chapter 21
Extensible Optimizer Interface
5. Suppose that the optimizer decides not to use the domain index because it is too
expensive. Then it calls the user-defined cost function for the functional
implementation of the operator as follows:
stat1.ODCIStatsFunctionCost (
ODCIFuncInfo('HR', 'Contains_fn', NULL, 1),
cost,
ODCIArgDescList( ODCIArgDesc(ODCIConst.ArgCol,
'T', 'HR', '"RESUME"', NULL, NULL, NULL),
ODCIArgDesc(ODCIConst.ArgLit,
NULL, NULL, NULL, NULL, NULL, NULL)),
21-2
Chapter 21
User-Defined ODCIStats Functions
NULL,
'ORACLE')
SELECT STATEMENT
Function Description
ODCIGetInterfaces() Discover which version of the ODCIStats interface the
user has implemented.
ODCIStatsCollect() Called by the DBMS_STATS package to collect user-defined
statistics on a table, a partition of a table, an index, or a
partition of an index.
ODCIStatsDelete() Deletes user-defined statistics on a table, a partition of a
table, an index, or a partition of an index.
21-3
Chapter 21
User-Defined ODCIStats Functions
Function Description
ODCIStatsFunctionCost() Computes the cost of a function.
ODCIStatsExchangePartition() Exchanges domain index statistics when an ALTER TABLE
EXCHANGE PARTITION ... INCLULDING INDEXES
command is issued.
ODCIStatsIndexCost() Calculates the cost of a domain index scan.
ODCIStatsSelectivity() Specifies the selectivity of a predicate.
ODCIStatsTableFunction() Provides cardinality statistics for table functions and input
cursor expressions.
ODCIStatsUpdPartStatistics() Updates statistics during partition maintenance operations.
Patches the domain index statistics.
• ODCIGetInterfaces()
• ODCIStatsCollect()
• ODCIStatsDelete()
• ODCIStatsFunctionCost()
• ODCIStatsExchangePartition()
• ODCIStatsIndexCost()
• ODCIStatsSelectivity()
• ODCIStatsTableFunction()
• ODCIStatsUpdPartStatistics()
21.2.1 ODCIGetInterfaces()
ODCIGetInterfaces is invoked by the server to discover which version of the
ODCIStats interface the user has implemented in the methods of the user-defined
statistics type.
Syntax
FUNCTION ODCIGetInterfaces(
ifclist OUT ODCIObjectList)
RETURN NUMBER;
Returns
ODCIConst.Success on success, ODCIConst.Error otherwise.
21-4
Chapter 21
User-Defined ODCIStats Functions
21.2.2 ODCIStatsCollect()
Called by the DBMS_STATS package to collect user-defined statistics.
Syntax Description
Returns
The function returns ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning.
Usage Notes
• This function should be implemented as a STATIC type method.
• If statistics are being collected for only one partition, the TablePartition field in the
ODCIColInfo type is filled in with the name of the partition. Otherwise (if statistics must be
collected for all the partitions or for the entire table), the TablePartition field is null.
• If the DBMS_STATS package methods are executed to collect user-defined statistics on a
partitioned table, then n+1 ODCIStatsCollect calls are made, where n is the number of
partitions in the table. The first n calls are made with the TablePartition attribute in
ODCIColInfo filled in with the partition name and the ODCIStatsOptions.CallProperty
set to IntermediateCall. The last call is made with ODCIEnv.CallPropertyflag set to
FinalCall to allow you to collect aggregate statistics for the entire table.
21-5
Chapter 21
User-Defined ODCIStats Functions
• If user-defined statistics are being collected for only one partition of the table, two
ODCIStatsCollect calls are made. In the first, you should collect statistics for the
partition. For this call, the TablePartition attribute of the ODCIColInfo structure is
filled in and the ODCIEnv.CallProperty is set to FirstCall.
• In the second call you can update the aggregate statistics of the table based upon
the new statistics collected for the partition. In this call, the
ODCIEnv.CallPropertyflag is set to FinalCall to indicate that it is the second
call. The ODCIColInfo.TablePartition is filled in with the partition name in both
the calls.
• The ODCIStatsCollect() method is invoked only one time for a non-partitioned
domain index, a partitioned domain index and a partition in a domain index. If the
statistics are being collected only for one partition in a domain index, the
IndexPartitionNum field in the ODCIIndexInfo type is filled in with the partition
number. Otherwise, the IndexPartitionNum field is null.
• Because the statistics OUT RAW argument of statistics is not used in the new
interface, the cartridge developer should store the user-defined statistics result in
some user-defined tables.
• If a non-partitioned domain index is being ANALYZEd, the user should collect
statistics for the domain index.
• If a partitioned domain index is being ANALYZEd,
– ODCIEnv.CallProperty = StatsGlobalAndPartition means that the user
should collect statistics for all partitions in the domain index and then
aggregate statistics of the domain index based upon the statistics collected for
all the partitions
– ODCIEnv.CallProperty = StatsGlobal means that the user should aggregate
domain index statistics from the statistics of all the domain index partitions.
– ODCIEnv.CallProperty = StatsPartition means that the user should collect
statistics for all index partitions in the domain index.
• If only one partition of the domain index is being ANALYZEd,
– ODCIEnv.CallProperty = StatsGlobalAndPartition means that the user
should collect statistics for the single index partition and then aggregate
statistics of the domain index based upon the statistics of all the partitions.
– ODCIEnv.CallProperty = StatsGlobal means that the user should aggregate
domain index statistics from the statistics of all the index partitions.
– ODCIEnv.CallProperty = StatsPartition means that the user should collect
statistics for the single index partition.
• Note that when ODCIEnv.CallProperty = StatsGlobalAndPartition or
StatsGlobal, the user should aggregate statistics for the domain index, depending
on the availability of the statistics collected for the other index partitions. If the
statistics for all the index partitions are available, aggregate these statistics. If any
one statistics for an index partition is absent, do nothing.
• To properly collect the statistics for a local system-managed domain index, the
IndexInfoFlags and IndexPartition fields in the ODCIIndexInfo argument and
the CallProperty in the ODCIEnv argument must be selected.
21-6
Chapter 21
User-Defined ODCIStats Functions
Note:
The ODCIConst.SubPartn will never be set if
ODCIIndexInfo.IndexPartition is not set.
21.2.3 ODCIStatsDelete()
ODCIStatsDelete is called to delete user-defined statistics.
Syntax Description
statistics OUT Contains table-level aggregate statistics for a partitioned table or index
env Contains general information about the environment in which the routine
is executing
21-7
Chapter 21
User-Defined ODCIStats Functions
Returns
ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning.
Usage Notes
• This function should be implemented as a STATIC method.
• When the function is called for a non-partitioned table, the statistics argument in
the ODCIStatsDelete interface is ignored.
• If the statistics are being deleted for a partitioned table, the ODCIStatsDelete is
called n+1 times. The first n calls are with the partition name filled in the
ODCIColInfo structure and the ODCIEnv.CallProperty set to IntermediateCall.
The last call is made with the ODCIEnv.CallProperty set to FinalCall.
• In the first call, delete the statistics for the specific partitions; and in the last call
drop or clean up any structures created for holding statistics for the deleted table.
The ODCIColInfo.TablePartition is set to null in the last call. In the first call, the
TablePartition field is filled in.
• If statistics are being deleted for only one partition and the
_minimal_stats_aggregation parameter is set to FALSE, two ODCIStatsDelete
calls are made. In each call, ODCIColInfo.TablePartition is filled in with the
partition name. On the first call, delete any user-defined statistics collected for that
partition. On the second call, update the aggregate statistics for the table.
• If statistics are being deleted for one partition and _minimal_stats_aggregation is
set to TRUE, ODCIStatsDelete is only called one to delete any user-defined
statistics collected for that partition.
• The initial value of _minimal_stats_aggregation is TRUE.
• The ODCIStatsDelete() method is invoked only one time for non-partitioned
domain index, partitioned domain index, or an index partition.
• If the statistics is being deleted for a non-partitioned domain index, the user should
delete user-defined statistics for the domain index.
• If the statistics is being deleted for a partitioned domain index, the user should
delete the aggregated statistics of the domain index and optionally delete user-
defined statistics for all domain index partitions, depending on Options in
ODCIEnv.CallProperty:
– ODCIEnv.CallProperty = StatsGlobalAndPartition means that the user
should delete statistics for all the domain index partitions and aggregated
statistics of the domain index.
– ODCIEnv.CallProperty = StatsGlobal means that the user should delete the
aggregated statistics of the domain index.
– ODCIEnv.CallProperty = StatsPartition is not valid option.
• If the statistics is being deleted for only one partition of the index, the user should
delete user-defined statistics for the index partition.
21.2.4 ODCIStatsFunctionCost()
Computes the cost of a function.
21-8
Chapter 21
User-Defined ODCIStats Functions
Syntax
FUNCTION ODCIStatsFunctionCost(
func ODCIFuncInfo,
cost OUT ODCICost,
args ODCIArgDescList,
list,
env ODCIEnv)
return NUMBER;
args Descriptor of actual arguments with which the function or type method
was called. If the function has n arguments, the args array contains n
elements, each describing the actual arguments of the function or type
method
List of actual parameters to the function or type method; the number,
list
position, and type of each argument must be identical in the function or
type method.
env Contains general information about the environment in which the routine
is executing
Returns
ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning.
Usage Notes
This function should be implemented as a static type method.
21.2.5 ODCIStatsExchangePartition()
Exchanges domain index statistics when an ALTER TABLE EXCHANGE PARTITION ...
INCLULDING INDEXES command is issued.
Syntax
FUNCTION ODCIStatsExchangePartition(
ia ODCIIndexInfo,
ia1 ODCIIndexInfo,
env ODCIEnv)
return NUMBER;
Information about the index of the non-partitioned table with which the
ia1
partition is exchanged
21-9
Chapter 21
User-Defined ODCIStats Functions
env Contains general information about the environment in which the routine
is executing
Returns
ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning
Usage Notes
• This method should be implemented as a STATIC type.
• This method should be capable of converting the statistics associated with a
domain index partition into statistics associated with a non-partitioned domain
index, and the reverse. If the statistics are missing for one of the indexes or index
partitions, the user should be able to delete these statistics.
21.2.6 ODCIStatsIndexCost()
Calculates the cost of a domain index scan, either a scan of the entire index or a scan
of one or more index partitions if a local domain index has been built.
Syntax
FUNCTION ODCIStatsIndexCost(
ia ODCIIndexInfo,
sel NUMBER,
cost OUT ODCICost,
qi ODCIQueryInfo,
pred ODCIPredInfo,
args ODCIArgDescList,
start operator_return_type,
stop operator_return_type,
list,
env ODCIEnv)
return NUMBER;
21-10
Chapter 21
User-Defined ODCIStats Functions
args Descriptor of start, stop, and actual value arguments with which
the operator was called. If the operator has n arguments, the args
array contains n+1 elements, the first element describing the start
value, the second element describing the stop value, and the
remaining n-1 elements describing the actual value arguments of
the operator (that is, the arguments after the first)
Returns
ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning
Usage Notes
• For each table in the query, the optimizer uses partition pruning to determine the range of
partitions that may be accessed. These partitions are called interesting partitions. The
set of interesting partitions for a table is also the set of interesting partitions for all domain
indexes on that table. The cost of a domain index can depend on the set of interesting
partitions, so the optimizer passes a list of interesting index partitions to
ODCIStatsIndexCost in the args argument (the type of this argument, ODCIArgDescList,
is a list of ODCIArgDesc argument descriptor types) for those arguments that are columns.
For non-partitioned domain indexes or for cases where no partition pruning is possible,
no partition list is passed to ODCIStatsIndexCost, and you should assume that the entire
index is accessed.
• The domain index key can contain multiple column arguments (for example, the indexed
column and column arguments from other tables appearing earlier in a join order). For
each column appearing in the index key, the args argument contains the list of interesting
partitions for the table. For example, for an index key
op(T1.c1, T2.c2) = 1
the optimizer passes a list of interesting partitions for tables T1 and T2 if they are
partitioned and there is partition pruning for them.
• This function should be implemented as a static type method.
• Only a single call is made to the ODCIStatsIndexCost() function for queries on
partitioned or non-partitioned tables. For queries on partitioned tables, additional
information is passed in the ODCIStatsIndexCost() function. Note that some partitions in
the list passed to ODCIStatsIndexCost() may not actually be accessed by the query. The
list of interesting partitions chiefly serves to exclude partitions that are definitely not
accessed.
21-11
Chapter 21
User-Defined ODCIStats Functions
See Also:
• ODCIGetInterfaces()
• ODCIStatsIndexCost()
21.2.7 ODCIStatsSelectivity()
Specifies the selectivity of a predicate. The selectivity of a predicate involving columns
from a single table is the fraction of rows of that table that satisfy the predicate. For
predicates involving columns from multiple tables (for example, join predicates), the
selectivity should be computed as a fraction of rows in the Cartesian product of those
tables.
Syntax
FUNCTION ODCIStatsSelectivity(
pred ODCIPredInfo,
sel OUT NUMBER,
args ODCIArgDescList,
start function_return_type,
stop function_return_type,
list,
env ODCIEnv)
return NUMBER;
21-12
Chapter 21
User-Defined ODCIStats Functions
Returns
ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning
Usage Notes
• As in ODCIStatsIndexCost, the args argument contains a list of interesting partitions for
the tables whose columns are referenced in the predicate for which the selectivity has to
be computed. These interesting partitions are partitions that cannot be eliminated by
partition pruning as possible candidates to be accessed. The set of interesting partitions
is passed to the function only if partition pruning has occurred (in other words, the
interesting partitions are a strict subset of all the partitions).
• For example, when ODCIStatsSelectivity is called to compute the selectivity of the
predicate:
f(T1.c1, T2.c2) > 4
the optimizer passes the list of interesting partitions for the table T1 (in the argument
descriptor for column T1.c1) if partition pruning is possible; similarly for the table T2.
If a predicate contains columns from several tables, this information is indicated by the
flag bit PredMultiTable, set in the Flags attribute of the pred argument.
• This function should be implemented as a static type method.
• Users implementing this interface must return 'SYS.ODCISTATS2' in the
ODCIGetInterfaces call.
• The selectivity of a predicate involving columns from a single table is the fraction of rows
of that table that satisfy the predicate. For predicates involving columns from multiple
tables (for example, join predicates), the selectivity should be computed as a fraction of
rows in the Cartesian product of those tables. For tables with partition pruning, the
selectivity should be expressed relative to the cardinalities of the interesting partitions of
the tables involved.
The selectivity of predicates involving columns on partitioned tables is computed relative
to the rows in the interesting partitions. Thus, the selectivity of the predicate
g(T1.c1) < 5
is the percentage of rows in the set of interesting partitions (or all partitions if no partition
pruning is possible) that satisfies this predicate. For predicates with columns from
multiple tables, the selectivity must be relative to the number of rows in the cartesian
product of the tables.
21-13
Chapter 21
User-Defined ODCIStats Functions
Suppose that the number of rows in the interesting partitions is 1000 for T1 and
5000 for T2. The selectivity of this predicate must be expressed as the percentage
of the 5,000,000 rows in the Cartesian product of T1 and T2 that satisfy the
predicate.
• If a predicate contains columns from several tables, this information is indicated by
the flag bit PredMultiTable set in the Flags attribute of the pred argument.
• A selectivity expressed relative to the base cardinalities of the tables involved may
be only an approximation of the true selectivity if cardinalities (and other statistics)
of the tables have been reduced based on single-table predicates or other joins
earlier in the join order. However, this approximation to the true selectivity should
be acceptable to most applications.
• Only one call is made to the ODCIStatsSelectivity function for queries on
partitioned or non-partitioned tables. In the case of queries on partitioned tables,
additional information is passed while calling the ODCIStatsSelectivity function.
21.2.8 ODCIStatsTableFunction()
This function provides cardinality statistics for table functions and input cursor
expressions.
Syntax
STATIC FUNCTION ODCIStatsTableFunction(
func IN SYS.ODCIFuncInfo,
outStats OUT SYS.ODCITabFuncStats,
argDesc IN SYS.ODCIArgDescList,
list)
RETURN NUMBER;
Returns
ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning.
21.2.9 ODCIStatsUpdPartStatistics()
Updates statistics during partition maintenance operations. This lets the statistics type
patch up the domain index statistics to correctly reflect the partition maintenance
operation.
21-14
Chapter 21
User-Defined ODCIStats Functions
Syntax
STATIC FUNCTION ODCIStatsCollect(
ia ODCIIndexInfo,
palist ODCIPartInfoList,
env ODCIEnv)
RETURN NUMBER
Returns
ODCIConst.Success, ODCIConst.Error, or ODCIConst.Warning.
• When the statistics type is specified by the SYSTEM MANAGED approach, then the
ODCIStatsUpdPartStatistics() method is invoked only one time during PMO. Only DML
and query are allowed in the method implementation.
• If the user maintains the domain index statistics in a global non-partitioned table, then the
user should delete the entry for the user-defined statistics for the dropped partition (and
optionally add a NULL entry for added partition). They can then check if
ODCIEnv.CallProperty is StatsGlobalAndPartition or StatsPartition. If
ODCIEnv.CallProperty is StatsGlobalAndPartition then they should aggregate all the
available index partition statistics. If ODCIEnv.CallProperty is StatsPartition they can
simply delete the aggregate statistics, or leave the aggregate statistics as they are.
ODCIEnv.CallProperty cannot be StatsGlobal for this call.
• The user should use the information passed in by the ODCIEnv.CallProperty to
determine the type of statistics to delete and adjust.
• If the method returns ODCIConst.Error, the error is ignored and the partition
management operation continues.
21-15
22
User-Defined Aggregate Functions Interface
These routines must be implemented to define a user-defined aggregate function. The
routines are implemented as methods in an object type. Then the CREATE FUNCTION
statement is used to actually create the aggregate function.
See Also:
Using User-Defined Aggregate Functions
Function Description
ODCIAggregateDelete() Removes an input value from the current group.
ODCIAggregateInitialize() Initializes the aggregation context and instance of the
implementation object type, and returns it as an OUT
parameter.
ODCIAggregateIterate() Iterates through input rows by processing the input
values, updating and then returning the aggregation
context.
Note : ODCIAggregateIterate is not invoked for null
values.
ODCIAggregateMerge() Merges two aggregation contexts into a single object
instance during either serial or parallel evaluation of the
user-defined aggregate.
ODCIAggregateTerminate() Calculates the result of the aggregate computation and
performs all necessary cleanup, such as freeing memory.
ODCIAggregateWrapContext() Integrates all external pieces of the current aggregation
context to make the context self-contained.
• ODCIAggregateDelete()
• ODCIAggregateInitialize()
• ODCIAggregateIterate()
• ODCIAggregateMerge()
22-1
Chapter 22
User-Defined Aggregate Functions
• ODCIAggregateTerminate()
• ODCIAggregateWrapContext()
22.1.1 ODCIAggregateDelete()
Removes an input value from the current group. The routine is invoked by Oracle by
passing in the aggregation context and the value of the input to be removed during It
processes the input value, updates the aggregation context, and returns the context.
This is an optional routine and is implemented as a member method.
Syntax
MEMBER FUNCTION ODCIAggregateDelete(
self IN OUT <impltype>,
val <inputdatatype>)
RETURN NUMBER
self IN OUT As input, the value of the current aggregation context; as output, the
updated value.
val IN The input value that is being removed from the current group.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
22.1.2 ODCIAggregateInitialize()
Initializes the aggregation context and instance of the implementation object type, and
returns it as an OUT parameter. Implement this routine as a static method.
Syntax
STATIC FUNCTION ODCIAggregateInitialize(
actx IN OUT <impltype>)
RETURN NUMBER
actx IN OUT The aggregation context that is initialized by the routine. This value is
NULL for regular aggregation cases. In aggregation over windows,
actx is the context of the previous window. This object instance is
passed in as a parameter to the next aggregation routine.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
22-2
Chapter 22
User-Defined Aggregate Functions
22.1.3 ODCIAggregateIterate()
Iterates through input rows by processing the input values, updating and then returning the
aggregation context. This is a mandatory routine and is implemented as a member method.
Syntax
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT <impltype>,
val <inputdatatype>)
RETURN NUMBER
self IN OUT As input, the value of the current aggregation context; as output, the
updated value.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
22.1.4 ODCIAggregateMerge()
Merges two aggregation contexts into a single object instance during either serial or parallel
evaluation of the user-defined aggregate. This is a mandatory routine and is implemented as
a member method.
Syntax
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT <impltype>,
ctx2 IN <impltype>)
RETURN NUMBER
self IN OUT On input, the value of the first aggregation context; on output, the resulting
value of the two merged aggregation contexts.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
22-3
Chapter 22
User-Defined Aggregate Functions
22.1.5 ODCIAggregateTerminate()
Calculates the result of the aggregate computation and performs all necessary
cleanup, such as freeing memory. Invoked by Oracle as the last step of aggregate
computation. This is a mandatory routine and is implemented as a member method.
Syntax
MEMBER FUNCTION ODCIAggregateTerminate(
self IN <impltype>,
ReturnValue OUT <return_type>,
flags IN number)
RETURN NUMBER
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
See Also:
"Reuse of Aggregation Context for Analytic Functions" for details on setting
the ODCI_AGGREGATE_REUSE_CTX flag bit.
22.1.6 ODCIAggregateWrapContext()
Integrates all external pieces of the current aggregation context to make the context
self-contained. Invoked by Oracle if the user-defined aggregate has been declared to
have external context and is transmitting partial aggregates from child processes. This
is an optional routine and is implemented as a member method.
Syntax
MEMBER FUNCTION ODCIAggregateWrapContext(
self IN OUT <impltype>)
RETURN NUMBER
self IN On input, the value of the current aggregation context; on output, the
self-contained combined aggregation context.
22-4
Chapter 22
User-Defined Aggregate Functions
Returns
ODCIConst.Success on success, or ODCIConst.Error on error.
See Also:
"Handling Large Aggregation Contexts" for more information on using this function
22-5
23
Pipelined and Parallel Table Functions
These routines must be implemented to define pipelined and parallel table functions in C.
See Also:
Using Pipelined and Parallel Table Functions for an overall explanation of pipelined
and parallel table functions
Function Description
ODCITableClose() Performs cleanup operations after scanning a table function.
ODCITableDescribe() Returns describe information for a table function whose return
type is ANYDATASET.
ODCITableFetch() returns the next batch of rows from a table function.
ODCITablePrepare() Prepares the scan context and other query information at compile
time.
ODCITableStart() initializes the scan of a table function.
• ODCITableClose()
• ODCITableDescribe()
• ODCITableFetch()
• ODCITablePrepare()
• ODCITableStart()
23.1.1 ODCITableClose()
ODCITableClose performs cleanup operations after scanning a table function.
Syntax
MEMBER FUNCTION ODCITableClose(
self IN <imptype>)
RETURN NUMBER;
23-1
Chapter 23
Routines for Pipelined and Parallel Table Functions in C
Returns
ODCIConst.Success on success, ODCIConst.Error otherwise.
Usage Notes
• Oracle invokes ODCITableClose after the last fetch call. The scan context is
passed in as a parameter. ODCITableClose then performs any necessary cleanup
operations, such as freeing memory.
• If ODCITablePrepare is implemented, this routine is only called one time, at the
end of query execution, rather than each time the table function exits.
23.1.2 ODCITableDescribe()
ODCITableDescribe returns describe information for a table function whose return type
is ANYDATASET.
Syntax
STATIC FUNCTION ODCITableDescribe(
rtype OUT ANYTYPE,
<args>)
RETURN NUMBER;
rtype OUT The AnyType value that describes the returned rows from the table
function
args IN The set of zero or more user specified arguments for the table
function.
Returns
ODCIConst.Success on success, ODCIConst.Error otherwise.
Usage Notes
• If the optional routine ODCITableDescribe is implemented, Oracle invokes it at
query compilation time to retrieve the specific type information.
• This interface is applicable only for table functions whose return type is
ANYDATASET. The format of elements within the returned collection is conveyed to
Oracle by returning an instance of ANYTYPE. The ANYTYPE instance specifies the
actual structure of the returned rows of the specific query.
• ANYTYPE provides a data type to model the metadata of a row: the names and data
types of all the columns (fields) comprising the row. It also provides a set of
PL/SQL and C interfaces for users to construct and access the metadata
23-2
Chapter 23
Routines for Pipelined and Parallel Table Functions in C
At query compilation time, Oracle invokes the ODCITableDescribe routine. The routine
typically uses the user arguments to figure out the nature of the return rows. In this
example, ODCITableDescribe consults the DTD of the XML documents at the specified
location to determine the appropriate ANYTYPE value to return. Each ANYTYPE instance is
constructed by invoking the constructor APIs with this field name and data type
information.
• Any arguments of the table function that are not constants are passed to
ODCITableDescribe as NULLs because their values are not known at compile time.
See Also:
Transient and Generic Types for a discussion of ANYTYPE, ANYDATA, and ANYDATASET
23.1.3 ODCITableFetch()
ODCITableFetch returns the next batch of rows from a table function.
Syntax
MEMBER FUNCTION ODCITableFetch(
self IN OUT <imptype>,
nrows IN NUMBER,
rws OUT <coll-type>)
RETURN NUMBER;
self IN OUT The in-bound is the scan context set up by previous scan routine invocation;
the outbound is the scan context to be passed to later scan routine
invocations.
nrows IN The number of rows the system expects in the current fetch cycle. The
method can ignore this value and return a different number of rows. If fewer
rows are returned, the method is called again; if more rows are returned,
they are processed in the next cycle.
rws OUT The next batch of rows from the table function. This is returned as an
instance of the same collection type as the return type of the table function.
Returns
ODCIConst.Success on success, ODCIConst.Error otherwise.
Usage Notes
• ODCITableFetch is invoked one or more times by Oracle to retrieve all the rows in the
collection returned by the table function. The scan context is passed in as a parameter.
23-3
Chapter 23
Routines for Pipelined and Parallel Table Functions in C
Typically ODCITableFetch uses the input scan context and computes the next set
of rows to be returned to Oracle. In addition, it may update the scan context
accordingly.
• Returning more rows in each invocation of fetch() reduces the number of fetch
calls that must be made and thus improves performance.
• Oracle calls ODCITableFetch repeatedly until all rows in the table function's
collection have been returned. When all rows have been returned,
ODCITableFetch should return a null collection.
23.1.4 ODCITablePrepare()
Prepares the scan context and other query information at compile time.
Syntax
STATIC FUNCTION ODCITablePrepare(
sctx OUT <imptype>,
tf_info SYS.ODCITabFuncInfo,
args);
sctx OUT The scan context returned by this routine. This value is passed in as a
parameter to the later scan routines. The scan context is an instance
of the object type containing the implementation of the ODCITable
routines.
tf_info Contains the projection information and the return type's table
descriptor object (TDO):
• Attrs (SYS.ODCINumberList): lists the positions of the
referenced attributes of the table function's output collection type
• RefType (SYS.AnyType): for AnyDataSet table functions, this is
the actual return type expected to be returned in the AnyDataSet
collection.
args IN The arguments that are passed to the table function. This method is
invoked at compile time; thus, only literal arguments have values.
Column and expression arguments are passed as null values.
Usage Notes
• This method prepares the scan context based on the information known at compile
time. This scan context is passed to ODCITableStart when it is called at the
beginning of query execution.
• If this optional method is implemented, ODCITableClose is only called one time, at
the end of query execution. Each time the table function is restarted,
ODCITableStart is called and passed the scan context. This allows the table
function to maintain context between restarts, and to perform cleanup operations
only one time at the end of query execution.
23.1.5 ODCITableStart()
ODCITableStart initializes the scan of a table function.
23-4
Chapter 23
Routines for Pipelined and Parallel Table Functions in C
Syntax
STATIC FUNCTION ODCITableStart(
sctx IN OUT <imptype>,
<args>)
RETURN NUMBER;
self IN OUT The scan context returned by this routine. This value is passed in as a
parameter to the later scan routines. The scan context is an instance of the
object type containing the implementation of the ODCITable routines. If
ODCITablePrepare is implemented, the scan context it creates is passed
in to ODCITableStart.
args IN Set of zero or more arguments specified by the user for the table function
rws OUT The next batch of rows from the table function. This is returned as an
instance of the same collection type as the return type of the table function.
Returns
ODCIConst.Success on success, ODCIConst.Error otherwise.
Usage Notes
• If ODCITablePrepare is not implemented, this is the first routine that is invoked to begin
retrieving rows from a table function. This routine typically performs the setup needed for
the scan. The scan context is created (as an object instance sctx) and returned to
Oracle. The arguments to the table function, specified by the user in the SELECT
statement, are passed in as parameters to this routine. If ODCITablePrepare is
implemented, it creates the scan context at compile time, and that scan context is passed
in to this routine.
• Any REF CURSOR arguments of the table function must be declared as SYS_REFCURSOR
type in the declaration of the ODCITableStart method.
23-5
A
User-Managed Local Domain Indexes
The user-managed approach for partitioning domain indexes has been the only method
available until Oracle Database 11g Release 1, when system-managed partitioning was
introduced. The user-managed approach has three significant limitations:
• Because the extensible indexing framework does not store information about the domain
index related objects in the kernel, you must maintain tables and partitions by invoking
user-supplied routines.
• Because the kernel does not support equipartitioned tables, each partition has to have a
set of tables and dependent schema objects, which must be managed programmatically
in the user-managed indexing code.
As the number of partitions increases, the proliferation of domain index storage objects
can become an obstacle to efficient operation. To use a table that contains images and
has 1,000 partitions, an indexing schema that creates 64 bitmap indexes on its storage
table (after it is extended to support local domain indexes) would need create and
manage 1,000 domain index storage tables and 64,000 bitmap indexes.
• During DML and query processing with local domain indexes, you would need a separate
set of cursors for each partition; this is required because each partition has its own set of
tables. As a consequence, applications that use a large number of partitions and require
access to several partitions simultaneously must compile new SQL cursors at run-time,
which impacts performance.
Oracle recommends that you use the system-managed approach, as described in Building
Domain Indexes .
Oracle plans to deprecate the user-managed approach in a future release. Information
provided in this appendix documents the specific differences between the user-managed and
system managed processes and APIs.
• Comparing User-Managed and System-Managed Domain Indexes
• Truncating Domain Indexes
• Creating Indextypes
• Using Domain Indexes for the Indextype
• Partitioning Domain Indexes
• APIs for User-Managed Domain Indexes
A-1
Appendix A
Truncating Domain Indexes
These operations can be implemented by taking actions in the server and by using
a very minimal set of interface routines. The cartridge code can then be relatively
unaware of partition issues.
• The number of objects that must be managed to support local partitioned domain
indexes is identical to the number for non-partitioned domain indexes. For local
partitioned indexes, the domain index storage tables are equipartitioned with
respect to the base tables; therefore, the number of domain index storage tables
does not increase with an increase in the number of partitions.
• A single set of query and DML statements can now access and manipulate the
system-partitioned storage tables, facilitating cursor sharing and enhancing
performance.
A-2
Appendix A
APIs for User-Managed Domain Indexes
• ODCIIndexSplitPartition()
A.6.1 ODCIIndexTruncate()
This is an index definition method. When a user issues a TRUNCATE statement against a table
that contains a column or object type attribute indexed by your indextype, Oracle calls your
ODCIIndexTruncate() method. This method should leave the domain index empty.
Syntax
FUNCTION ODCIIndexTruncate(
ia ODCIIndexInfo,
env ODCIEnv)
RETURN NUMBER
Parameter Description
Returns
ODCIConst.Success on success, or ODCIConst.Error on error, or ODCIConst.Warning.
While truncating a local domain index, the first N+1 calls can return ODCIConst.ErrContinue
too.
Usage Notes
• This function should be implemented as a static type method.
• After this function executes, the domain index should be empty (corresponding to the
empty base table).
• While the ODCIIndexTruncate() routine is being executed, the domain index is marked
LOADING. If the ODCIIndexTruncate() routine returns with an ODCIConst.Error (or
exception), the domain index is marked FAILED. The only operation permitted on FAILED
domain indexes is DROP INDEX, TRUNCATE TABLE or ALTER INDEX REBUILD. If
ODCIIndexTruncate() returns with ODCIConst.Warning, the operation succeeds but a
warning message is returned to the user.
• Every SQL statement executed by ODCIIndexTruncate() is treated as an independent
operation. The changes made by ODCIIndexTruncate() are not guaranteed to be atomic.
• This method is invoked for truncating a non-partitioned index, truncating a local domain
index, and also for truncating a single index partition during ALTER TABLE TRUNCATE
PARTITION.
For truncating a non-partitioned index, the ODCIIndexTruncate() is invoked with the
IndexPartition, TablePartition and callProperty set to NULL.
For truncating a local domain index, the routine is invoked N+2 times, where N is the
number of partitions.
A-3
Appendix A
APIs for User-Managed Domain Indexes
For truncating a single index partition during ALTER TABLE TRUNCATE PARTITION,
this routine is invoked with the IndexPartition and the TablePartition filled in
and the callProperty set to NULL.
See Also:
ODCIIndexTruncate()
A.6.2 ODCIIndexMergePartition()
Invoked when a ALTER TABLE MERGE PARTITION is issued on range partitioned table on
which a domain index is defined.
Syntax
FUNCTION ODCIIndexMergePartition(
ia ODCIIndexInfo,
part_name1 ODCIPartInfo,
part_name2 ODCIPartInfo,
parms VARCHAR2,
env ODCIEnv)
RETURN NUMBER
Parameter Description
ia Contains index and table partition name for one of the partitions to be
merged
part_name1 Contains index and table partition name for the second partition to be
merged
part_name2 Holds index and table partition name for the new merged partition
parms Contains the parameter string for the resultant merged partition,
essentially the default parameter string associated with the index.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error, or ODCIConst.Warning.
Usage Notes
• The function should be implemented as a static type method.
• You should create a new table representing the resultant merged partition and
populate it with data from the merged partitions. Then drop the tables
corresponding to the merged index partitions.
• The newly created partition should pick the default parameter string associated
with the index level. Resulting local index partitions are marked UNUSABLE; you
should not attempt to populate the data in the new partition until after an ALTER
INDEX REBUILD PARTITION call.
A-4
Appendix A
APIs for User-Managed Domain Indexes
• The old table and the dictionary entries for the old index partitions are deleted before the
call to ODCIIndexMergePartition(), so the cartridge code for this routine should not rely
on the existence of this data in the views.
A.6.3 ODCIIndexSplitPartition()
Invoked when an ALTER TABLE SPLIT PARTITION is invoked on a partitioned table where a
domain index is defined.
Syntax
FUNCTION ODCIIndexSplitPartition(
ia ODCIIndexInfo,
part_name1 ODCIPartInfo,
part_name2 ODCIPartInfo,
parms VARCHAR2,
env ODCIEnv)
RETURN NUMBER
Parameter Description
part_name1 Holds the index and table partition names for one of the new partitions
part_name2 Holds the index and table partition names for the other new partition
parms Contains the parameter string for the new partitions, the string associated
with the index partition that is being split.
Returns
ODCIConst.Success on success, or ODCIConst.Error on error, or ODCIConst.Warning.
Usage Notes
• The function should be implemented as a static type method.
• You must to drop the metadata corresponding to the partition that is split, and create
metadata for the two newly created partitions.
• The new tables should pick up the default parameter string associated with the split
partition.
• The index data corresponding to these partitions need not be computed since the
indexes are marked UNUSABLE. The indexes can be built after an ALTER INDEX REBUILD
PARTITION call makes the indexes usable again.
• The old table and the old index partition's dictionary entries are deleted before the call to
ODCIIndexSplitPartition(), so the cartridge code for this routine should not rely on the
existence of this data in the views.
A-5
Index
Symbols binary large object, see BLOB, 6-1
binding, 8-2, 9-1
.so files BLOB, 6-1
naming conventions, 2-5 EMPTY_BLOB function, 6-4
A C
aggregate function, user-defined, 12-1 C and C++
analytic functions, 12-7, 12-8 debugging DLLs, 5-12
analytic functions and external context, 12-8 differences from PL/SQL, 4-5
creating, 12-3 callback
defining, 12-2, 12-3 restrictions, 5-10
example, 12-9 Callback Restrictions, 5-10
external context, 12-8 character large object, see CLOB, 6-1
external context and parallel aggregation, character sets
12-6 support for, 2-9
implementing, 12-3 CLOB, 6-1
large aggregation contexts, 12-5 EMPTY_CLOB function, 6-4
ODCIAggregate interface, 12-1, 22-1 collection types, 1-5
ODCIAggregateDelete, 12-8, 22-2 complex data objects, 1-2
ODCIAggregateInitialize, 12-2, 22-2 configuration files
ODCIAggregateIterate, 12-2, 22-3 naming conventions, 2-5
ODCIAggregateMerge, 12-2, 22-3 configuration files for external procedures, 5-5
ODCIAggregateTerminate, 12-2, 22-4 constructor method, 3-4
ODCIAggregateWrapContext, 12-6, 22-4 content, 1-2
parallel evaluation, 12-5 content of data cartridge, 1-2
reuse for analytic functions, 12-7 context
using, 12-3 inline, 12-6
using materialized views, 12-9 WITH CONTEXT clause, 5-9
aggregate interface, 22-1 conventions
Alias library, 5-1 naming, 2-6
ALL_INDEXTYPE_COMMENTS view, 8-9 corruption of package, 4-6
analytic functions, 12-7, 12-8 cost model, 1-8
ancillary binding, 9-11 CREATE FUNCTION statement, 22-1
ANYTYPE type, 13-24 CREATE LIBRARY
Associating the Extensible Optimizer Methods CREDENTIAL option, 5-2
with Database Objects, 15-49 DIRECTORY object option, 5-2
attributes of object type, 15-1 CREATE TYPE
referencing in method, 4-4 syntax, 1-9
autonomous transaction restriction, 13-8 CREATE TYPE BODY statement, 4-1
CREATE TYPE with OID statement, 3-3
Creating Statistics Table (PowerCartUserStats),
B 15-33
B-tree indexing algorithm, 7-3
B+trees, 1-7
Index-1
Index
Index-2
Index
Index-3
Index
Index-4
Index
statistics type U
definition, 1-1
suggested development approach for data USER_INDEXTYPE_COMMENTS view, 8-9
cartridge, 2-10 user-defined operator, 9-1
user-defined type, 1-5
T
V
table functions
parallel execution of, 13-5, 13-16 view
partitioning input, 13-19 ALL_INDEXTYPE_COMMENTS, 8-9
pipelined, 13-4, 13-6, 13-8 DBA_INDEXTYPE_COMMENTS, 8-9
querying, 13-14 DBA_SECONDARY_OBJECTS, 8-15
tables USER_INDEXTYPE_COMMENTS, 8-9
index-organized, 7-6
termination, ODCIAggregate, 12-2
transient types
W
See ANYTYPE type WITH CONTEXT clause and external procedure,
triggers 5-9
with LOBs, 6-11
Index-5