|Oracle® Data Cartridge Developer's Guide
10g Release 1 (10.1)
Part Number B10800-01
Domain indexes are indexes whose behavior is specific to an industry, a business function, or some other special purpose. As the cartridge developer, you specify the details of this behavior, as described in this chapter.
This chapter contains these topics:
Indextypes encapsulate search and retrieval methods for complex domains such as text, spatial, and image processing. An indextype is similar to the indexes that are supplied with the Oracle server. The difference is that you provide the application software that implements the indextype.
An indextype has two major components:
The methods that implement the behavior of the indextype, such as creating and scanning the index
The operators that the indextype supports, such as
To create an indextype:
Define the supported operators and create the functions that implement them
See Also:Chapter 9, " Defining Operators"
In this context:
Interface means a logical set of documented method specifications (not a separate schema object)
ODCIIndex interface means a set of index definition, maintenance, and scan routine specifications
ODCIIndex interface specifies all the routines you must supply to implement an indextype. The routines must be implemented as type methods.
ODCIIndex interface comprises the following method classes:
Index definition methods
Index maintenance methods
Index scan methods
Index metadata method
See Also:Chapter 20, " Extensible Indexing Interface" for method signatures and parameter descriptions
Your index definition methods are called when a user issues a
TRUNCATE statement on an index of your indextype.
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
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.
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
RENAME options. What your method needs to do depends on the nature of your domain index, so the details are left to you as the designer of the indextype.
When a user destroys an index of your indextype by issuing a
INDEX statement, Oracle calls your
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.
Your index maintenance methods are called when users issue
DELETE statements on tables with columns or object type attributes indexed by your indextype.
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.
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.
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.
Your index scan methods specify the index-based implementation for evaluating predicates containing the operators supported by your indextype. Index scans involve methods for initialization, fetching rows or row identifiers, and cleaning up after all rows are returned.
There are two modes of evaluating the operator predicate and returning the resulting set of rows:
Precompute All: Compute the entire result set in
ODCIIndexStart(). Iterate over the results returning a batch of rows from each call to
ODCIIndexFetch(). This mode is applicable to operators that must look at the entire result set to compute ranking, relevance, and so on for each candidate row. It is also possible to return one row at a time if your application requires that.
Incremental Computation: Compute a batch of result rows in each call to
ODCIIndexFetch(). This mode is applicable to operators that can determine the candidate rows one at a time without having to look at the entire result set. It is also possible to return one row at a time if your application requires that.
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
The information on the index and the operator is not passed to the fetch and cleanup methods. Thus,
ODCIIndexStart() must save state data that needs to be shared among the index scan routines and return it through an output
SELF parameter. To share large amounts of state data, allocate cursor-duration memory and return a handle to the memory in the
See Also:Oracle Call Interface Programmer's Guide for information on memory services and maintaining context
As member methods,
ODCIIndexClose() are passed the built-in
SELF parameter, through which they can access the state data.
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
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.
ODCIIndexGetMetadata method is optional. If you implement it, the Export utility calls it to write implementation-specific metadata into the Export dump file. This metadata might be policy information, version information, individual user settings, and so on, which are not stored in the system catalogs. The metadata is written to the dump files as anonymous PL/SQL blocks that are executed at import time immediately prior to the creation of the associated index.
The index interface methods (with the exception of the index definition methods,
ODCIIndexDrop()) are invoked under the same transaction that triggered these actions. Thus, the changes made by these routines are atomic and are committed or aborted based on the parent transaction. To achieve this, there are certain restrictions on the nature of the actions that you can perform in the different indextype routines:
Index definition routines have no restrictions.
Index maintenance routines can only execute Data Manipulation Language statements. These DML statements cannot update the base table on which the domain index is created.
Index scan routines can only execute SQL query statements.
For example, if an
INSERT statement caused the
ODCIIndexInsert() routine to be invoked,
ODCIIndexInsert() runs under the same transaction as
ODCIIndexInsert() routine can execute any number of DML statements (for example, insert into index-organized tables). If the original transaction aborts, all the changes made by the indextype routines are rolled back.
However, if the indextype routines cause changes external to the database (like writing to external files), transaction semantics are not assured.
The index definition routines do not have any restrictions on the nature of actions within them. Consider
ODCIIndexCreate() to understand this difference. A typical set of actions to be performed in
ODCIIndexCreate() could be:
Create an index-organized table
Insert data into the index-organized table
Create a secondary index on a column of the index-organized table
ODCIIndexCreate() to execute an arbitrary sequence of DDL and DML statements,each statement is considered to be an independent operation. Consequently, the changes made by
ODCIIndexCreate() are not guaranteed to be atomic. The same is true for other index-definition routines.
The index maintenance (and scan routines) execute with the same snapshot as the top level SQL statement performing the DML (or query) operation. This keeps the index data processed by the index method consistent with the data in the base tables.
Indextype routines always execute as the owner of the index. To support this, the index access driver dynamically changes user mode to index owner before invoking the indextype routines.
For certain operations, indextype routines might need to store information in tables owned by the indextype designer. The indextype implementation must perform those actions in a separate routine, which is executed using the definer's privileges.
See Also:Oracle Database SQL Reference for details on
This section describes the SQL statements that manipulate indextypes.
See Also:Oracle Database SQL Reference for complete descriptions of these SQL statements
When you have implemented the
ODCIIndex interface and defined the implementation type, you can create a new indextype by specifying the list of operators supported by the indextype and referring to the type that implements the index interface.
Using the information retrieval example, the DDL statement for defining the new indextype
TextIndexType, which supports the
Contains operator and whose implementation is provided by the type
CREATE INDEXTYPE TextIndexType
FOR Contains (VARCHAR2, VARCHAR2) USING TextIndexMethods;
In addition to the
ODCIIndex interface routines, the implementation type must implement the
ODCIGetInterfaces routine. This routine returns the list of names of the interface routines implemented by the type and tells the server the version of these routines. Oracle invokes the
ODCIGetInterfaces routine when
CREATE INDEXTYPE is executed. If the indextype implements the Oracle9i or later version of the routines,
ODCIGetInterfaces must specify
SYS.ODCIINDEX2 in the
OUT parameter. If the indextype implements the Oracle8i version of the routines,
ODCIGetInterfaces must specify
SYS.ODCIINDEX1 in the
OUT parameter. (The Oracle8i routines lack the
ODCIEnv parameter added to many of the routines in Oracle9i.)
To remove the definition of an indextype, use the
DROP statement. For example:
DROP INDEXTYPE TextIndexType;
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.
See Also:"Object Dependencies, Drop Semantics, and Validation" for details on object dependencies and drop semantics
COMMENT statement to supply information about an indextype or operator. For example:
COMMENT ON INDEXTYPE Ordsys.TextIndexType IS 'implemented by the type TextIndexMethods to support the Contains operator';
Comments on indextypes can be viewed in these data dictionary views:
To place a comment on an indextype, the indextype must be in your own schema or you must have the
COMMENT ANY INDEXTYPE privilege.
A domain index can be created on a column of a table just like a B-tree index. However, an indextype must be explicitly specified. For example:
CREATE INDEX ResumeTextIndex ON Employees(resume) INDEXTYPE IS TextIndexType PARAMETERS (':Language English :Ignore the a an');
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.
ALTER INDEX ResumeTextIndex PARAMETERS (':Ignore on');
The parameter string is passed uninterpreted to
ODCIIndexAlter() routine, which takes appropriate actions to alter the domain index. This example specifies an additional stop word to ignore in the text index.
ALTER statement can be used to rename a domain index.
ALTER INDEX ResumeTextIndex RENAME TO ResumeTIdx;
A statement of this form causes Oracle to invoke the
ODCIIndexAlter() method, which takes appropriate actions to rename the domain index.
In addition, the
ALTER statement can be used to rebuild a domain index.
ALTER INDEX ResumeTextIndex REBUILD PARAMETERS (':Ignore of');
ODCIIndexAlter() routine is called as before, but with additional information about the
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.
There is no explicit statement for truncating a domain index. However, when the corresponding table is truncated, your indextype's truncate method is invoked. For example:
TRUNCATE TABLE Employees;
ResumeTextIndex by calling your
To drop an instance of a domain index, use the
INDEX statement. For example:
DROP INDEX ResumeTextIndex;
This results in Oracle calling the
ODCIIndexDrop() method, passing it information about the index.
This section discusses some issues you must consider if your indextype creates domain indexes on index-organized tables. You can use the
IndexOnIOT bit of
IndexInfoFlags in the
ODCIIndexInfo structure to determine if the base table is an IOT.
When the base table of a domain index is an index-organized table, and you want to store rowids for the base table in a table of your own, you should store the rowids in a
UROWID (universal rowid) column if you will be testing rowids for equality.
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 (DBAs) stored with them.)
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
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 following query to determine a suitable size for the
SELECT (SUM(column_length + 3) + 7) FROM user_ind_columns ic, user_indexes i WHERE ic.index_name = i.index_name AND i.index_type = 'IOT - TOP' AND ic.table_ name = <base_table>;
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.
If you maintain a
UROWID column in the index storage table, then you may need to change the type of the rowid bind variable in DML
DELETE statements so that it will work for all kinds of rowids. Converting the rowid argument passed in to a text string and then binding it as a text string works well for both physical and universal rowids. This strategy may help you to code your indextype to work with both regular tables and IOTs.
If you use an index scan-context structure to pass context between
Close, you need to alter this structure. In particular, if you store the rowid define variable for the query in a buffer in this structure, then you need to allocate the maximum size for a
UROWID in this buffer (3800 bytes for universal rowids in byte format, 5072 for universal rowids in character format) unless you know the size of the primary key of the base table in advance or wish to determine it at runtime. You also need to store a bit in the context to indicate if the base table is an IOT, since
ODCIIndexInfo is not available in
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.
All values of a primary key column must be unique, so a domain index defined upon a non-unique column of a table cannot use this column as the primary key of an underlying IOT used to store the index. To work around this, you can add a column in the IOT, holding the index data, to hold a unique sequence number. When a column value is inserted in the table, generate a unique sequence number to go with it; you can then use the indexed column together with the sequence number as the primary key of the IOT. (Note that the sequence-number column cannot be a
UROWID columns cannot be part of a primary key for an IOT.) This approach also preserves the fast access to primary key column values that is a major benefit of IOTs.
For B-tree indexes, users can query the
USER_INDEXES view to get index information. To provide similar support for domain indexes, you can provide domain-specific metadata in the following manner:
Define one or more tables that will contain this meta information. The key column of this table must be a unique identifier for the index. This unique key could be the index name (
schema.index). The remainder of the columns can contain your metadata.
Create views that join the system-defined metadata tables with the index meta tables to provide a comprehensive set of information for each instance of a domain index. It is your responsibility as the indextype designer to provide the view definitions.
Like B-tree and bitmap indexes, domain indexes are exported and subsequently imported when their base tables are exported. However, domain indexes can have implementation-specific metadata associated with them that is not stored in the system catalogs. For example, a text domain index can have associated policy information, a list of irrelevant words, and so on. Export/Import provides a mechanism to move this metadata from the source platform to the target platform.
To move the domain index metadata, the indextype must implement the
ODCIIndexGetMetadata() interface method. When a domain index is being exported, this method is invoked and passed the domain index information. It can return any number of 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.
Note that the
ODCIIndexGetMetadata() is an optional interface routine. You only need it if the domain index has extra metadata to be moved.
See Also:Oracle Database Utilities for information about using Export/Import
The transportable tablespaces feature lets you move tablespaces from one Oracle database into another. You can use transportable tablespaces to move domain index data as an alternative to exporting and importing it.
Moving data using transportable tablespaces can be much faster than performing either an export/import or unload/load of the data because transporting a tablespace only requires copying datafiles and integrating tablespace structural information. Also, you do not need to rebuild the index afterward as you do when loading or importing. You can check for the presence of the
TransTblspc flag in
ODCIIndexInfo to determine whether the
ODCIIndexCreate() call is the result of an imported domain index.
In order to use transportable tablespace for the secondary tables for a domain index, you need to provide two additional ODCI interfaces,
ODCIIndexUtilCleanup, in the implementation type.
See Also:Oracle Database Administrator's Guide for information about using transportable tablespaces
This section discusses issues that affect objects used in domain indexes.
The dependencies among various objects are as follows:
Functions, Packages, and Object Types: referenced by operators and indextypes
Operators: referenced by indextypes, DML, and query SQL Statements
Indextypes: referenced by domain indexes
Domain Indexes: referenced (used implicitly) by DML and query SQL statements
Thus, the order in which these objects must be created, or their definitions exported for future import, is:
Functions, packages, and object types
The drop behavior for an object is as follows:
RESTRICT semantics: if there are any dependent objects the drop operation is disallowed.
FORCE semantics: the object is dropped even in the presence of dependent objects; any dependent objects are recursively marked invalid.
Table 8-1 shows the default and explicit drop options supported for operators and indextypes. The other schema objects are included for completeness and context.
To create an operator and its bindings, you must have
EXECUTE privilege on the function, operator, package, or the type referenced in addition to
To create an indextype, you must have
EXECUTE privilege on the type that implements the indextype in addition to
INDEXTYPE privilege. Also, you must have
EXECUTE privileges on the operators that the indextype supports.
To alter an indextype in your own schema, you must have
CREATE INDEXTYPE system privilege.
To alter an indextype or operator in another user's schema, you must have the
ALTER ANY INDEXTYPE or
ALTER ANY OPERATOR system privilege.
To create a domain index, you must have
EXECUTE privilege on the indextype in addition to
To alter a domain index, you must have
EXECUTE privilege on the indextype.
To use the operators in queries or DML statements, you must have
EXECUTE privilege on the operator and the associated function, package, and type.
To change the implementation type, you must have
EXECUTE privilege on the new implementation type.
A domain index can be built to have discrete index partitions that correspond to the partitions of a range-partitioned table. Such an index is called a local domain index, as opposed to a global domain index, which has no index partitions. Local domain index refers to a partitioned index as a whole, not to the partitions that compose a local domain index.
A local domain index is equipartitioned with the underlying table: all keys in a local domain index refer to rows stored in its corresponding table partition; none refer to rows in other partitions.
Local domain indexes can be created only for range-partitioned heap-organized tables. Local domain indexes cannot be built for hash-partitioned tables or
A local domain index can index only a single column; it cannot index an expression.
You provide for using local domain indexes in the indextype, with the
CREATE INDEXTYPE statement. For example:
CREATE INDEXTYPE TextIndexType FOR Contains (VARCHAR2, VARCHAR2) USING TextIndexMethods WITH LOCAL RANGE PARTITION;
This statement specifies that the implementation type
TextIndexMethods is capable of creating and maintaining local domain indexes. The clause
WITH LOCAL RANGE PARTITION specifies the partitioning method for the base table.
CREATE INDEX statement creates and partitions the index:
CREATE INDEX [schema.]index ON [schema.]table [t.alias] (indexed_column) INDEXTYPE IS indextype [LOCAL [PARTITION [partition [PARAMETERS ('string')]]] [...] ] [PARALLEL parallel_degree] [PARAMETERS ('string')];
LOCAL [PARTITION] clause indicates that the index is a local index on a partitioned table. You can specify partition names or allow Oracle to generate them.
PARALLEL clause specifies that the index partitions are to be created in parallel. The
ODCIIndexCreate routines that correspond to index partition creation are called in parallel.
PARAMETERS clause, specify the parameter string that is passed uninterpreted to the appropriate ODCI indextype routine. The maximum length of the parameter string is 1000 characters.
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.
Once the domain index is created, Oracle invokes the appropriate ODCI routine. If the routine does not return successfully, the domain index is marked
FAILED. The only operations supported on an failed domain index are
DROP INDEX and (for non-local indexes)
The following example creates a local domain index
CREATE INDEX ResumeIndex ON Employees(Resume) INDEXTYPE IS TextIndexType LOCAL;
There are restrictions on creating a local domain index:
The index can only be defined on a single column.
You cannot specify a bitmap or unique domain index.
A specified index partition cannot be dropped explicitly. To drop a local index partition, you must drop the entire local domain index:
DROP INDEX ResumeIndex;
ALTER INDEX statement to perform the following operations on a local domain index:
Rename the top level index
Modify the default parameter string for all the index objects
Modify the parameter string associated with a specific partition
Rename an index partition
Rebuild an index partition
ALTER INDEXTYPE statement lets you change properties and the implementation type of an indextype without having to drop and re-create the indextype, then rebuild all dependent indexes.
See Also:Oracle Database SQL Reference for complete syntax of the SQL statements mentioned in this section
Like a domain index, a partition of a local domain index can be in one or more of several states, listed in Table 8-2.
Table 8-2 Summary of Index States
||The index or the index partition is in this state before and during the execution of the |
||If the |
||Same as for regular indexes: An index on a partitioned table is marked |
||An index is marked |
This property is associated only with an index, never with an index partition.
DML operations cannot be performed on the underlying table if an index partition of a local domain index is in any of these states:
The tables in this section list operations that can be performed on the underlying table of an index and describe the effect, if any, on the index.
Table 8-3 Summary of Table Operations
|DROP table||Drops the table. Drops all the indexes and their corresponding partitions|
|TRUNCATE table||Truncates the table. Truncates all the indexes and the index partitions|
Table 8-4 lists
TABLE operations that do not involve partition maintenance.
Table 8-4 Summary of ALTER TABLE Operations without Partition Maintenance
|ALTER TABLE Operation||Description|
|Modify Partition Unusable local indexes||Marks the local index partition associated with the table partition as |
|Modify Partition Rebuild Unusable local indexes||Rebuilds the local index partitions that are marked |
|Add Partition||Adds a new table partition. Also adds a new local index partition.|
|Drop Partition||Drops a range table partition. Also drops the associated local index partition|
|Truncate Partition||Truncate the table partition. Also truncates the associated local index partition|
Table 8-5 lists
TABLE operations that involve partition maintenance.
Table 8-5 Summary of ALTER TABLE Operations with Partition Maintenance
|ALTER TABLE Operation||Description|
|Move Partition||Moves the base table partition to another tablespace. Corresponding local index partitions are marked |
|Split Partition||Splits a table partition into two partitions. Corresponding local index partition is also split. If the resulting partitions are non-empty, the index partitions are marked |
|Merge Partition||Merges two table partitions into one partition. Corresponding local index partitions should also merge. If the resulting partition contains data, the index partition is marked |
|Exchange Partition Excluding Indexes||Exchanges a table partition with a non-partitioned table. Local index partitions and global indexes are marked |
|Exchange Partition Including Indexes||Exchanges a table partition with a non-partitioned table. Local index partition is exchanged with global index on the non-partitioned table. Index partitions remain |
To support local domain indexes, you must implement the standard
ODCIIndex methods, plus three additional methods that are specific to local domain indexes:
the table must be heap-organized
the domain index cannot be defined on a
To do a direct path load on a domain index defined on an
IOT or on a
Drop the domain index
Do the direct path load in SQL*Loader
Re-create the domain indexes