|
| 1 | +:sectnums: |
| 2 | +:sectnumlevels: 5 |
| 3 | + |
| 4 | +:imagesdir: ./_images |
| 5 | + |
| 6 | += 索引 ONLINE 参数 |
| 7 | + |
| 8 | +== 目的 |
| 9 | + |
| 10 | +IvorySQL 数据库支持在创建索引时使用 `ONLINE` 参数,用于在线创建索引,同时不阻塞DML操作。 |
| 11 | + |
| 12 | +== 实现说明 |
| 13 | + |
| 14 | +=== 数据结构扩展 |
| 15 | + |
| 16 | +`IndexStmt` 新增 `online_keyword` 字段。 |
| 17 | + |
| 18 | +[source,c] |
| 19 | +---- |
| 20 | +bool transformed; /* true when transformIndexStmt is finished */ |
| 21 | +bool concurrent; /* should this be a concurrent index build? */ |
| 22 | +bool online_keyword; /* was ONLINE keyword used (as opposed to CONCURRENTLY)? */ |
| 23 | +---- |
| 24 | + |
| 25 | +=== 语法与解析 |
| 26 | + |
| 27 | +==== 语法规则扩展 |
| 28 | + |
| 29 | +在 `ora_gram.y` 文件中引入一个弹性选项列表 `create_index_opt_list` / `create_index_opt`,类似现有的 `rebuild_index_opt_list`: |
| 30 | + |
| 31 | +[source,yacc] |
| 32 | +---- |
| 33 | +create_index_opt_list: |
| 34 | + create_index_opt_list create_index_opt { $$ = lappend($1, $2); } |
| 35 | + | /* EMPTY */ { $$ = NIL; } |
| 36 | +; |
| 37 | +
|
| 38 | +create_index_opt: |
| 39 | + ONLINE |
| 40 | + { $$ = makeDefElem("online", (Node *) makeBoolean(true), @1); } |
| 41 | + | TABLESPACE name |
| 42 | + { $$ = makeDefElem("tablespace", (Node *) makeString($2), @1); } |
| 43 | +; |
| 44 | +---- |
| 45 | + |
| 46 | +修改 `IndexStmt` 的两个产生式,将 `OptTableSpace` 替换为 `create_index_opt_list`,并在 action 中从选项列表中提取 `online` 和 `tablespace`。 |
| 47 | +[source,yacc] |
| 48 | +---- |
| 49 | +IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_single_name |
| 50 | + ON relation_expr access_method_clause '(' index_params ')' |
| 51 | + opt_include opt_unique_null_treatment opt_reloptions |
| 52 | + create_index_opt_list where_clause |
| 53 | + { |
| 54 | + IndexStmt *n = makeNode(IndexStmt); |
| 55 | + bool online = false; |
| 56 | + bool online_seen = false; |
| 57 | + char *tablespace = NULL; |
| 58 | + ListCell *lc; |
| 59 | +
|
| 60 | + /* 解析 create_index_opt_list,提取 online 和 tablespace */ |
| 61 | + foreach(lc, $15) |
| 62 | + { |
| 63 | + DefElem *opt = (DefElem *) lfirst(lc); |
| 64 | +
|
| 65 | + if (strcmp(opt->defname, "online") == 0) |
| 66 | + { |
| 67 | + if (online_seen) |
| 68 | + ereport(ERROR, |
| 69 | + (errcode(ERRCODE_SYNTAX_ERROR), |
| 70 | + errmsg("ONLINE specified multiple times"), |
| 71 | + parser_errposition(opt->location))); |
| 72 | + online = defGetBoolean(opt); |
| 73 | + online_seen = true; |
| 74 | + } |
| 75 | + else if (strcmp(opt->defname, "tablespace") == 0) |
| 76 | + { |
| 77 | + if (tablespace != NULL) |
| 78 | + ereport(ERROR, |
| 79 | + (errcode(ERRCODE_SYNTAX_ERROR), |
| 80 | + errmsg("TABLESPACE specified multiple times"), |
| 81 | + parser_errposition(opt->location))); |
| 82 | + tablespace = defGetString(opt); |
| 83 | + } |
| 84 | + } |
| 85 | +
|
| 86 | + /* CONCURRENTLY 与 ONLINE 互斥 */ |
| 87 | + if ($4 && online) |
| 88 | + ereport(ERROR, |
| 89 | + (errcode(ERRCODE_SYNTAX_ERROR), |
| 90 | + errmsg("cannot use both CONCURRENTLY and ONLINE"), |
| 91 | + parser_errposition(@4))); |
| 92 | +
|
| 93 | + n->unique = $2; |
| 94 | + n->concurrent = $4 || online; |
| 95 | + n->online_keyword = online; |
| 96 | + n->idxname = $5; |
| 97 | + n->relation = $7; |
| 98 | + n->accessMethod = $8; |
| 99 | + n->indexParams = $10; |
| 100 | + n->indexIncludingParams = $12; |
| 101 | + n->nulls_not_distinct = !$13; |
| 102 | + n->options = $14; /* opt_reloptions (WITH clause) */ |
| 103 | + n->tableSpace = tablespace; |
| 104 | + n->whereClause = $16; |
| 105 | + n->excludeOpNames = NIL; |
| 106 | + n->idxcomment = NULL; |
| 107 | + n->indexOid = InvalidOid; |
| 108 | + n->oldNumber = InvalidRelFileNumber; |
| 109 | + n->oldCreateSubid = InvalidSubTransactionId; |
| 110 | + n->oldFirstRelfilelocatorSubid = InvalidSubTransactionId; |
| 111 | + n->primary = false; |
| 112 | + n->isconstraint = false; |
| 113 | + n->deferrable = false; |
| 114 | + n->initdeferred = false; |
| 115 | + n->transformed = false; |
| 116 | + n->if_not_exists = false; |
| 117 | + n->reset_default_tblspc = false; |
| 118 | + $$ = (Node *) n; |
| 119 | + } |
| 120 | +---- |
| 121 | + |
| 122 | +==== 执行层修改(`indexcmds.c`) |
| 123 | + |
| 124 | +`DefineIndex()` 中已有临时表降级逻辑(`concurrent = false` when temp table),需在相同位置补充分区表的降级逻辑: |
| 125 | + |
| 126 | +[source,c] |
| 127 | +---- |
| 128 | +/* 已有:临时表降级 */ |
| 129 | +if (stmt->concurrent && get_rel_persistence(tableId) != RELPERSISTENCE_TEMP) |
| 130 | + concurrent = true; |
| 131 | +else |
| 132 | + concurrent = false; |
| 133 | +
|
| 134 | +/* 新增:分区表降级 |
| 135 | + * Oracle 对分区表 CREATE INDEX ONLINE 返回成功(全局非分区索引)。 |
| 136 | + * PostgreSQL 的 concurrent + partitioned 路径会报错,因此在 Oracle 解析器 |
| 137 | + * 模式下(stmt->online_keyword = true)对分区表静默降级为普通构建。 |
| 138 | + */ |
| 139 | +if (concurrent && partitioned && stmt->online_keyword) |
| 140 | + concurrent = false; |
| 141 | +---- |
| 142 | + |
0 commit comments