ONLINE Parameter for CREATE INDEX
1. Purpose
IvorySQL supports the ONLINE parameter when creating an index, allowing indexes to be built online without blocking DML operations.
2. Implementation Notes
2.1. Data Structure Extension
A new online_keyword field is added to IndexStmt.
bool transformed; /* true when transformIndexStmt is finished */
bool concurrent; /* should this be a concurrent index build? */
bool online_keyword; /* was ONLINE keyword used (as opposed to CONCURRENTLY)? */
2.2. Syntax and Parsing
2.2.1. Grammar Rule Extension
A flexible option list create_index_opt_list / create_index_opt is introduced in ora_gram.y, similar to the existing rebuild_index_opt_list:
create_index_opt_list:
create_index_opt_list create_index_opt { $$ = lappend($1, $2); }
| /* EMPTY */ { $$ = NIL; }
;
create_index_opt:
ONLINE
{ $$ = makeDefElem("online", (Node *) makeBoolean(true), @1); }
| TABLESPACE name
{ $$ = makeDefElem("tablespace", (Node *) makeString($2), @1); }
;
The two productions for IndexStmt are modified to replace OptTableSpace with create_index_opt_list, extracting online and tablespace from the option list in the action:
IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_single_name
ON relation_expr access_method_clause '(' index_params ')'
opt_include opt_unique_null_treatment opt_reloptions
create_index_opt_list where_clause
{
IndexStmt *n = makeNode(IndexStmt);
bool online = false;
bool online_seen = false;
char *tablespace = NULL;
ListCell *lc;
/* Parse create_index_opt_list, extract online and tablespace */
foreach(lc, $15)
{
DefElem *opt = (DefElem *) lfirst(lc);
if (strcmp(opt->defname, "online") == 0)
{
if (online_seen)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("ONLINE specified multiple times"),
parser_errposition(opt->location)));
online = defGetBoolean(opt);
online_seen = true;
}
else if (strcmp(opt->defname, "tablespace") == 0)
{
if (tablespace != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("TABLESPACE specified multiple times"),
parser_errposition(opt->location)));
tablespace = defGetString(opt);
}
}
/* CONCURRENTLY and ONLINE are mutually exclusive */
if ($4 && online)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use both CONCURRENTLY and ONLINE"),
parser_errposition(@4)));
n->unique = $2;
n->concurrent = $4 || online;
n->online_keyword = online;
n->idxname = $5;
n->relation = $7;
n->accessMethod = $8;
n->indexParams = $10;
n->indexIncludingParams = $12;
n->nulls_not_distinct = !$13;
n->options = $14; /* opt_reloptions (WITH clause) */
n->tableSpace = tablespace;
n->whereClause = $16;
n->excludeOpNames = NIL;
n->idxcomment = NULL;
n->indexOid = InvalidOid;
n->oldNumber = InvalidRelFileNumber;
n->oldCreateSubid = InvalidSubTransactionId;
n->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
n->primary = false;
n->isconstraint = false;
n->deferrable = false;
n->initdeferred = false;
n->transformed = false;
n->if_not_exists = false;
n->reset_default_tblspc = false;
$$ = (Node *) n;
}
2.2.2. Executor Layer Changes (indexcmds.c)
DefineIndex() already contains downgrade logic for temporary tables (concurrent = false when temp table). The same location must also be updated with downgrade logic for partitioned tables:
/* Existing: downgrade for temp tables */
if (stmt->concurrent && get_rel_persistence(tableId) != RELPERSISTENCE_TEMP)
concurrent = true;
else
concurrent = false;
/* New: downgrade for partitioned tables.
* Oracle returns success for CREATE INDEX ONLINE on partitioned tables
* (global non-partitioned index). PostgreSQL's concurrent + partitioned
* path raises an error, so in Oracle parser mode (stmt->online_keyword = true)
* we silently downgrade to a regular build for partitioned tables.
*/
if (concurrent && partitioned && stmt->online_keyword)
concurrent = false;