/Users/deen/code/yugabyte-db/src/yb/yql/cql/ql/ptree/pt_create_index.cc
Line | Count | Source (jump to first uncovered line) |
1 | | //-------------------------------------------------------------------------------------------------- |
2 | | // Copyright (c) YugaByte, Inc. |
3 | | // |
4 | | // Treenode definitions for CREATE INDEX statements. |
5 | | //-------------------------------------------------------------------------------------------------- |
6 | | #include "yb/yql/cql/ql/ptree/pt_create_index.h" |
7 | | |
8 | | #include "yb/client/schema.h" |
9 | | #include "yb/client/table.h" |
10 | | #include "yb/common/schema.h" |
11 | | #include "yb/gutil/strings/ascii_ctype.h" |
12 | | #include "yb/yql/cql/ql/ptree/column_desc.h" |
13 | | #include "yb/yql/cql/ql/ptree/pt_column_definition.h" |
14 | | #include "yb/yql/cql/ql/ptree/pt_expr.h" |
15 | | #include "yb/yql/cql/ql/ptree/pt_option.h" |
16 | | #include "yb/yql/cql/ql/ptree/sem_context.h" |
17 | | |
18 | | DEFINE_bool(cql_raise_index_where_clause_error, false, |
19 | | "Raise unsupported error if where clause is specified for create index"); |
20 | | |
21 | | namespace yb { |
22 | | namespace ql { |
23 | | |
24 | | using std::shared_ptr; |
25 | | using std::to_string; |
26 | | using client::YBColumnSchema; |
27 | | using client::YBSchema; |
28 | | using client::YBTableName; |
29 | | |
30 | | //-------------------------------------------------------------------------------------------------- |
31 | | |
32 | | PTCreateIndex::PTCreateIndex(MemoryContext *memctx, |
33 | | YBLocationPtr loc, |
34 | | bool is_backfill_deferred, |
35 | | bool is_unique, |
36 | | const MCSharedPtr<MCString>& name, |
37 | | const PTQualifiedNamePtr& table_name, |
38 | | const PTListNodePtr& columns, |
39 | | const bool create_if_not_exists, |
40 | | const PTTablePropertyListNodePtr& ordering_list, |
41 | | const PTListNodePtr& covering, |
42 | | const PTExpr::SharedPtr& where_clause) |
43 | | : PTCreateTable(memctx, loc, table_name, columns, create_if_not_exists, ordering_list), |
44 | | is_unique_(is_unique), |
45 | | is_backfill_deferred_(is_backfill_deferred), |
46 | | name_(name), |
47 | | covering_(covering), |
48 | | is_local_(false), |
49 | | column_descs_(memctx), |
50 | | auto_includes_(memctx), |
51 | | where_clause_(where_clause), |
52 | 512 | where_clause_column_refs_(nullptr) { |
53 | 512 | } |
54 | | |
55 | 510 | PTCreateIndex::~PTCreateIndex() { |
56 | 510 | } |
57 | | |
58 | | namespace { |
59 | | |
60 | 175 | CHECKED_STATUS SetupCoveringColumn(PTIndexColumn *node, SemContext *sem_context) { |
61 | 175 | RETURN_NOT_OK(node->SetupCoveringIndexColumn(sem_context)); |
62 | 174 | return Status::OK(); |
63 | 175 | } |
64 | | |
65 | | } // namespace |
66 | | |
67 | 511 | CHECKED_STATUS PTCreateIndex::Analyze(SemContext *sem_context) { |
68 | | // Look up indexed table. |
69 | 511 | bool is_system_ignored; |
70 | 511 | RETURN_NOT_OK(relation_->AnalyzeName(sem_context, ObjectType::TABLE)); |
71 | | |
72 | 511 | RETURN_NOT_OK(sem_context->LookupTable(relation_->ToTableName(), relation_->loc(), |
73 | 511 | true /* write_table */, |
74 | 511 | PermissionType::ALTER_PERMISSION, |
75 | 511 | &table_, &is_system_ignored, |
76 | 511 | &column_descs_)); |
77 | | |
78 | | // Save context state, and set "this" as current create-table statement in the context. |
79 | 508 | SymbolEntry cached_entry = *sem_context->current_processing_id(); |
80 | 508 | sem_context->set_current_create_table_stmt(this); |
81 | | |
82 | | // Analyze index table like a regular table for the primary key definitions. |
83 | | // If flag use_cassandra_authentication is enabled, we will not check for the create permission |
84 | | // on the table because creating an index requires the alter permission on the table. |
85 | 508 | RETURN_NOT_OK(PTCreateTable::Analyze(sem_context)); |
86 | | |
87 | 504 | if (!name_) { |
88 | 52 | string auto_name = relation_->last_name().c_str(); |
89 | | |
90 | 52 | for (const auto& column : hash_columns_) { |
91 | 52 | auto_name += string("_") + column->yb_name(); |
92 | 52 | } |
93 | | |
94 | 52 | for (const auto& column : primary_columns_) { |
95 | 0 | auto_name += string("_") + column->yb_name(); |
96 | 0 | } |
97 | | |
98 | 52 | auto_name += "_idx"; |
99 | 52 | string final_name; |
100 | | |
101 | 1.02k | for (char& c : auto_name) { |
102 | 1.02k | if (ascii_isalnum(c) || c == '_'218 ) { // Accepted a-z, A-Z, 0-9, _. |
103 | 1.02k | final_name += c; |
104 | 1.02k | } |
105 | 1.02k | } |
106 | | |
107 | 52 | LOG(INFO) << "Set automatic name for the new index: " << final_name; |
108 | 52 | name_ = MCMakeShared<MCString>(sem_context->PTreeMem(), final_name.c_str()); |
109 | 52 | } |
110 | | |
111 | | // Add covering columns. |
112 | 504 | if (covering_ != nullptr) { |
113 | 137 | RETURN_NOT_OK((covering_->Apply<SemContext, PTIndexColumn>(sem_context, &SetupCoveringColumn))); |
114 | 137 | } |
115 | | |
116 | | // Add remaining primary key columns from the indexed table. For non-unique index, add the columns |
117 | | // to the primary key of the index table to make the non-unique values unique. For unique index, |
118 | | // they should be added as non-primary-key columns. |
119 | 503 | const YBSchema& schema = table_->schema(); |
120 | 1.78k | for (size_t idx = 0; idx < schema.num_key_columns(); idx++1.28k ) { |
121 | | // Not adding key-column schema.columns(idx) to the INDEX metadata if it is already referred to |
122 | | // by one of the index-columns. |
123 | 1.28k | const MCString key_name(schema.Column(idx).name().c_str(), sem_context->PTempMem()); |
124 | 1.28k | PTColumnDefinition *coldef = sem_context->GetColumnDefinition(key_name); |
125 | 1.28k | if (coldef && coldef->colexpr()->opcode() == TreeNodeOpcode::kPTRef214 ) { |
126 | | // Column is already defined as a part of INDEX. |
127 | 213 | continue; |
128 | 213 | } |
129 | | |
130 | | // Create a new treenode PTIndexColumn for column definition. |
131 | 1.07k | MCSharedPtr<MCString> col_name = |
132 | 1.07k | MCMakeShared<MCString>(sem_context->PSemMem(), key_name.c_str()); |
133 | 1.07k | PTQualifiedName::SharedPtr ref_name = |
134 | 1.07k | PTQualifiedName::MakeShared(sem_context->PSemMem(), loc_ptr(), col_name); |
135 | 1.07k | PTRef::SharedPtr col_ref = PTRef::MakeShared(sem_context->PSemMem(), loc_ptr(), ref_name); |
136 | 1.07k | PTIndexColumn::SharedPtr col = |
137 | 1.07k | PTIndexColumn::MakeShared(sem_context->PSemMem(), loc_ptr(), col_name, col_ref); |
138 | 1.07k | RETURN_NOT_OK(col->Analyze(sem_context)); |
139 | | |
140 | | // Add this key column to INDEX as it is not yet in the INDEX description. |
141 | 1.07k | auto_includes_.push_back(col); |
142 | 1.07k | RETURN_NOT_OK(AppendIndexColumn(sem_context, col.get())); |
143 | 1.07k | } |
144 | | |
145 | | // Check whether the index is local, i.e. whether the hash keys match (including being in the |
146 | | // same order). |
147 | 502 | is_local_ = true; |
148 | 502 | if (schema.num_hash_key_columns() != hash_columns_.size()) { |
149 | 191 | is_local_ = false; |
150 | 311 | } else { |
151 | 311 | int idx = 0; |
152 | 311 | for (const auto& column : hash_columns_) { |
153 | 311 | if (column->yb_name() != column_descs_[idx].name()) { |
154 | 308 | is_local_ = false; |
155 | 308 | break; |
156 | 308 | } |
157 | 3 | idx++; |
158 | 3 | } |
159 | 311 | } |
160 | | |
161 | | // Verify transactions and consistency settings. |
162 | 502 | TableProperties table_properties; |
163 | 502 | RETURN_NOT_OK(ToTableProperties(&table_properties)); |
164 | 502 | if (table_->InternalSchema().table_properties().is_transactional()) { |
165 | 362 | if (!table_properties.is_transactional()) { |
166 | 24 | return sem_context->Error(this, |
167 | 24 | "Transactions must be enabled in an index of a " |
168 | 24 | "transactions-enabled table.", |
169 | 24 | ErrorCode::INVALID_TABLE_DEFINITION); |
170 | 24 | } |
171 | 338 | if (table_properties.consistency_level() == YBConsistencyLevel::USER_ENFORCED) { |
172 | 9 | return sem_context->Error(this, |
173 | 9 | "User-enforced consistency level not allowed in a " |
174 | 9 | "transactions-enabled index.", |
175 | 9 | ErrorCode::INVALID_TABLE_DEFINITION); |
176 | 9 | } |
177 | 338 | } else { |
178 | 140 | if (table_properties.is_transactional()) { |
179 | 18 | return sem_context->Error(this, |
180 | 18 | "Transactions cannot be enabled in an index of a table without " |
181 | 18 | "transactions enabled.", |
182 | 18 | ErrorCode::INVALID_TABLE_DEFINITION); |
183 | 18 | } |
184 | 122 | if (table_properties.consistency_level() != YBConsistencyLevel::USER_ENFORCED) { |
185 | 3 | return sem_context->Error(this, |
186 | 3 | "Consistency level must be user-enforced in an index without " |
187 | 3 | "transactions enabled.", |
188 | 3 | ErrorCode::INVALID_TABLE_DEFINITION); |
189 | 3 | } |
190 | 122 | } |
191 | | |
192 | | // TODO: create local index when co-partition table is available. |
193 | 448 | if (is_local_) { |
194 | 3 | LOG(WARNING) << "Creating local secondary index " << yb_table_name().ToString() |
195 | 3 | << " as global index."; |
196 | 3 | is_local_ = false; |
197 | 3 | } |
198 | | |
199 | | // If partial index (i.e., where clause predicate present in CREATE INDEX), analyze the index's |
200 | | // predicate. |
201 | 448 | if (where_clause_.get()) { |
202 | 132 | IdxPredicateState idx_predicate_state(sem_context->PTempMem(), opcode()); |
203 | 132 | SemState sem_state(sem_context, QLType::Create(BOOL), InternalType::kBoolValue); |
204 | 132 | sem_state.SetIdxPredicateState(&idx_predicate_state); |
205 | 132 | RETURN_NOT_OK(where_clause_->Analyze(sem_context)); |
206 | 129 | where_clause_column_refs_ = idx_predicate_state.column_refs(); |
207 | 129 | } |
208 | | |
209 | | // Restore the context value as we are done with this table. |
210 | 445 | sem_context->set_current_processing_id(cached_entry); |
211 | 445 | if (VLOG_IS_ON(3)) { |
212 | 0 | PrintSemanticAnalysisResult(sem_context); |
213 | 0 | } |
214 | | |
215 | 445 | return Status::OK(); |
216 | 448 | } |
217 | | |
218 | 1.23k | Status PTCreateIndex::AppendIndexColumn(SemContext *sem_context, PTColumnDefinition *column) { |
219 | 1.23k | if (!is_unique_ && sem_context->GetColumnDesc(*column->name())->is_primary()979 ) { |
220 | 857 | return AppendPrimaryColumn(sem_context, column); |
221 | 857 | } |
222 | | |
223 | 380 | return AppendColumn(sem_context, column); |
224 | 1.23k | } |
225 | | |
226 | 0 | void PTCreateIndex::PrintSemanticAnalysisResult(SemContext *sem_context) { |
227 | 0 | PTCreateTable::PrintSemanticAnalysisResult(sem_context); |
228 | 0 | } |
229 | | |
230 | 947 | Status PTCreateIndex::ToTableProperties(TableProperties *table_properties) const { |
231 | 947 | table_properties->SetTransactional(true); |
232 | 947 | table_properties->SetUseMangledColumnName(true); |
233 | 947 | return PTCreateTable::ToTableProperties(table_properties); |
234 | 947 | } |
235 | | |
236 | 890 | const std::string& PTCreateIndex::indexed_table_id() const { |
237 | 890 | return table_->id(); |
238 | 890 | } |
239 | | |
240 | 708 | client::YBTableName PTCreateIndex::yb_table_name() const { |
241 | 708 | return client::YBTableName(YQL_DATABASE_CQL, |
242 | 708 | PTCreateTable::yb_table_name().namespace_name().c_str(), |
243 | 708 | name_->c_str()); |
244 | 708 | } |
245 | | |
246 | 890 | client::YBTableName PTCreateIndex::indexed_table_name() const { |
247 | 890 | return PTCreateTable::yb_table_name(); |
248 | 890 | } |
249 | | |
250 | | } // namespace ql |
251 | | } // namespace yb |