001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.sequencer.ddl.dialect.oracle; 017 018import static org.modeshape.sequencer.ddl.StandardDdlLexicon.CREATE_VIEW_QUERY_EXPRESSION; 019import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DROP_BEHAVIOR; 020import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DROP_OPTION; 021import static org.modeshape.sequencer.ddl.StandardDdlLexicon.NEW_NAME; 022import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_ADD_COLUMN_DEFINITION; 023import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_ALTER_TABLE_STATEMENT; 024import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_ALTER_COLUMN_DEFINITION; 025import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_COLUMN_DEFINITION; 026import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_COLUMN_REFERENCE; 027import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_CREATE_VIEW_STATEMENT; 028import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_DROP_COLUMN_DEFINITION; 029import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_DROP_TABLE_CONSTRAINT_DEFINITION; 030import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_DROP_TABLE_STATEMENT; 031import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_GRANT_STATEMENT; 032import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_STATEMENT_OPTION; 033import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_TABLE_REFERENCE; 034import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_UNKNOWN_STATEMENT; 035import static org.modeshape.sequencer.ddl.StandardDdlLexicon.VALUE; 036import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.AUTHID_VALUE; 037import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.BITMAP_INDEX; 038import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.IN_OUT_NO_COPY; 039import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_CLUSTER_STATEMENT; 040import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_DATABASE_STATEMENT; 041import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_DIMENSION_STATEMENT; 042import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_DISKGROUP_STATEMENT; 043import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_FUNCTION_STATEMENT; 044import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_INDEXTYPE_STATEMENT; 045import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_INDEX_STATEMENT; 046import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_JAVA_STATEMENT; 047import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_MATERIALIZED_STATEMENT; 048import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_OPERATOR_STATEMENT; 049import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_OUTLINE_STATEMENT; 050import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_PACKAGE_STATEMENT; 051import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_PROCEDURE_STATEMENT; 052import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_PROFILE_STATEMENT; 053import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_RESOURCE_STATEMENT; 054import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_ROLE_STATEMENT; 055import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_ROLLBACK_STATEMENT; 056import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_SEQUENCE_STATEMENT; 057import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_SESSION_STATEMENT; 058import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_SYSTEM_STATEMENT; 059import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_TABLESPACE_STATEMENT; 060import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_TRIGGER_STATEMENT; 061import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_TYPE_STATEMENT; 062import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_USER_STATEMENT; 063import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ALTER_VIEW_STATEMENT; 064import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ANALYZE_STATEMENT; 065import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ASSOCIATE_STATISTICS_STATEMENT; 066import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_AUDIT_STATEMENT; 067import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_BACKSLASH_TERMINATOR; 068import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_COMMENT_ON_STATEMENT; 069import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_COMMIT_STATEMENT; 070import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_BITMAP_JOIN_INDEX_STATEMENT; 071import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_CLUSTER_INDEX_STATEMENT; 072import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_CLUSTER_STATEMENT; 073import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_CONTEXT_STATEMENT; 074import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_CONTROLFILE_STATEMENT; 075import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_DATABASE_STATEMENT; 076import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_DIMENSION_STATEMENT; 077import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_DIRECTORY_STATEMENT; 078import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_DISKGROUP_STATEMENT; 079import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_FUNCTION_STATEMENT; 080import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_INDEXTYPE_STATEMENT; 081import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_JAVA_STATEMENT; 082import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_LIBRARY_STATEMENT; 083import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_MATERIALIZED_VIEW_LOG_STATEMENT; 084import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_MATERIALIZED_VIEW_STATEMENT; 085import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_OPERATOR_STATEMENT; 086import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_OUTLINE_STATEMENT; 087import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_PACKAGE_STATEMENT; 088import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_PFILE_STATEMENT; 089import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_PROCEDURE_STATEMENT; 090import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_PROFILE_STATEMENT; 091import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_ROLE_STATEMENT; 092import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_ROLLBACK_STATEMENT; 093import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_SEQUENCE_STATEMENT; 094import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_SPFILE_STATEMENT; 095import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_SYNONYM_STATEMENT; 096import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_TABLESPACE_STATEMENT; 097import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_TABLE_INDEX_STATEMENT; 098import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_TRIGGER_STATEMENT; 099import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_TYPE_STATEMENT; 100import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_CREATE_USER_STATEMENT; 101import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DISASSOCIATE_STATISTICS_STATEMENT; 102import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_CLUSTER_STATEMENT; 103import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_CONTEXT_STATEMENT; 104import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_DATABASE_STATEMENT; 105import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_DIMENSION_STATEMENT; 106import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_DIRECTORY_STATEMENT; 107import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_DISKGROUP_STATEMENT; 108import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_FUNCTION_STATEMENT; 109import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_INDEXTYPE_STATEMENT; 110import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_INDEX_STATEMENT; 111import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_JAVA_STATEMENT; 112import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_LIBRARY_STATEMENT; 113import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_MATERIALIZED_STATEMENT; 114import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_OPERATOR_STATEMENT; 115import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_OUTLINE_STATEMENT; 116import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_PACKAGE_STATEMENT; 117import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_PROCEDURE_STATEMENT; 118import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_PROFILE_STATEMENT; 119import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_ROLE_STATEMENT; 120import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_ROLLBACK_STATEMENT; 121import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_SEQUENCE_STATEMENT; 122import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_SYNONYM_STATEMENT; 123import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_TABLESPACE_STATEMENT; 124import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_TRIGGER_STATEMENT; 125import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_TYPE_STATEMENT; 126import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_DROP_USER_STATEMENT; 127import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_EXPLAIN_PLAN_STATEMENT; 128import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_FLASHBACK_STATEMENT; 129import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_FUNCTION_PARAMETER; 130import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_LOCK_TABLE_STATEMENT; 131import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_MERGE_STATEMENT; 132import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_NOAUDIT_STATEMENT; 133import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_PURGE_STATEMENT; 134import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_RENAME_COLUMN; 135import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_RENAME_CONSTRAINT; 136import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_RENAME_STATEMENT; 137import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_REVOKE_STATEMENT; 138import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_ROLLBACK_STATEMENT; 139import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_SAVEPOINT_STATEMENT; 140import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_SET_CONSTRAINTS_STATEMENT; 141import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_SET_CONSTRAINT_STATEMENT; 142import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_SET_ROLE_STATEMENT; 143import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_SET_TRANSACTION_STATEMENT; 144import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.TYPE_TRUNCATE_STATEMENT; 145import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.UNIQUE_INDEX; 146import java.util.ArrayList; 147import java.util.List; 148import java.util.Locale; 149import org.modeshape.common.text.ParsingException; 150import org.modeshape.common.text.Position; 151import org.modeshape.common.text.TokenStream; 152import org.modeshape.common.util.CheckArg; 153import org.modeshape.sequencer.ddl.DdlParserProblem; 154import org.modeshape.sequencer.ddl.DdlSequencerI18n; 155import org.modeshape.sequencer.ddl.DdlTokenStream; 156import org.modeshape.sequencer.ddl.DdlTokenStream.DdlTokenizer; 157import org.modeshape.sequencer.ddl.StandardDdlLexicon; 158import org.modeshape.sequencer.ddl.StandardDdlParser; 159import org.modeshape.sequencer.ddl.datatype.DataType; 160import org.modeshape.sequencer.ddl.datatype.DataTypeParser; 161import org.modeshape.sequencer.ddl.node.AstNode; 162 163/** 164 * Oracle-specific DDL Parser. Includes custom data types as well as custom DDL statements. 165 */ 166public class OracleDdlParser extends StandardDdlParser 167 implements OracleDdlConstants, OracleDdlConstants.OracleStatementStartPhrases { 168 169 /** 170 * The Oracle parser identifier. 171 */ 172 public static final String ID = "ORACLE"; 173 174 static List<String[]> oracleDataTypeStrings = new ArrayList<String[]>(); 175 176 public OracleDdlParser() { 177 super(); 178 179 setDatatypeParser(new OracleDataTypeParser()); 180 initialize(); 181 } 182 183 /** 184 * {@inheritDoc} 185 * 186 * @see org.modeshape.sequencer.ddl.StandardDdlParser#areNextTokensCreateTableOptions(org.modeshape.sequencer.ddl.DdlTokenStream) 187 */ 188 @Override 189 protected boolean areNextTokensCreateTableOptions( final DdlTokenStream tokens ) throws ParsingException { 190 // current token can't be a terminator or the next n-tokens can't be a statement 191 return (tokens.hasNext() && !isTerminator(tokens) && (tokens.computeNextStatementStartKeywordCount() == 0)); 192 } 193 194 /** 195 * {@inheritDoc} 196 * 197 * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseNextCreateTableOption(org.modeshape.sequencer.ddl.DdlTokenStream, 198 * org.modeshape.sequencer.ddl.node.AstNode) 199 */ 200 @Override 201 protected void parseNextCreateTableOption( final DdlTokenStream tokens, 202 final AstNode tableNode ) throws ParsingException { 203 final String tableProperty = tokens.consume(); 204 boolean processed = false; 205 206 // if token is a number add it to previous option 207 if (tableProperty.matches("\\b\\d+\\b")) { 208 final List<AstNode> options = tableNode.getChildren(TYPE_STATEMENT_OPTION); 209 210 if (!options.isEmpty()) { 211 final AstNode option = options.get(options.size() - 1); 212 final String currValue = (String)option.getProperty(VALUE); 213 option.setProperty(VALUE, currValue + SPACE + tableProperty); 214 processed = true; 215 } 216 } 217 218 if (!processed) { 219 final AstNode tableOption = nodeFactory().node("option", tableNode, TYPE_STATEMENT_OPTION); 220 tableOption.setProperty(VALUE, tableProperty); 221 } 222 } 223 224 private void initialize() { 225 setTerminator(DEFAULT_TERMINATOR); 226 227 setDoUseTerminator(true); 228 229 oracleDataTypeStrings.addAll(OracleDataTypes.CUSTOM_DATATYPE_START_PHRASES); // 230 } 231 232 /** 233 * {@inheritDoc} 234 * 235 * @see org.modeshape.sequencer.ddl.StandardDdlParser#getId() 236 */ 237 @Override 238 public String getId() { 239 return ID; 240 } 241 242 /** 243 * {@inheritDoc} 244 * 245 * @see org.modeshape.sequencer.ddl.StandardDdlParser#getIdentifyingKeywords() 246 */ 247 @Override 248 public String[] getIdentifyingKeywords() { 249 return new String[] {getId(), "spool.log"}; 250 } 251 252 /** 253 * {@inheritDoc} 254 * 255 * @see org.modeshape.sequencer.ddl.StandardDdlParser#initializeTokenStream(org.modeshape.sequencer.ddl.DdlTokenStream) 256 */ 257 @Override 258 protected void initializeTokenStream( DdlTokenStream tokens ) { 259 super.initializeTokenStream(tokens); 260 tokens.registerKeyWords(CUSTOM_KEYWORDS); 261 tokens.registerKeyWords(OracleDataTypes.CUSTOM_DATATYPE_START_WORDS); 262 tokens.registerStatementStartPhrase(ALTER_PHRASES); 263 tokens.registerStatementStartPhrase(CREATE_PHRASES); 264 tokens.registerStatementStartPhrase(DROP_PHRASES); 265 tokens.registerStatementStartPhrase(MISC_PHRASES); 266 tokens.registerStatementStartPhrase(SET_PHRASES); 267 } 268 269 @Override 270 protected void rewrite( DdlTokenStream tokens, 271 AstNode rootNode ) { 272 assert tokens != null; 273 assert rootNode != null; 274 275 // We may have a prepare statement that is followed by a missing terminator node 276 // We also may have nodes that have an extra terminator node representing the '/' backslash 277 // These nodes will have type "TYPE_BACKSLASH_TERMINATOR". 278 279 List<AstNode> copyOfNodes = new ArrayList<AstNode>(rootNode.getChildren()); 280 281 AstNode complexNode = null; 282 283 for (AstNode child : copyOfNodes) { 284 285 if ((complexNode != null && nodeFactory().hasMixinType(child, TYPE_UNKNOWN_STATEMENT)) 286 || (complexNode != null && nodeFactory().hasMixinType(child, TYPE_BACKSLASH_TERMINATOR))) { 287 mergeNodes(tokens, complexNode, child); 288 rootNode.removeChild(child); 289 } else { 290 complexNode = null; 291 } 292 293 if (nodeFactory().hasMixinType(child, TYPE_CREATE_FUNCTION_STATEMENT) 294 || nodeFactory().hasMixinType(child, TYPE_CREATE_TRIGGER_STATEMENT) 295 || nodeFactory().hasMixinType(child, TYPE_CREATE_LIBRARY_STATEMENT) 296 || nodeFactory().hasMixinType(child, TYPE_CREATE_PACKAGE_STATEMENT) 297 || nodeFactory().hasMixinType(child, TYPE_CREATE_PROCEDURE_STATEMENT)) { 298 complexNode = child; 299 } 300 } 301 302 // We also may have nodes that have an extra terminator node representing the '/' backslash 303 // These nodes will have type "TYPE_BACKSLASH_TERMINATOR". 304 305 super.rewrite(tokens, rootNode); // Removes all extra "missing terminator" nodes 306 307 // Now we need to walk the tree again looking for unknown nodes under the root 308 // and attach them to the previous node, assuming the node can contain multiple nested statements. 309 // CREATE FUNCTION is one of those types 310 311 copyOfNodes = new ArrayList<AstNode>(rootNode.getChildren()); 312 boolean foundComplexNode = false; 313 complexNode = null; 314 for (AstNode child : copyOfNodes) { 315 if (matchesComplexNode(child)) { 316 foundComplexNode = true; 317 complexNode = child; 318 } else if (foundComplexNode) { 319 if (complexNode != null && nodeFactory().hasMixinType(child, TYPE_UNKNOWN_STATEMENT)) { 320 mergeNodes(tokens, complexNode, child); 321 rootNode.removeChild(child); 322 } else { 323 foundComplexNode = false; 324 complexNode = null; 325 } 326 } 327 } 328 } 329 330 /** 331 * {@inheritDoc} 332 * 333 * @see org.modeshape.sequencer.ddl.StandardDdlParser#consumeIdentifier(org.modeshape.sequencer.ddl.DdlTokenStream) 334 */ 335 @Override 336 protected String consumeIdentifier( DdlTokenStream tokens ) throws ParsingException { 337 Position startPosition = tokens.nextPosition(); 338 String id = super.consumeIdentifier(tokens); 339 if (!tokens.hasNext()) return id; 340 Position nextPosition = tokens.nextPosition(); 341 342 while (((nextPosition.getIndexInContent() - startPosition.getIndexInContent() - id.length()) == 0)) { 343 // allowed symbols in an identifier: underscore, dollar sign, pound sign, at sign 344 if (tokens.matches(DdlTokenizer.SYMBOL)) { 345 if (tokens.matches('$') || tokens.matches('#') || tokens.matches('@')) { 346 id += tokens.consume(); // consume symbol 347 } else { 348 break; // not a valid ID symbol 349 } 350 } else { 351 id += tokens.consume(); // consume text 352 } 353 354 nextPosition = tokens.nextPosition(); 355 } 356 357 return id; 358 } 359 360 private boolean matchesComplexNode( AstNode node ) { 361 assert node != null; 362 363 for (String mixin : COMPLEX_STMT_TYPES) { 364 if (nodeFactory().hasMixinType(node, mixin)) { 365 return true; 366 } 367 } 368 369 return false; 370 } 371 372 /** 373 * {@inheritDoc} 374 * 375 * @see org.modeshape.sequencer.ddl.StandardDdlParser#handleUnknownToken(org.modeshape.sequencer.ddl.DdlTokenStream, 376 * java.lang.String) 377 */ 378 @Override 379 public AstNode handleUnknownToken( DdlTokenStream tokens, 380 String tokenValue ) throws ParsingException { 381 if (tokenValue.equals("/")) { 382 return nodeFactory().node("backslashTerminator", getRootNode(), TYPE_BACKSLASH_TERMINATOR); 383 } 384 385 return null; 386 } 387 388 /** 389 * {@inheritDoc} The CREATE SCHEMA statement can include CREATE TABLE, CREATE VIEW, and GRANT statements. 390 * 391 * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCreateSchemaStatement(org.modeshape.sequencer.ddl.DdlTokenStream, 392 * org.modeshape.sequencer.ddl.node.AstNode) 393 */ 394 @Override 395 protected AstNode parseCreateSchemaStatement( DdlTokenStream tokens, 396 AstNode parentNode ) throws ParsingException { 397 assert tokens != null; 398 assert parentNode != null; 399 400 return super.parseCreateSchemaStatement(tokens, parentNode); 401 } 402 403 /** 404 * {@inheritDoc} 405 * 406 * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCustomStatement(org.modeshape.sequencer.ddl.DdlTokenStream, 407 * org.modeshape.sequencer.ddl.node.AstNode) 408 */ 409 @Override 410 protected AstNode parseCustomStatement( DdlTokenStream tokens, 411 AstNode parentNode ) throws ParsingException { 412 assert tokens != null; 413 assert parentNode != null; 414 415 AstNode result = null; 416 417 if (tokens.matches(STMT_COMMENT_ON)) { 418 result = parseCommentStatement(tokens, parentNode); 419 } else if (tokens.matches(STMT_ANALYZE)) { 420 return parseStatement(tokens, STMT_ANALYZE, parentNode, TYPE_ANALYZE_STATEMENT); 421 } else if (tokens.matches(STMT_ASSOCIATE_STATISTICS)) { 422 return parseStatement(tokens, STMT_ASSOCIATE_STATISTICS, parentNode, TYPE_ASSOCIATE_STATISTICS_STATEMENT); 423 } else if (tokens.matches(STMT_AUDIT)) { 424 return parseStatement(tokens, STMT_AUDIT, parentNode, TYPE_AUDIT_STATEMENT); 425 } else if (tokens.matches(STMT_COMMIT_FORCE) || tokens.matches(STMT_COMMIT_WORK) || tokens.matches(STMT_COMMIT_WRITE)) { 426 return parseStatement(tokens, STMT_COMMIT, parentNode, TYPE_COMMIT_STATEMENT); 427 } else if (tokens.matches(STMT_DISASSOCIATE_STATISTICS)) { 428 return parseStatement(tokens, STMT_DISASSOCIATE_STATISTICS, parentNode, TYPE_DISASSOCIATE_STATISTICS_STATEMENT); 429 } else if (tokens.matches(STMT_EXPLAIN_PLAN)) { 430 return parseStatement(tokens, STMT_EXPLAIN_PLAN, parentNode, TYPE_EXPLAIN_PLAN_STATEMENT); 431 } else if (tokens.matches(STMT_FLASHBACK)) { 432 return parseStatement(tokens, STMT_FLASHBACK, parentNode, TYPE_FLASHBACK_STATEMENT); 433 } else if (tokens.matches(STMT_LOCK_TABLE)) { 434 return parseStatement(tokens, STMT_LOCK_TABLE, parentNode, TYPE_LOCK_TABLE_STATEMENT); 435 } else if (tokens.matches(STMT_MERGE)) { 436 return parseStatement(tokens, STMT_MERGE, parentNode, TYPE_MERGE_STATEMENT); 437 } else if (tokens.matches(STMT_NOAUDIT)) { 438 return parseStatement(tokens, STMT_NOAUDIT, parentNode, TYPE_NOAUDIT_STATEMENT); 439 } else if (tokens.matches(STMT_PURGE)) { 440 return parseStatement(tokens, STMT_PURGE, parentNode, TYPE_PURGE_STATEMENT); 441 } else if (tokens.matches(STMT_RENAME)) { 442 return parseStatement(tokens, STMT_RENAME, parentNode, TYPE_RENAME_STATEMENT); 443 } else if (tokens.matches(STMT_ROLLBACK)) { 444 return parseStatement(tokens, STMT_ROLLBACK, parentNode, TYPE_ROLLBACK_STATEMENT); 445 } else if (tokens.matches(STMT_ROLLBACK_WORK)) { 446 return parseStatement(tokens, STMT_ROLLBACK_WORK, parentNode, TYPE_ROLLBACK_STATEMENT); 447 } else if (tokens.matches(STMT_ROLLBACK_TO_SAVEPOINT)) { 448 return parseStatement(tokens, STMT_ROLLBACK_TO_SAVEPOINT, parentNode, TYPE_ROLLBACK_STATEMENT); 449 } else if (tokens.matches(STMT_SAVEPOINT)) { 450 return parseStatement(tokens, STMT_SAVEPOINT, parentNode, TYPE_SAVEPOINT_STATEMENT); 451 } else if (tokens.matches(STMT_TRUNCATE)) { 452 return parseStatement(tokens, STMT_TRUNCATE, parentNode, TYPE_TRUNCATE_STATEMENT); 453 } 454 455 if (result == null) { 456 result = super.parseCustomStatement(tokens, parentNode); 457 } 458 return result; 459 } 460 461 @Override 462 protected AstNode parseCreateStatement( DdlTokenStream tokens, 463 AstNode parentNode ) throws ParsingException { 464 assert tokens != null; 465 assert parentNode != null; 466 467 if (tokens.matches(STMT_CREATE_INDEX) || tokens.matches(STMT_CREATE_UNIQUE_INDEX) 468 || tokens.matches(STMT_CREATE_BITMAP_INDEX)) { 469 return parseCreateIndex(tokens, parentNode); 470 } else if (tokens.matches(STMT_CREATE_CLUSTER)) { 471 return parseCreateClusterStatement(tokens, parentNode); 472 } else if (tokens.matches(STMT_CREATE_CONTEXT)) { 473 return parseStatement(tokens, STMT_CREATE_CONTEXT, parentNode, TYPE_CREATE_CONTEXT_STATEMENT); 474 } else if (tokens.matches(STMT_CREATE_CONTROLFILE)) { 475 return parseStatement(tokens, STMT_CREATE_CONTROLFILE, parentNode, TYPE_CREATE_CONTROLFILE_STATEMENT); 476 } else if (tokens.matches(STMT_CREATE_DATABASE)) { 477 return parseStatement(tokens, STMT_CREATE_DATABASE, parentNode, TYPE_CREATE_DATABASE_STATEMENT); 478 } else if (tokens.matches(STMT_CREATE_PUBLIC_DATABASE)) { 479 return parseStatement(tokens, STMT_CREATE_PUBLIC_DATABASE, parentNode, TYPE_CREATE_DATABASE_STATEMENT); 480 } else if (tokens.matches(STMT_CREATE_DIMENSION)) { 481 return parseCreateDimensionStatement(tokens, parentNode); 482 } else if (tokens.matches(STMT_CREATE_DIRECTORY)) { 483 return parseStatement(tokens, STMT_CREATE_DIRECTORY, parentNode, TYPE_CREATE_DIRECTORY_STATEMENT); 484 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_DIRECTORY)) { 485 return parseStatement(tokens, STMT_CREATE_OR_REPLACE_DIRECTORY, parentNode, TYPE_CREATE_DIRECTORY_STATEMENT); 486 } else if (tokens.matches(STMT_CREATE_DISKGROUP)) { 487 return parseStatement(tokens, STMT_CREATE_DISKGROUP, parentNode, TYPE_CREATE_DISKGROUP_STATEMENT); 488 } else if (tokens.matches(STMT_CREATE_FUNCTION)) { 489 // ============ > PARSE UNTIL '/' for STMT_CREATE_FUNCTION 490 return parseCreateFunctionStatement(tokens, parentNode); 491 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_FUNCTION)) { 492 // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_FUNCTION 493 return parseCreateFunctionStatement(tokens, parentNode); 494 } else if (tokens.matches(STMT_CREATE_INDEXTYPE)) { 495 return parseStatement(tokens, STMT_CREATE_INDEXTYPE, parentNode, TYPE_CREATE_INDEXTYPE_STATEMENT); 496 } else if (tokens.matches(STMT_CREATE_JAVA)) { 497 return parseCreateJavaStatement(tokens, parentNode); 498 } else if (tokens.matches(STMT_CREATE_LIBRARY)) { 499 // ============ > PARSE UNTIL '/' for STMT_CREATE_LIBRARY 500 return parseStatement(tokens, STMT_CREATE_LIBRARY, parentNode, TYPE_CREATE_LIBRARY_STATEMENT); 501 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_LIBRARY)) { 502 // ============ > PARSE UNTIL '/' STMT_CREATE_OR_REPLACE_LIBRARY 503 return parseStatement(tokens, STMT_CREATE_OR_REPLACE_LIBRARY, parentNode, TYPE_CREATE_LIBRARY_STATEMENT); 504 } else if (tokens.matches(STMT_CREATE_MATERIALIZED_VIEW)) { 505 return parseMaterializedViewStatement(tokens, parentNode); 506 } else if (tokens.matches(STMT_CREATE_OPERATOR)) { 507 return parseStatement(tokens, STMT_CREATE_OPERATOR, parentNode, TYPE_CREATE_OPERATOR_STATEMENT); 508 } else if (tokens.matches(STMT_CREATE_OUTLINE)) { 509 return parseStatement(tokens, STMT_CREATE_OUTLINE, parentNode, TYPE_CREATE_OUTLINE_STATEMENT); 510 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_OUTLINE)) { 511 return parseStatement(tokens, STMT_CREATE_OR_REPLACE_OUTLINE, parentNode, TYPE_CREATE_OUTLINE_STATEMENT); 512 } else if (tokens.matches(STMT_CREATE_PACKAGE)) { 513 // ============ > PARSE UNTIL '/' for STMT_CREATE_PACKAGE 514 return parseStatement(tokens, STMT_CREATE_PACKAGE, parentNode, TYPE_CREATE_PACKAGE_STATEMENT); 515 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_PACKAGE)) { 516 // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_PACKAGE 517 return parseStatement(tokens, STMT_CREATE_OR_REPLACE_PACKAGE, parentNode, TYPE_CREATE_PACKAGE_STATEMENT); 518 } else if (tokens.matches(STMT_CREATE_PFILE)) { 519 return parseStatement(tokens, STMT_CREATE_PFILE, parentNode, TYPE_CREATE_PFILE_STATEMENT); 520 } else if (tokens.matches(STMT_CREATE_PROCEDURE)) { 521 // ============ > PARSE UNTIL '/' for STMT_CREATE_PROCEDURE 522 return parseCreateProcedureStatement(tokens, parentNode); 523 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_PROCEDURE)) { 524 // ============ > PARSE UNTIL '/' for STMT_CREATE_PROCEDURE 525 return parseCreateProcedureStatement(tokens, parentNode); 526 } else if (tokens.matches(STMT_CREATE_PROFILE)) { 527 return parseStatement(tokens, STMT_CREATE_PROFILE, parentNode, TYPE_CREATE_PROFILE_STATEMENT); 528 } else if (tokens.matches(STMT_CREATE_ROLE)) { 529 return parseStatement(tokens, STMT_CREATE_ROLE, parentNode, TYPE_CREATE_ROLE_STATEMENT); 530 } else if (tokens.matches(STMT_CREATE_ROLLBACK)) { 531 return parseStatement(tokens, STMT_CREATE_ROLLBACK, parentNode, TYPE_CREATE_ROLLBACK_STATEMENT); 532 } else if (tokens.matches(STMT_CREATE_PUBLIC_ROLLBACK)) { 533 return parseStatement(tokens, STMT_CREATE_PUBLIC_ROLLBACK, parentNode, TYPE_CREATE_ROLLBACK_STATEMENT); 534 } else if (tokens.matches(STMT_CREATE_SEQUENCE)) { 535 return parseStatement(tokens, STMT_CREATE_SEQUENCE, parentNode, TYPE_CREATE_SEQUENCE_STATEMENT); 536 } else if (tokens.matches(STMT_CREATE_SPFILE)) { 537 return parseStatement(tokens, STMT_CREATE_SPFILE, parentNode, TYPE_CREATE_SPFILE_STATEMENT); 538 } else if (tokens.matches(STMT_CREATE_SYNONYM)) { 539 return parseStatement(tokens, STMT_CREATE_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT); 540 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_SYNONYM)) { 541 return parseStatement(tokens, STMT_CREATE_OR_REPLACE_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT); 542 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_PUBLIC_SYNONYM)) { 543 return parseStatement(tokens, STMT_CREATE_OR_REPLACE_PUBLIC_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT); 544 } else if (tokens.matches(STMT_CREATE_PUBLIC_SYNONYM)) { 545 return parseStatement(tokens, STMT_CREATE_PUBLIC_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT); 546 } else if (tokens.matches(STMT_CREATE_TABLESPACE)) { 547 return parseStatement(tokens, STMT_CREATE_TABLESPACE, parentNode, TYPE_CREATE_TABLESPACE_STATEMENT); 548 } else if (tokens.matches(STMT_CREATE_TRIGGER)) { 549 // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_TRIGGER 550 return parseSlashedStatement(tokens, STMT_CREATE_TRIGGER, parentNode, TYPE_CREATE_TRIGGER_STATEMENT); 551 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_TRIGGER)) { 552 // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_TRIGGER 553 return parseSlashedStatement(tokens, STMT_CREATE_OR_REPLACE_TRIGGER, parentNode, TYPE_CREATE_TRIGGER_STATEMENT); 554 } else if (tokens.matches(STMT_CREATE_TYPE)) { 555 return parseStatement(tokens, STMT_CREATE_TYPE, parentNode, TYPE_CREATE_TYPE_STATEMENT); 556 } else if (tokens.matches(STMT_CREATE_OR_REPLACE_TYPE)) { 557 return parseStatement(tokens, STMT_CREATE_OR_REPLACE_TYPE, parentNode, TYPE_CREATE_TYPE_STATEMENT); 558 } else if (tokens.matches(STMT_CREATE_USER)) { 559 return parseStatement(tokens, STMT_CREATE_USER, parentNode, TYPE_CREATE_USER_STATEMENT); 560 } 561 562 return super.parseCreateStatement(tokens, parentNode); 563 } 564 565 private AstNode parseCreateClusterStatement( DdlTokenStream tokens, 566 AstNode parentNode ) throws ParsingException { 567 markStartOfStatement(tokens); 568 tokens.consume(STMT_CREATE_CLUSTER); 569 String name = parseName(tokens); 570 571 AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_CLUSTER_STATEMENT); 572 573 parseUntilTerminator(tokens); 574 575 markEndOfStatement(tokens, node); 576 577 return node; 578 } 579 580 private AstNode parseCreateDimensionStatement( DdlTokenStream tokens, 581 AstNode parentNode ) throws ParsingException { 582 markStartOfStatement(tokens); 583 tokens.consume(STMT_CREATE_DIMENSION); 584 String name = parseName(tokens); 585 586 AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_DIMENSION_STATEMENT); 587 588 parseUntilTerminator(tokens); 589 590 markEndOfStatement(tokens, node); 591 592 return node; 593 } 594 595 /** 596 * Parses DDL CREATE FUNCTION statement 597 * 598 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 599 * @param parentNode the parent {@link AstNode} node; may not be null 600 * @return the parsed CREATE FUNCTION statement node 601 * @throws ParsingException 602 */ 603 protected AstNode parseCreateFunctionStatement( DdlTokenStream tokens, 604 AstNode parentNode ) throws ParsingException { 605 assert tokens != null; 606 assert parentNode != null; 607 608 markStartOfStatement(tokens); 609 610 /* ---------------------------------------------------------------------- 611 CREATE [ OR REPLACE ] FUNCTION [ schema. ] function_name 612 [ ( parameter_declaration [, parameter_declaration] ) 613 ] 614 RETURN datatype 615 [ { invoker_rights_clause 616 | DETERMINISTIC 617 | parallel_enable_clause 618 | result_cache_clause 619 }... 620 ] 621 { { AGGREGATE | PIPELINED } 622 USING [ schema. ] implementation_type 623 | [ PIPELINED ] { IS | AS } { [ declare_section ] body | call_spec } 624 } ; 625 626 parameter_declaration = parameter_name [ IN | { { OUT | { IN OUT }} [ NOCOPY ] } ] datatype [ { := | DEFAULT } expression ] 627 ---------------------------------------------------------------------- */ 628 629 boolean isReplace = tokens.canConsume(STMT_CREATE_OR_REPLACE_FUNCTION); 630 631 tokens.canConsume(STMT_CREATE_FUNCTION); 632 633 String name = parseName(tokens); 634 635 AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_FUNCTION_STATEMENT); 636 637 if (isReplace) { 638 // TODO: SET isReplace = TRUE to node (possibly a cnd mixin of "replaceable" 639 } 640 641 boolean ok = parseParameters(tokens, node); 642 643 if (ok) { 644 if (tokens.canConsume("RETURN")) { 645 DataType dType = getDatatypeParser().parse(tokens); 646 if (dType != null) { 647 getDatatypeParser().setPropertiesOnNode(node, dType); 648 } 649 } 650 } 651 652 parseUntilFwdSlash(tokens, false); 653 654 tokens.canConsume("/"); 655 656 markEndOfStatement(tokens, node); 657 658 return node; 659 } 660 661 /** 662 * Parses DDL CREATE PROCEDURE statement 663 * 664 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 665 * @param parentNode the parent {@link AstNode} node; may not be null 666 * @return the parsed CREATE PROCEDURE statement node 667 * @throws ParsingException 668 */ 669 protected AstNode parseCreateProcedureStatement( DdlTokenStream tokens, 670 AstNode parentNode ) throws ParsingException { 671 assert tokens != null; 672 assert parentNode != null; 673 674 markStartOfStatement(tokens); 675 /* ---------------------------------------------------------------------- 676 CREATE [ OR REPLACE ] PROCEDURE [ schema. ] procedure_name 677 [ ( parameter_declaration [, parameter_declaration ] ) ] 678 [ AUTHID { CURRENT_USER | DEFINER ] 679 { IS | AS } 680 { [ declare_section ] body | call_spec | EXTERNAL} ; 681 682 call_spec = LANGUAGE { Java_declaration | C_declaration } 683 684 Java_declaration = JAVA NAME string 685 686 C_declaration = 687 C [ NAME name ] 688 LIBRARY lib_name 689 [ AGENT IN (argument[, argument ]...) ] 690 [ WITH CONTEXT ] 691 [ PARAMETERS (parameter[, parameter ]...) ] 692 693 parameter_declaration = parameter_name [ IN | { { OUT | { IN OUT }} [ NOCOPY ] } ] datatype [ { := | DEFAULT } expression ] 694 ---------------------------------------------------------------------- */ 695 696 boolean isReplace = tokens.canConsume(STMT_CREATE_OR_REPLACE_PROCEDURE); 697 698 tokens.canConsume(STMT_CREATE_PROCEDURE); 699 700 String name = parseName(tokens); 701 702 AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_PROCEDURE_STATEMENT); 703 704 if (isReplace) { 705 // TODO: SET isReplace = TRUE to node (possibly a cnd mixin of "replaceable" 706 } 707 708 boolean ok = parseParameters(tokens, node); 709 710 if (ok) { 711 if (tokens.canConsume("AUTHID")) { 712 if (tokens.canConsume("CURRENT_USER")) { 713 node.setProperty(AUTHID_VALUE, "AUTHID CURRENT_USER"); 714 } else { 715 tokens.consume("DEFINER"); 716 node.setProperty(AUTHID_VALUE, "DEFINER"); 717 } 718 } 719 } 720 721 parseUntilFwdSlash(tokens, false); 722 723 tokens.canConsume("/"); 724 725 markEndOfStatement(tokens, node); 726 727 return node; 728 } 729 730 private boolean parseParameters( DdlTokenStream tokens, 731 AstNode procedureNode ) throws ParsingException { 732 assert tokens != null; 733 assert procedureNode != null; 734 735 // parameter_declaration = parameter_name [ IN | { { OUT | { IN OUT }} [ NOCOPY ] } ] datatype [ { := | DEFAULT } 736 // expression ] 737 // Assume we start with open parenthesis '(', then we parse comma separated list of function parameters 738 // which have the form: [ parameter-Name ] DataType 739 // So, try getting datatype, if datatype == NULL, then parseName() & parse datatype, then repeat as long as next token is 740 // "," 741 742 tokens.consume(L_PAREN); // EXPECTED 743 744 while (!tokens.canConsume(R_PAREN)) { 745 746 String paramName = parseName(tokens); 747 String inOutStr = null; 748 if (tokens.matches("IN")) { 749 if (tokens.canConsume("IN", "OUT")) { 750 if (tokens.canConsume("NOCOPY")) { 751 inOutStr = "IN OUT NOCOPY"; 752 } else { 753 inOutStr = "IN OUT"; 754 } 755 } else { 756 tokens.consume("IN"); 757 inOutStr = "IN"; 758 } 759 760 } else if (tokens.matches("OUT")) { 761 if (tokens.canConsume("OUT", "NOCOPY")) { 762 inOutStr = "OUT NOCOPY"; 763 } else { 764 tokens.consume("OUT"); 765 inOutStr = "OUT"; 766 } 767 } 768 769 DataType datatype = getDatatypeParser().parse(tokens); 770 AstNode paramNode = nodeFactory().node(paramName, procedureNode, TYPE_FUNCTION_PARAMETER); 771 if (datatype != null) { 772 getDatatypeParser().setPropertiesOnNode(paramNode, datatype); 773 } 774 775 if (tokens.matchesAnyOf(":=", "DEFAULT") || !tokens.matchesAnyOf(COMMA, R_PAREN)) { 776 String msg = DdlSequencerI18n.unsupportedProcedureParameterDeclaration.text(procedureNode.getName()); 777 DdlParserProblem problem = new DdlParserProblem(Problems.WARNING, getCurrentMarkedPosition(), msg); 778 addProblem(problem, procedureNode); 779 return false; 780 } 781 782 if (inOutStr != null) { 783 paramNode.setProperty(IN_OUT_NO_COPY, inOutStr); 784 } 785 786 tokens.canConsume(COMMA); 787 } 788 789 return true; 790 } 791 792 /** 793 * Parses DDL CREATE MATERIALIZED VIEW statement This could either be a standard view or a VIEW LOG ON statement. 794 * 795 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 796 * @param parentNode the parent {@link AstNode} node; may not be null 797 * @return the parsed CREATE MATERIALIZED VIEW statement node 798 * @throws ParsingException 799 */ 800 protected AstNode parseMaterializedViewStatement( DdlTokenStream tokens, 801 AstNode parentNode ) throws ParsingException { 802 assert tokens != null; 803 assert parentNode != null; 804 805 markStartOfStatement(tokens); 806 807 /* ---------------------------------------------------------------------- 808 CREATE MATERIALIZED VIEW 809 [ schema. ]materialized_view 810 [ column_alias [, column_alias]... ] 811 [ OF [ schema. ]object_type ] .................... (MORE...) 812 813 EXAMPLES: 814 815 CREATE MATERIALIZED VIEW LOG ON products 816 WITH ROWID, SEQUENCE (prod_id) 817 INCLUDING NEW VALUES; 818 819 CREATE MATERIALIZED VIEW sales_mv 820 BUILD IMMEDIATE 821 REFRESH FAST ON COMMIT 822 AS SELECT t.calendar_year, p.prod_id, 823 SUM(s.amount_sold) AS sum_sales 824 FROM times t, products p, sales s 825 WHERE t.time_id = s.time_id AND p.prod_id = s.prod_id 826 GROUP BY t.calendar_year, p.prod_id; 827 ---------------------------------------------------------------------- */ 828 829 boolean isLog = tokens.canConsume(STMT_CREATE_MATERIALIZED_VEIW_LOG); 830 831 tokens.canConsume(STMT_CREATE_MATERIALIZED_VIEW); 832 833 String name = parseName(tokens); 834 835 AstNode node = null; 836 837 if (isLog) { 838 node = nodeFactory().node(name, parentNode, TYPE_CREATE_MATERIALIZED_VIEW_LOG_STATEMENT); 839 } else { 840 node = nodeFactory().node(name, parentNode, TYPE_CREATE_MATERIALIZED_VIEW_STATEMENT); 841 } 842 843 parseUntilTerminator(tokens); 844 845 markEndOfStatement(tokens, node); 846 847 return node; 848 } 849 850 @Override 851 protected AstNode parseGrantStatement( DdlTokenStream tokens, 852 AstNode parentNode ) throws ParsingException { 853 CheckArg.isNotNull(tokens, "tokens"); 854 CheckArg.isNotNull(parentNode, "parentNode"); 855 856 // GRANT { grant_system_privileges | grant_object_privileges } ; 857 // 858 // ** grant_system_privileges ** 859 // 860 // { system_privilege | role | ALL PRIVILEGES } [, { system_privilege | role | ALL PRIVILEGES } ]... 861 // TO grantee_clause [ WITH ADMIN OPTION ] 862 // 863 // ** grant_object_privileges ** 864 // 865 // { object_privilege | ALL [ PRIVILEGES ] } [ (column [, column ]...) ] [, { object_privilege | ALL [ PRIVILEGES ] } [ 866 // (column [, column ]...) ] ]... 867 // on_object_clause 868 // TO grantee_clause [ WITH HIERARCHY OPTION ] [ WITH GRANT OPTION ] 869 870 // ** on_object_clause ** 871 // 872 // { [ schema. ] object | { DIRECTORY directory_name | JAVA { SOURCE | RESOURCE } [ schema. ] object } } 873 // 874 // ** grantee_clause ** 875 // 876 // { user [ IDENTIFIED BY password ] | role | PUBLIC } [, { user [ IDENTIFIED BY password ] | role | PUBLIC } ]... 877 878 AstNode node = null; 879 880 // Original implementation does NOT parse Insert statement, but just returns a generic TypedStatement 881 markStartOfStatement(tokens); 882 883 tokens.consume(GRANT); 884 String name = GRANT; 885 886 tokens.consume(); // First Privilege token 887 888 node = nodeFactory().node(name, parentNode, TYPE_GRANT_STATEMENT); 889 890 while (tokens.hasNext() 891 && !isTerminator(tokens) 892 && (!tokens.matches(DdlTokenizer.STATEMENT_KEY) || (tokens.matches(DdlTokenizer.STATEMENT_KEY) && tokens.matches("GRANT", 893 "OPTION")))) { 894 tokens.consume(); 895 } 896 897 markEndOfStatement(tokens, node); 898 899 return node; 900 901 // if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "TO")) { 902 // markStartOfStatement(tokens); 903 // tokens.consume(GRANT); 904 // String privilege = tokens.consume(); 905 // tokens.consume("TO"); 906 // tokens.consume(); // TO Value 907 // && problem != null 908 // String value = parseUntilTerminator(tokens); 909 // 910 // AstNode grantNode = nodeFactory().node("GRANT", parentNode, TYPE_GRANT_STATEMENT); 911 // markEndOfStatement(tokens, grantNode); 912 // 913 // return grantNode; 914 // } else if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, COMMA)) { 915 // markStartOfStatement(tokens); 916 // tokens.consume(GRANT); 917 // String privilege = tokens.consume(); 918 // // Assume Multiple Privileges 919 // while( tokens.canConsume(COMMA)) { 920 // tokens.consume(); // Next privilege 921 // } 922 // 923 // tokens.consume("ON"); 924 // tokens.consume(); // TO Value 925 // 926 // tokens.canConsume("WITH", GRANT); 927 // 928 // String value = parseUntilTerminator(tokens); 929 // 930 // AstNode grantNode = nodeFactory().node("GRANT", parentNode, TYPE_GRANT_STATEMENT); 931 // markEndOfStatement(tokens, grantNode); 932 // 933 // return grantNode; 934 // } else if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "ON", DdlTokenStream.ANY_VALUE, "TO", 935 // DdlTokenStream.ANY_VALUE, "WITH", GRANT)) { 936 // markStartOfStatement(tokens); 937 // //GRANT ALL ON bonuses TO hr WITH GRANT OPTION; 938 // 939 // tokens.consume(GRANT); 940 // String privilege = tokens.consume(); 941 // tokens.consume("ON"); 942 // tokens.consume(); // ON Value 943 // tokens.consume("TO"); 944 // tokens.consume(); 945 // tokens.consume("WITH", GRANT); 946 // String value = parseUntilTerminator(tokens); 947 // 948 // AstNode grantNode = nodeFactory().node("GRANT", parentNode, TYPE_GRANT_STATEMENT); 949 // markEndOfStatement(tokens, grantNode); 950 // 951 // return grantNode; 952 // } 953 // else if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "ON")) { 954 // tokens.consume(GRANT); 955 // String privilege = tokens.consume(); 956 // tokens.consume("ON"); 957 // tokens.consume(); // ON Value 958 // 959 // String value = parseUntilTerminator(tokens); 960 // stmt.appendSource(true, value);&& problem != null 961 // stmt.setType("GRANT" + SPACE + privilege + SPACE + "ON"); 962 // consumeTerminator(tokens); 963 // } else if( tokens.matches(GRANT, CREATE) || 964 // tokens.matches(GRANT, ALTER) || 965 // tokens.matches(GRANT, DROP) || 966 // tokens.matches(GRANT, "EXECUTE") || 967 // tokens.matches(GRANT, "MANAGE") || 968 // tokens.matches(GRANT, "QUERY") || 969 // tokens.matches(GRANT, "ON", "COMMIT") || 970 // tokens.matches(GRANT, "ANY") || 971 // tokens.matches(GRANT, "SELECT") || 972 // tokens.matches(GRANT, "RESTRICTED") || 973 // // 974 // ======================================================================================================================== 975 // === 976 // // 977 // ======================================================================================================================== 978 // === 979 // tokens.matches(GRANT, "FLASHBACK") || 980 // tokens.matches(GRANT, "GLOBAL") || 981 // tokens.matches(GRANT, "DEBUG") || 982 // tokens.matches(GRANT, "GLOBAL") || 983 // tokens.matches(GRANT, "ADVISOR") || 984 // tokens.matches(GRANT, "ADMINISTER") || 985 // tokens.matches(GRANT, "BACKUP") || 986 // tokens.matches(GRANT, "LOCK") || 987 // tokens.matches(GRANT, "UPDATE") || 988 // tokens.matches(GRANT, "DELETE") || 989 // tokens.matches(GRANT, "INSERT") || 990 // tokens.matches(GRANT, "UNLIMITED") || 991 // tokens.matches(GRANT, "UNDER") || 992 // tokens.matches(GRANT, "ANALYZE") || 993 // tokens.matches(GRANT, "AUDIT") || 994 // tokens.matches(GRANT, "COMMENT") || 995 // tokens.matches(GRANT, "EXEMPT") || 996 // tokens.matches(GRANT, "FORCE") || 997 // tokens.matches(GRANT, "RESUMABLE") || 998 // tokens.matches(GRANT, "SYSDBA") || 999 // tokens.matches(GRANT, "REFERENCES") || 1000 // tokens.matches(GRANT, "SYSOPER") || 1001 // tokens.matches(GRANT, "WRITE") ) { 1002 // tokens.consume(GRANT); 1003 // 1004 // String nextTok = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume(); 1005 // 1006 // String value = parseUntilTerminator(tokens); 1007 // stmt.setType("GRANT" + SPACE + nextTok); 1008 // consumeTerminator(tokens); 1009 // } 1010 // 1011 // 1012 // return grantNode; 1013 1014 // return super.parseGrantStatement(tokens, parentNode); 1015 } 1016 1017 /** 1018 * {@inheritDoc} 1019 * 1020 * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseRevokeStatement(org.modeshape.sequencer.ddl.DdlTokenStream, 1021 * org.modeshape.sequencer.ddl.node.AstNode) 1022 */ 1023 @Override 1024 protected AstNode parseRevokeStatement( DdlTokenStream tokens, 1025 AstNode parentNode ) throws ParsingException { 1026 return parseStatement(tokens, STMT_REVOKE, parentNode, TYPE_REVOKE_STATEMENT); 1027 } 1028 1029 @Override 1030 protected AstNode parseAlterTableStatement( DdlTokenStream tokens, 1031 AstNode parentNode ) throws ParsingException { 1032 assert tokens != null; 1033 assert parentNode != null; 1034 1035 markStartOfStatement(tokens); 1036 1037 tokens.consume("ALTER", "TABLE"); 1038 String tableName = parseName(tokens); 1039 1040 // System.out.println(" >> PARSING ALTER STATEMENT >> TABLE Name = " + tableName); 1041 1042 AstNode alterTableNode = nodeFactory().node(tableName, parentNode, TYPE_ALTER_TABLE_STATEMENT); 1043 1044 // Standalone alter commands 1045 if (tokens.canConsume("RENAME")) { 1046 if (tokens.matches("COLUMN", TokenStream.ANY_VALUE, "TO", TokenStream.ANY_VALUE)) { 1047 // Rename of column 1048 tokens.consume("COLUMN"); 1049 String oldColumnName = parseName(tokens); 1050 tokens.consume("TO"); 1051 String newColumnName = parseName(tokens); 1052 1053 AstNode renameColumn = nodeFactory().node(oldColumnName, alterTableNode, TYPE_RENAME_COLUMN); 1054 renameColumn.setProperty(NEW_NAME, newColumnName); 1055 } else if (tokens.matches("CONSTRAINT", TokenStream.ANY_VALUE, "TO", TokenStream.ANY_VALUE)) { 1056 // Rename of constraint 1057 tokens.consume("CONSTRAINT"); 1058 String oldConstraintName = parseName(tokens); 1059 tokens.consume("TO"); 1060 String newConstraintName = parseName(tokens); 1061 1062 AstNode renameColumn = nodeFactory().node(oldConstraintName, alterTableNode, TYPE_RENAME_CONSTRAINT); 1063 renameColumn.setProperty(NEW_NAME, newConstraintName); 1064 } else if (tokens.canConsume("TO")) { 1065 // Rename of table 1066 alterTableNode.setProperty(NEW_NAME, parseName(tokens)); 1067 } else { 1068 // TODO add problem 1069 } 1070 1071 parseUntilTerminator(tokens); 1072 } else if (tokens.canConsume("DROP")) { 1073 1074 if (tokens.matches(L_PAREN)) { 1075 // Columns enclosed in "()" 1076 parseColumnNameList(tokens, alterTableNode, TYPE_DROP_COLUMN_DEFINITION); 1077 } else if (tokens.canConsume("COLUMN")) { 1078 // Single DROP COLUMN 1079 String columnName = parseName(tokens); 1080 1081 AstNode columnNode = nodeFactory().node(columnName, alterTableNode, TYPE_DROP_COLUMN_DEFINITION); 1082 1083 if (tokens.canConsume(DropBehavior.CASCADE)) { 1084 columnNode.setProperty(DROP_BEHAVIOR, DropBehavior.CASCADE); 1085 } else if (tokens.canConsume(DropBehavior.RESTRICT)) { 1086 columnNode.setProperty(DROP_BEHAVIOR, DropBehavior.RESTRICT); 1087 } 1088 } else if (tokens.canConsume("CONSTRAINT")) { 1089 String constraintName = parseName(tokens); 1090 1091 AstNode constraintNode = nodeFactory().node(constraintName, alterTableNode, TYPE_DROP_TABLE_CONSTRAINT_DEFINITION); 1092 1093 if (tokens.canConsume(DropBehavior.CASCADE)) { 1094 constraintNode.setProperty(DROP_BEHAVIOR, DropBehavior.CASCADE); 1095 } else if (tokens.canConsume(DropBehavior.RESTRICT)) { 1096 constraintNode.setProperty(DROP_BEHAVIOR, DropBehavior.RESTRICT); 1097 } 1098 } 1099 1100 parseUntilTerminator(tokens); 1101 } else { 1102 // Loop over tokens and find known parts 1103 while (tokens.hasNext() && !tokens.matches(DdlTokenizer.STATEMENT_KEY) 1104 && ((doUseTerminator() && !isTerminator(tokens)) || !doUseTerminator())) { 1105 // skip to next known part in statement 1106 while (tokens.hasNext() && !tokens.matchesAnyOf(new String[] {"ADD", "MODIFY"}) 1107 && ((doUseTerminator() && !isTerminator(tokens)) || !doUseTerminator())) { 1108 tokens.consume(); 1109 } 1110 1111 if (!tokens.hasNext()) break; 1112 1113 if (tokens.canConsume("ADD")) { 1114 1115 if (isTableConstraint(tokens)) { 1116 parseTableConstraint(tokens, alterTableNode, true); 1117 } else { 1118 // This segment can also be enclosed in "()" brackets to handle multiple ColumnDefinition ADDs 1119 if (tokens.matches(L_PAREN, "REF")) { 1120 // ALTER TABLE staff ADD (REF(dept) WITH ROWID); 1121 tokens.consume(L_PAREN, "REF", L_PAREN); 1122 parseName(tokens); 1123 tokens.consume(R_PAREN, "WITH", "ROWID", R_PAREN); 1124 } else if (tokens.matches(L_PAREN, "SCOPE")) { 1125 // ALTER TABLE staff ADD (SCOPE FOR (dept) IS offices); 1126 tokens.consume(L_PAREN, "SCOPE", "FOR", L_PAREN); 1127 parseName(tokens); 1128 tokens.consume(R_PAREN, "IS"); 1129 parseName(tokens); 1130 tokens.consume(R_PAREN); 1131 } else if (tokens.matches(L_PAREN)) { 1132 // Columns enclosed in "()" 1133 parseColumns(tokens, alterTableNode, TYPE_ADD_COLUMN_DEFINITION); 1134 } else { 1135 // Assume single ADD COLUMN 1136 parseColumnDefinition(tokens, alterTableNode, TYPE_ADD_COLUMN_DEFINITION); 1137 } 1138 } 1139 1140 } else if (tokens.canConsume("MODIFY")) { 1141 1142 if (tokens.matches(L_PAREN)) { 1143 // Columns enclosed in "()" 1144 parseColumns(tokens, alterTableNode, TYPE_ALTER_COLUMN_DEFINITION); 1145 } else { 1146 // Only support column definition changes if not followed by a reserved word, i.e.: 1147 // - COLUMN - column substitution 1148 // - LOB - lob properties modification 1149 // - NESTED [TABLE] - nested table 1150 // - DEFAULT - table default attributes 1151 // - [SUB]PARTITION - [sub]partition attributes 1152 if (tokens.matchesAnyOf("COLUMN", "LOB", "NESTED", "DEFAULT", "PARTITION", "SUBPARTITION")) continue; 1153 1154 // Assume single MODIFY 1155 parseColumnDefinition(tokens, alterTableNode, TYPE_ALTER_COLUMN_DEFINITION); 1156 } 1157 1158 } 1159 } 1160 } 1161 1162 markEndOfStatement(tokens, alterTableNode); 1163 1164 return alterTableNode; 1165 1166 } 1167 1168 @Override 1169 protected AstNode parseAlterStatement( DdlTokenStream tokens, 1170 AstNode parentNode ) throws ParsingException { 1171 assert tokens != null; 1172 assert parentNode != null; 1173 1174 if (tokens.matches(ALTER, TABLE)) { 1175 return parseAlterTableStatement(tokens, parentNode); 1176 } else if (tokens.matches(STMT_ALTER_CLUSTER)) { 1177 return parseStatement(tokens, STMT_ALTER_CLUSTER, parentNode, TYPE_ALTER_CLUSTER_STATEMENT); 1178 } else if (tokens.matches(STMT_ALTER_DATABASE)) { 1179 // could encounter: ALTER DATABASE RENAME FILE 'diskc:log3.log' TO 'diskb:log3.log'; 1180 // So need to parse up past the RENAME check 1181 markStartOfStatement(tokens); 1182 tokens.consume(STMT_ALTER_DATABASE); 1183 AstNode result = nodeFactory().node("database", parentNode, TYPE_ALTER_DATABASE_STATEMENT); 1184 tokens.canConsume("RENAME"); 1185 parseUntilTerminator(tokens); 1186 markEndOfStatement(tokens, result); 1187 return result; 1188 } else if (tokens.matches(STMT_ALTER_DIMENSION)) { 1189 return parseStatement(tokens, STMT_ALTER_DIMENSION, parentNode, TYPE_ALTER_DIMENSION_STATEMENT); 1190 } else if (tokens.matches(STMT_ALTER_DISKGROUP)) { 1191 return parseStatement(tokens, STMT_ALTER_DISKGROUP, parentNode, TYPE_ALTER_DISKGROUP_STATEMENT); 1192 } else if (tokens.matches(STMT_ALTER_FUNCTION)) { 1193 return parseStatement(tokens, STMT_ALTER_FUNCTION, parentNode, TYPE_ALTER_FUNCTION_STATEMENT); 1194 } else if (tokens.matches(STMT_ALTER_INDEX)) { 1195 // could encounter: ALTER INDEX upper_ix RENAME TO xxxxxx 1196 // So need to parse up past the RENAME check 1197 markStartOfStatement(tokens); 1198 tokens.consume(ALTER, INDEX); 1199 String indexName = parseName(tokens); 1200 AstNode result = nodeFactory().node(indexName, parentNode, TYPE_ALTER_INDEX_STATEMENT); 1201 tokens.canConsume("RENAME"); 1202 parseUntilTerminator(tokens); 1203 markEndOfStatement(tokens, result); 1204 return result; 1205 } else if (tokens.matches(STMT_ALTER_INDEXTYPE)) { 1206 return parseStatement(tokens, STMT_ALTER_INDEXTYPE, parentNode, TYPE_ALTER_INDEXTYPE_STATEMENT); 1207 } else if (tokens.matches(STMT_ALTER_JAVA)) { 1208 return parseStatement(tokens, STMT_ALTER_JAVA, parentNode, TYPE_ALTER_JAVA_STATEMENT); 1209 } else if (tokens.matches(STMT_ALTER_MATERIALIZED)) { 1210 return parseStatement(tokens, STMT_ALTER_MATERIALIZED, parentNode, TYPE_ALTER_MATERIALIZED_STATEMENT); 1211 } else if (tokens.matches(STMT_ALTER_OPERATOR)) { 1212 return parseStatement(tokens, STMT_ALTER_OPERATOR, parentNode, TYPE_ALTER_OPERATOR_STATEMENT); 1213 } else if (tokens.matches(STMT_ALTER_OUTLINE)) { 1214 return parseStatement(tokens, STMT_ALTER_OUTLINE, parentNode, TYPE_ALTER_OUTLINE_STATEMENT); 1215 } else if (tokens.matches(STMT_ALTER_PACKAGE)) { 1216 return parseStatement(tokens, STMT_ALTER_PACKAGE, parentNode, TYPE_ALTER_PACKAGE_STATEMENT); 1217 } else if (tokens.matches(STMT_ALTER_PROCEDURE)) { 1218 return parseStatement(tokens, STMT_ALTER_PROCEDURE, parentNode, TYPE_ALTER_PROCEDURE_STATEMENT); 1219 } else if (tokens.matches(STMT_ALTER_PROFILE)) { 1220 return parseStatement(tokens, STMT_ALTER_PROFILE, parentNode, TYPE_ALTER_PROFILE_STATEMENT); 1221 } else if (tokens.matches(STMT_ALTER_RESOURCE)) { 1222 return parseStatement(tokens, STMT_ALTER_RESOURCE, parentNode, TYPE_ALTER_RESOURCE_STATEMENT); 1223 } else if (tokens.matches(STMT_ALTER_ROLE)) { 1224 return parseStatement(tokens, STMT_ALTER_ROLE, parentNode, TYPE_ALTER_ROLE_STATEMENT); 1225 } else if (tokens.matches(STMT_ALTER_ROLLBACK)) { 1226 return parseStatement(tokens, STMT_ALTER_ROLLBACK, parentNode, TYPE_ALTER_ROLLBACK_STATEMENT); 1227 } else if (tokens.matches(STMT_ALTER_SEQUENCE)) { 1228 return parseStatement(tokens, STMT_ALTER_SEQUENCE, parentNode, TYPE_ALTER_SEQUENCE_STATEMENT); 1229 } else if (tokens.matches(STMT_ALTER_SESSION)) { 1230 return parseStatement(tokens, STMT_ALTER_SESSION, parentNode, TYPE_ALTER_SESSION_STATEMENT); 1231 } else if (tokens.matches(STMT_ALTER_SYSTEM)) { 1232 return parseStatement(tokens, STMT_ALTER_SYSTEM, parentNode, TYPE_ALTER_SYSTEM_STATEMENT); 1233 } else if (tokens.matches(STMT_ALTER_TABLESPACE)) { 1234 return parseStatement(tokens, STMT_ALTER_TABLESPACE, parentNode, TYPE_ALTER_TABLESPACE_STATEMENT); 1235 } else if (tokens.matches(STMT_ALTER_TRIGGER)) { 1236 return parseStatement(tokens, STMT_ALTER_TRIGGER, parentNode, TYPE_ALTER_TRIGGER_STATEMENT); 1237 } else if (tokens.matches(STMT_ALTER_TYPE)) { 1238 return parseStatement(tokens, STMT_ALTER_TYPE, parentNode, TYPE_ALTER_TYPE_STATEMENT); 1239 } else if (tokens.matches(STMT_ALTER_USER)) { 1240 // could encounter: ALTER USER app_user1 GRANT ..... 1241 // So need to parse up past the GRANT check 1242 markStartOfStatement(tokens); 1243 tokens.consume(STMT_ALTER_USER); 1244 String name = parseName(tokens); 1245 AstNode result = nodeFactory().node(name, parentNode, TYPE_ALTER_USER_STATEMENT); 1246 tokens.canConsume("GRANT"); 1247 parseUntilTerminator(tokens); 1248 markEndOfStatement(tokens, result); 1249 return result; 1250 } else if (tokens.matches(STMT_ALTER_VIEW)) { 1251 return parseStatement(tokens, STMT_ALTER_VIEW, parentNode, TYPE_ALTER_VIEW_STATEMENT); 1252 } 1253 1254 return super.parseAlterStatement(tokens, parentNode); 1255 } 1256 1257 /** 1258 * {@inheritDoc} 1259 * 1260 * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCreateViewStatement(org.modeshape.sequencer.ddl.DdlTokenStream, 1261 * org.modeshape.sequencer.ddl.node.AstNode) 1262 */ 1263 @Override 1264 protected AstNode parseCreateViewStatement( DdlTokenStream tokens, 1265 AstNode parentNode ) throws ParsingException { 1266 assert tokens != null; 1267 assert parentNode != null; 1268 1269 markStartOfStatement(tokens); 1270 // CREATE [OR REPLACE] 1271 // [[NO] FORCE] VIEW [schema.] view 1272 // [ ( { alias [ inline_constraint... ] 1273 // | out_of_line_constraint 1274 // } 1275 // [, { alias [ inline_constraint...] 1276 // | out_of_line_constraint 1277 // } 1278 // ] 1279 // ) 1280 // | object_view_clause 1281 // | XMLType_view_clause 1282 // ] 1283 // AS subquery [ subquery_restriction_clause ] ; 1284 1285 // NOTE: the query expression along with the CHECK OPTION clause require no SQL statement terminator. 1286 // So the CHECK OPTION clause will NOT 1287 1288 String stmtType = "CREATE"; 1289 tokens.consume("CREATE"); 1290 if (tokens.canConsume("OR", "REPLACE")) { 1291 stmtType = stmtType + SPACE + "OR REPLACE"; 1292 } else if (tokens.canConsume("NO", "FORCE")) { 1293 stmtType = stmtType + SPACE + "NO FORCE"; 1294 } else if (tokens.canConsume("FORCE")) { 1295 stmtType = stmtType + SPACE + "FORCE"; 1296 } 1297 1298 tokens.consume("VIEW"); 1299 stmtType = stmtType + SPACE + "VIEW"; 1300 1301 String name = parseName(tokens); 1302 1303 AstNode createViewNode = nodeFactory().node(name, parentNode, TYPE_CREATE_VIEW_STATEMENT); 1304 1305 // CONSUME COLUMNS 1306 parseColumnNameList(tokens, createViewNode, TYPE_COLUMN_REFERENCE); 1307 1308 // (object_view_clause) 1309 // 1310 // OF [ schema. ] type_name 1311 // { WITH OBJECT IDENTIFIER 1312 // { DEFAULT | ( attribute [, attribute ]... ) } 1313 // | UNDER [ schema. ] superview 1314 // } 1315 // ( { out_of_line_constraint 1316 // | attribute { inline_constraint }... 1317 // } [, { out_of_line_constraint 1318 // | attribute { inline_constraint }... 1319 // } 1320 // ]... 1321 // ) 1322 1323 // (XMLType_view_clause) 1324 // 1325 // OF XMLTYPE [ XMLSchema_spec ] 1326 // WITH OBJECT IDENTIFIER 1327 // { DEFAULT | ( expr [, expr ]...) } 1328 1329 // Basically, if next token matches "OF", then parse until token matches "AS" 1330 1331 if (tokens.matches("OF")) { 1332 do { 1333 tokens.consume(); 1334 } while (!tokens.matches("AS")); 1335 } 1336 1337 tokens.consume("AS"); 1338 1339 String queryExpression = parseUntilTerminator(tokens); 1340 1341 createViewNode.setProperty(CREATE_VIEW_QUERY_EXPRESSION, queryExpression); 1342 1343 markEndOfStatement(tokens, createViewNode); 1344 1345 return createViewNode; 1346 } 1347 1348 /** 1349 * The tokens must start with a left paren, end with a right paren, and have content between. Any parens in the content must 1350 * have matching parens. 1351 * 1352 * @param tokens the tokens being processed (cannot be <code>null</code> but or empty) 1353 * @return the content (never <code>null</code> or empty. 1354 * @throws ParsingException if there is a problem parsing the query expression 1355 */ 1356 private String parseContentBetweenParens( final DdlTokenStream tokens ) throws ParsingException { 1357 tokens.consume(L_PAREN); // don't include first paren in expression 1358 1359 int numLeft = 1; 1360 int numRight = 0; 1361 1362 final StringBuilder text = new StringBuilder(); 1363 1364 while (tokens.hasNext()) { 1365 if (tokens.matches(L_PAREN)) { 1366 ++numLeft; 1367 } else if (tokens.matches(R_PAREN)) { 1368 if (numLeft == ++numRight) { 1369 tokens.consume(R_PAREN); // don't include last paren in expression 1370 break; 1371 } 1372 } 1373 1374 final String token = tokens.consume(); 1375 1376 // don't add space if empty or if this token or previous token is a period 1377 if (!PERIOD.equals(token) && (text.length() != 0) && (PERIOD.charAt(0) != (text.charAt(text.length() - 1)))) { 1378 text.append(SPACE); 1379 } 1380 1381 text.append(token); 1382 } 1383 1384 if ((numLeft != numRight) || (text.length() == 0)) { 1385 throw new ParsingException(tokens.nextPosition()); 1386 } 1387 1388 return text.toString(); 1389 } 1390 1391 /** 1392 * If the index type is a bitmap-join the columns are from the dimension tables which are defined in the FROM clause. All 1393 * other index types the columns are from the table the index in on. 1394 * <p> 1395 * <code> 1396 * column-expression == left-paren column-name [ASC | DESC] | constant | function [, column-name [ASC | DESC] | constant | function ]* right-paren 1397 * </code> 1398 * 1399 * @param columnExpressionList the comma separated column expression list (cannot be <code>null</code>) 1400 * @param indexNode the index node whose column expression list is being processed (cannot be <code>null</code>) 1401 */ 1402 private void parseIndexColumnExpressionList( final String columnExpressionList, 1403 final AstNode indexNode ) { 1404 final DdlTokenStream tokens = new DdlTokenStream(columnExpressionList, DdlTokenStream.ddlTokenizer(false), false); 1405 tokens.start(); 1406 1407 tokens.consume(L_PAREN); // must have opening paren 1408 int numLeft = 1; 1409 int numRight = 0; 1410 1411 // must have content between the parens 1412 if (!tokens.matches(R_PAREN)) { 1413 final List<String> possibleColumns = new ArrayList<String>(); // dimension table columns 1414 final List<String> functions = new ArrayList<String>(); // functions, constants 1415 final StringBuilder text = new StringBuilder(); 1416 boolean isFunction = false; 1417 1418 while (tokens.hasNext()) { 1419 if (tokens.canConsume(COMMA)) { 1420 if (isFunction) { 1421 functions.add(text.toString()); 1422 } else { 1423 possibleColumns.add(text.toString()); 1424 } 1425 1426 text.setLength(0); // clear out 1427 isFunction = false; 1428 continue; 1429 } 1430 1431 if (tokens.matches(L_PAREN)) { 1432 isFunction = true; 1433 ++numLeft; 1434 } else if (tokens.matches("ASC") || tokens.matches("DESC")) { 1435 text.append(SPACE); 1436 } else if (tokens.matches(R_PAREN)) { 1437 if (numLeft == ++numRight) { 1438 if (isFunction) { 1439 functions.add(text.toString()); 1440 } else { 1441 possibleColumns.add(text.toString()); 1442 } 1443 1444 break; 1445 } 1446 } 1447 1448 text.append(tokens.consume()); 1449 } 1450 1451 if (!possibleColumns.isEmpty()) { 1452 List<AstNode> tableNodes = null; 1453 final boolean tableIndex = indexNode.hasMixin(OracleDdlLexicon.TYPE_CREATE_TABLE_INDEX_STATEMENT); 1454 1455 // find appropriate table nodes 1456 if (tableIndex) { 1457 // table index so find table node 1458 final String tableName = (String)indexNode.getProperty(OracleDdlLexicon.TABLE_NAME); 1459 final AstNode parent = indexNode.getParent(); 1460 final List<AstNode> nodes = parent.childrenWithName(tableName); 1461 1462 if (!nodes.isEmpty()) { 1463 if (nodes.size() == 1) { 1464 tableNodes = nodes; 1465 } else { 1466 // this should not be possible but check none the less 1467 for (final AstNode node : nodes) { 1468 if (node.hasMixin(StandardDdlLexicon.TYPE_CREATE_TABLE_STATEMENT)) { 1469 tableNodes = new ArrayList<AstNode>(1); 1470 tableNodes.add(node); 1471 break; 1472 } 1473 } 1474 } 1475 } 1476 } else { 1477 // must be bitmap-join 1478 tableNodes = indexNode.getChildren(StandardDdlLexicon.TYPE_TABLE_REFERENCE); 1479 } 1480 1481 if ((tableNodes != null) && !tableNodes.isEmpty()) { 1482 boolean processed = false; 1483 1484 for (String possibleColumn : possibleColumns) { 1485 // first determine any ordering 1486 final int ascIndex = possibleColumn.toUpperCase().indexOf(" ASC"); 1487 final boolean asc = (ascIndex != -1); 1488 final int descIndex = possibleColumn.toUpperCase().indexOf(" DESC"); 1489 boolean desc = (descIndex != -1); 1490 1491 // adjust column name if there is ordering 1492 if (asc) { 1493 possibleColumn = possibleColumn.substring(0, ascIndex); 1494 } else if (desc) { 1495 possibleColumn = possibleColumn.substring(0, descIndex); 1496 } 1497 1498 if (tableIndex) { 1499 if (tableNodes.isEmpty()) { 1500 if (asc) { 1501 functions.add(possibleColumn + SPACE + "ASC"); 1502 } else if (desc) { 1503 functions.add(possibleColumn + SPACE + "DESC"); 1504 } else { 1505 functions.add(possibleColumn); 1506 } 1507 } else { 1508 // only one table reference. need to find column. 1509 final AstNode tableNode = tableNodes.get(0); 1510 final List<AstNode> columnNodes = tableNode.getChildren(StandardDdlLexicon.TYPE_COLUMN_DEFINITION); 1511 1512 if (!columnNodes.isEmpty()) { 1513 // find column 1514 for (final AstNode colNode : columnNodes) { 1515 if (colNode.getName().equalsIgnoreCase(possibleColumn)) { 1516 final AstNode colRef = nodeFactory().node(possibleColumn, 1517 indexNode, 1518 TYPE_COLUMN_REFERENCE); 1519 1520 if (asc || desc) { 1521 colRef.addMixin(OracleDdlLexicon.TYPE_INDEX_ORDERABLE); 1522 1523 if (asc) { 1524 colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "ASC"); 1525 } else { 1526 colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "DESC"); 1527 } 1528 } 1529 1530 processed = true; 1531 break; 1532 } 1533 } 1534 } 1535 1536 if (!processed) { 1537 if (asc) { 1538 functions.add(possibleColumn + SPACE + "ASC"); 1539 } else if (desc) { 1540 functions.add(possibleColumn + SPACE + "DESC"); 1541 } else { 1542 functions.add(possibleColumn); 1543 } 1544 1545 processed = true; 1546 } 1547 } 1548 } else { 1549 // bitmap-join 1550 for (final AstNode dimensionTableNode : tableNodes) { 1551 if (possibleColumn.toUpperCase(Locale.ROOT).startsWith(dimensionTableNode.getName().toUpperCase(Locale.ROOT) + PERIOD)) { 1552 final AstNode colRef = nodeFactory().node(possibleColumn, indexNode, TYPE_COLUMN_REFERENCE); 1553 1554 if (asc || desc) { 1555 colRef.addMixin(OracleDdlLexicon.TYPE_INDEX_ORDERABLE); 1556 1557 if (asc) { 1558 colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "ASC"); 1559 } else { 1560 colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "DESC"); 1561 } 1562 } 1563 1564 processed = true; 1565 break; 1566 } 1567 } 1568 1569 // probably a constant or function 1570 if (!processed) { 1571 if (asc) { 1572 functions.add(possibleColumn + SPACE + "ASC"); 1573 } else if (desc) { 1574 functions.add(possibleColumn + SPACE + "DESC"); 1575 } else { 1576 functions.add(possibleColumn); 1577 } 1578 1579 processed = true; 1580 } 1581 } 1582 } 1583 } 1584 } 1585 1586 if (!functions.isEmpty()) { 1587 indexNode.setProperty(OracleDdlLexicon.OTHER_INDEX_REFS, functions); 1588 } 1589 } 1590 1591 if (numLeft != numRight) { 1592 throw new ParsingException(tokens.nextPosition()); 1593 } 1594 1595 tokens.consume(R_PAREN); // must have closing paren 1596 } 1597 1598 /** 1599 * Parses DDL CREATE INDEX 1600 * <p> 1601 * <code> 1602 * CREATE [ UNIQUE | BITMAP ] INDEX index-name ON { cluster_index_clause | table_index_clause | bitmap_join_index_clause } [index_attributes] [UNUSABLE] 1603 * 1604 * cluster_index_clause = CLUSTER cluster-name 1605 * table_index_clause = table-name [table-alias] ( { column | constant | function } [ ASC | DESC ] [ , { column | column_expression } [ ASC | DESC ]] * ) 1606 * bitmap_join_index_clause = table-name ( column [ASC | DESC] [, column [ASC | DESC]] ) FROM table-name [, table-name] WHERE condition [local-partition-index] 1607 * 1608 * </code> 1609 * 1610 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 1611 * @param parentNode the parent {@link AstNode} node; may not be null 1612 * @return the parsed CREATE INDEX 1613 * @throws ParsingException 1614 */ 1615 private AstNode parseCreateIndex( final DdlTokenStream tokens, 1616 final AstNode parentNode ) throws ParsingException { 1617 assert tokens != null; 1618 assert parentNode != null; 1619 assert (tokens.matches(STMT_CREATE_INDEX) || tokens.matches(STMT_CREATE_UNIQUE_INDEX) || tokens.matches(STMT_CREATE_BITMAP_INDEX)); 1620 1621 markStartOfStatement(tokens); 1622 tokens.consume(CREATE); 1623 1624 final boolean isUnique = tokens.canConsume(UNIQUE); 1625 final boolean isBitmap = tokens.canConsume("BITMAP"); 1626 1627 tokens.consume(INDEX); 1628 final String indexName = parseName(tokens); 1629 1630 tokens.consume(ON); 1631 1632 AstNode indexNode = null; 1633 1634 if (tokens.canConsume("CLUSTER")) { 1635 // table-cluster_index_clause 1636 indexNode = nodeFactory().node(indexName, parentNode, TYPE_CREATE_CLUSTER_INDEX_STATEMENT); 1637 indexNode.setProperty(OracleDdlLexicon.INDEX_TYPE, OracleDdlConstants.IndexTypes.CLUSTER); 1638 1639 final String clusterName = parseName(tokens); 1640 indexNode.setProperty(OracleDdlLexicon.CLUSTER_NAME, clusterName); 1641 } else { 1642 final String tableName = parseName(tokens); 1643 1644 if (!tokens.matches('(')) { 1645 // must be a table-index-clause as this has to be table-alias 1646 final String tableAlias = tokens.consume(); 1647 indexNode = nodeFactory().node(indexName, parentNode, TYPE_CREATE_TABLE_INDEX_STATEMENT); 1648 indexNode.setProperty(OracleDdlLexicon.INDEX_TYPE, OracleDdlConstants.IndexTypes.TABLE); 1649 indexNode.setProperty(OracleDdlLexicon.TABLE_ALIAS, tableAlias); 1650 } 1651 1652 // parse left-paren content right-paren 1653 final String columnExpressionList = parseContentBetweenParens(tokens); 1654 1655 // must have FROM and WHERE clauses 1656 if (tokens.canConsume("FROM")) { 1657 indexNode = nodeFactory().node(indexName, parentNode, TYPE_CREATE_BITMAP_JOIN_INDEX_STATEMENT); 1658 indexNode.setProperty(OracleDdlLexicon.INDEX_TYPE, OracleDdlConstants.IndexTypes.BITMAP_JOIN); 1659 parseTableReferenceList(tokens, indexNode); 1660 1661 tokens.consume("WHERE"); 1662 final String whereClause = parseUntilTerminator(tokens); // this will have index attributes also:-( 1663 indexNode.setProperty(OracleDdlLexicon.WHERE_CLAUSE, whereClause); 1664 } else { 1665 indexNode = nodeFactory().node(indexName, parentNode, TYPE_CREATE_TABLE_INDEX_STATEMENT); 1666 indexNode.setProperty(OracleDdlLexicon.INDEX_TYPE, OracleDdlConstants.IndexTypes.TABLE); 1667 } 1668 1669 indexNode.setProperty(OracleDdlLexicon.TABLE_NAME, tableName); 1670 parseIndexColumnExpressionList('(' + columnExpressionList + ')', indexNode); 1671 } 1672 1673 indexNode.setProperty(UNIQUE_INDEX, isUnique); 1674 indexNode.setProperty(BITMAP_INDEX, isBitmap); 1675 1676 // index attributes are optional as is UNUSABLE 1677 if (tokens.hasNext()) { 1678 boolean unusable = false; 1679 final List<String> indexAttributes = new ArrayList<String>(); 1680 1681 while (tokens.hasNext() && !isTerminator(tokens)) { 1682 String token = tokens.consume(); 1683 1684 if ("UNUSABLE".equalsIgnoreCase(token)) { 1685 unusable = true; 1686 break; // must be last token found before terminator 1687 } 1688 1689 // if number add it to previous 1690 boolean processed = false; 1691 1692 if (token.matches("\\b\\d+\\b")) { 1693 if (!indexAttributes.isEmpty()) { 1694 final int index = (indexAttributes.size() - 1); 1695 final String value = indexAttributes.get(index); 1696 final String newValue = (value + SPACE + token); 1697 indexAttributes.set(index, newValue); 1698 processed = true; 1699 } 1700 } 1701 1702 if (!processed) { 1703 indexAttributes.add(token); 1704 } 1705 } 1706 1707 if (!indexAttributes.isEmpty()) { 1708 indexNode.setProperty(OracleDdlLexicon.INDEX_ATTRIBUTES, indexAttributes); 1709 } 1710 1711 indexNode.setProperty(OracleDdlLexicon.UNUSABLE_INDEX, unusable); 1712 } 1713 1714 markEndOfStatement(tokens, indexNode); 1715 return indexNode; 1716 } 1717 1718 private void parseTableReferenceList( final DdlTokenStream tokens, 1719 final AstNode parentNode ) { 1720 final List<String> tableRefs = parseNameList(tokens); 1721 1722 if (!tableRefs.isEmpty()) { 1723 for (String tableName : tableRefs) { 1724 nodeFactory().node(tableName, parentNode, TYPE_TABLE_REFERENCE); 1725 } 1726 } 1727 } 1728 1729 private AstNode parseCommentStatement( DdlTokenStream tokens, 1730 AstNode parentNode ) throws ParsingException { 1731 assert tokens != null; 1732 assert parentNode != null; 1733 1734 markStartOfStatement(tokens); 1735 1736 // COMMENT ON COLUMN CS_EXT_FILES.FILE_UID IS 1737 // 'UNIQUE INTERNAL IDENTIFIER, NOT EXPOSED' 1738 // % 1739 1740 /* 1741 commentCommand 1742 : 'COMMENT' 'ON' 1743 ( 'TABLE' tableName 1744 | 'VIEW' tableName 1745 | 'INDEX' indexName 1746 | 'COLUMN' columnName 1747 | 'PROCEDURE' procedureId 1748 | 'MATERIALIZED'? 'VIEW' tableName 1749 ) 'IS' ('NULL' | (CharLiteral)+) commandEnd? //CharLiteral (',' unicodeCharLiteral)*) commandEnd? 1750 ; 1751 */ 1752 tokens.consume("COMMENT"); // consumes 'COMMENT' 'ON' 1753 tokens.consume("ON"); 1754 String obj = tokens.consume(); 1755 String objName = parseName(tokens); 1756 1757 // System.out.println(" >> FOUND [COMMENT ON] STATEMENT >> TABLE Name = " + objName); 1758 String commentString = null; 1759 1760 tokens.consume("IS"); 1761 if (tokens.matches("NULL")) { 1762 tokens.consume("NULL"); 1763 commentString = "NULL"; 1764 } else { 1765 commentString = parseUntilTerminator(tokens).trim(); 1766 } 1767 1768 AstNode commentNode = nodeFactory().node(objName, parentNode, TYPE_COMMENT_ON_STATEMENT); 1769 commentNode.setProperty(OracleDdlLexicon.COMMENT, commentString); 1770 commentNode.setProperty(OracleDdlLexicon.TARGET_OBJECT_TYPE, obj); 1771 1772 markEndOfStatement(tokens, commentNode); 1773 1774 return commentNode; 1775 } 1776 1777 @Override 1778 protected AstNode parseSetStatement( DdlTokenStream tokens, 1779 AstNode parentNode ) throws ParsingException { 1780 assert tokens != null; 1781 assert parentNode != null; 1782 1783 if (tokens.matches(STMT_SET_CONSTRAINT)) { 1784 return parseStatement(tokens, STMT_SET_CONSTRAINT, parentNode, TYPE_SET_CONSTRAINT_STATEMENT); 1785 } else if (tokens.matches(STMT_SET_CONSTRAINTS)) { 1786 return parseStatement(tokens, STMT_SET_CONSTRAINTS, parentNode, TYPE_SET_CONSTRAINTS_STATEMENT); 1787 } else if (tokens.matches(STMT_SET_ROLE)) { 1788 return parseStatement(tokens, STMT_SET_ROLE, parentNode, TYPE_SET_ROLE_STATEMENT); 1789 } else if (tokens.matches(STMT_SET_TRANSACTION)) { 1790 return parseStatement(tokens, STMT_SET_TRANSACTION, parentNode, TYPE_SET_TRANSACTION_STATEMENT); 1791 } 1792 1793 return super.parseSetStatement(tokens, parentNode); 1794 } 1795 1796 @Override 1797 protected AstNode parseDropStatement( DdlTokenStream tokens, 1798 AstNode parentNode ) throws ParsingException { 1799 assert tokens != null; 1800 assert parentNode != null; 1801 1802 AstNode dropNode = null; 1803 1804 if (tokens.matches(StatementStartPhrases.STMT_DROP_TABLE)) { 1805 markStartOfStatement(tokens); 1806 1807 // DROP TABLE [ schema. ]table [ CASCADE CONSTRAINTS ] [ PURGE ] ; 1808 1809 tokens.consume(DROP, TABLE); 1810 1811 String name = parseName(tokens); 1812 dropNode = nodeFactory().node(name, parentNode, TYPE_DROP_TABLE_STATEMENT); 1813 1814 if (tokens.canConsume("CASCADE", "CONSTRAINTTS")) { 1815 dropNode.setProperty(DROP_BEHAVIOR, "CASCADE CONSTRAINTS"); 1816 } 1817 1818 if (tokens.canConsume("PURGE")) { 1819 AstNode optionNode = nodeFactory().node(DROP_OPTION, dropNode, TYPE_STATEMENT_OPTION); 1820 optionNode.setProperty(StandardDdlLexicon.VALUE, "PURGE"); 1821 } 1822 1823 markEndOfStatement(tokens, dropNode); 1824 1825 return dropNode; 1826 } else if (tokens.matches(STMT_DROP_CLUSTER)) { 1827 return parseStatement(tokens, STMT_DROP_CLUSTER, parentNode, TYPE_DROP_CLUSTER_STATEMENT); 1828 } else if (tokens.matches(STMT_DROP_CONTEXT)) { 1829 return parseStatement(tokens, STMT_DROP_CONTEXT, parentNode, TYPE_DROP_CONTEXT_STATEMENT); 1830 } else if (tokens.matches(STMT_DROP_DATABASE)) { 1831 return parseStatement(tokens, STMT_DROP_DATABASE, parentNode, TYPE_DROP_DATABASE_STATEMENT); 1832 } else if (tokens.matches(STMT_DROP_PUBLIC_DATABASE)) { 1833 return parseStatement(tokens, STMT_DROP_PUBLIC_DATABASE, parentNode, TYPE_DROP_DATABASE_STATEMENT); 1834 } else if (tokens.matches(STMT_DROP_DIMENSION)) { 1835 return parseStatement(tokens, STMT_DROP_DIMENSION, parentNode, TYPE_DROP_DIMENSION_STATEMENT); 1836 } else if (tokens.matches(STMT_DROP_DIRECTORY)) { 1837 return parseStatement(tokens, STMT_DROP_DIRECTORY, parentNode, TYPE_DROP_DIRECTORY_STATEMENT); 1838 } else if (tokens.matches(STMT_DROP_DISKGROUP)) { 1839 return parseStatement(tokens, STMT_DROP_DISKGROUP, parentNode, TYPE_DROP_DISKGROUP_STATEMENT); 1840 } else if (tokens.matches(STMT_DROP_FUNCTION)) { 1841 return parseStatement(tokens, STMT_DROP_FUNCTION, parentNode, TYPE_DROP_FUNCTION_STATEMENT); 1842 } else if (tokens.matches(STMT_DROP_INDEX)) { 1843 return parseStatement(tokens, STMT_DROP_INDEX, parentNode, TYPE_DROP_INDEX_STATEMENT); 1844 } else if (tokens.matches(STMT_DROP_INDEXTYPE)) { 1845 return parseStatement(tokens, STMT_DROP_INDEXTYPE, parentNode, TYPE_DROP_INDEXTYPE_STATEMENT); 1846 } else if (tokens.matches(STMT_DROP_JAVA)) { 1847 return parseStatement(tokens, STMT_DROP_JAVA, parentNode, TYPE_DROP_JAVA_STATEMENT); 1848 } else if (tokens.matches(STMT_DROP_LIBRARY)) { 1849 return parseStatement(tokens, STMT_DROP_LIBRARY, parentNode, TYPE_DROP_LIBRARY_STATEMENT); 1850 } else if (tokens.matches(STMT_DROP_MATERIALIZED)) { 1851 return parseStatement(tokens, STMT_DROP_MATERIALIZED, parentNode, TYPE_DROP_MATERIALIZED_STATEMENT); 1852 } else if (tokens.matches(STMT_DROP_OPERATOR)) { 1853 return parseStatement(tokens, STMT_DROP_OPERATOR, parentNode, TYPE_DROP_OPERATOR_STATEMENT); 1854 } else if (tokens.matches(STMT_DROP_OUTLINE)) { 1855 return parseStatement(tokens, STMT_DROP_OUTLINE, parentNode, TYPE_DROP_OUTLINE_STATEMENT); 1856 } else if (tokens.matches(STMT_DROP_PACKAGE)) { 1857 return parseStatement(tokens, STMT_DROP_PACKAGE, parentNode, TYPE_DROP_PACKAGE_STATEMENT); 1858 } else if (tokens.matches(STMT_DROP_PROCEDURE)) { 1859 return parseStatement(tokens, STMT_DROP_PROCEDURE, parentNode, TYPE_DROP_PROCEDURE_STATEMENT); 1860 } else if (tokens.matches(STMT_DROP_PROFILE)) { 1861 return parseStatement(tokens, STMT_DROP_PROFILE, parentNode, TYPE_DROP_PROFILE_STATEMENT); 1862 } else if (tokens.matches(STMT_DROP_ROLE)) { 1863 return parseStatement(tokens, STMT_DROP_ROLE, parentNode, TYPE_DROP_ROLE_STATEMENT); 1864 } else if (tokens.matches(STMT_DROP_ROLLBACK)) { 1865 return parseStatement(tokens, STMT_DROP_ROLLBACK, parentNode, TYPE_DROP_ROLLBACK_STATEMENT); 1866 } else if (tokens.matches(STMT_DROP_SEQUENCE)) { 1867 return parseStatement(tokens, STMT_DROP_SEQUENCE, parentNode, TYPE_DROP_SEQUENCE_STATEMENT); 1868 } else if (tokens.matches(STMT_DROP_SYNONYM)) { 1869 return parseStatement(tokens, STMT_DROP_SYNONYM, parentNode, TYPE_DROP_SYNONYM_STATEMENT); 1870 } else if (tokens.matches(STMT_DROP_PUBLIC_SYNONYM)) { 1871 return parseStatement(tokens, STMT_DROP_PUBLIC_SYNONYM, parentNode, TYPE_DROP_SYNONYM_STATEMENT); 1872 } else if (tokens.matches(STMT_DROP_TABLESPACE)) { 1873 return parseStatement(tokens, STMT_DROP_TABLESPACE, parentNode, TYPE_DROP_TABLESPACE_STATEMENT); 1874 } else if (tokens.matches(STMT_DROP_TRIGGER)) { 1875 return parseStatement(tokens, STMT_DROP_TRIGGER, parentNode, TYPE_DROP_TRIGGER_STATEMENT); 1876 } else if (tokens.matches(STMT_DROP_TYPE)) { 1877 return parseStatement(tokens, STMT_DROP_TYPE, parentNode, TYPE_DROP_TYPE_STATEMENT); 1878 } else if (tokens.matches(STMT_DROP_USER)) { 1879 return parseStatement(tokens, STMT_DROP_USER, parentNode, TYPE_DROP_USER_STATEMENT); 1880 } 1881 1882 return super.parseDropStatement(tokens, parentNode); 1883 } 1884 1885 private AstNode parseCreateJavaStatement( DdlTokenStream tokens, 1886 AstNode parentNode ) throws ParsingException { 1887 assert tokens != null; 1888 assert parentNode != null; 1889 1890 markStartOfStatement(tokens); 1891 tokens.consume(STMT_CREATE_JAVA); 1892 AstNode result = nodeFactory().node(getStatementTypeName(STMT_CREATE_JAVA), parentNode, TYPE_CREATE_JAVA_STATEMENT); 1893 1894 // We want to parse until we find a terminator AND all brackets are matched. 1895 1896 // Assume we start with open parenthesis '{', then we can count on walking through ALL tokens until we find the close 1897 // parenthesis '}'. If there are intermediate parenthesis, we can count on them being pairs. 1898 1899 int iParen = 0; 1900 1901 while (tokens.hasNext()) { 1902 if (tokens.matches('{')) { 1903 iParen++; 1904 } else if (tokens.matches('}')) { 1905 iParen--; 1906 } 1907 tokens.consume(); 1908 1909 if (isTerminator(tokens) && iParen == 0) { 1910 break; 1911 } 1912 } 1913 1914 markEndOfStatement(tokens, result); 1915 1916 return result; 1917 } 1918 1919 /** 1920 * Utility method to additionally check if MODIFY definition without datatype 1921 * 1922 * @param tokens a {@link DdlTokenStream} instance; may not be null 1923 * @param columnMixinType a {@link String}; may not be null 1924 * @return {@code true} if the given stream si at the start of the column defintion, {@code false} otherwise. 1925 * @throws ParsingException 1926 */ 1927 protected boolean isColumnDefinitionStart( DdlTokenStream tokens, 1928 String columnMixinType ) throws ParsingException { 1929 boolean result = isColumnDefinitionStart(tokens); 1930 if (!result && TYPE_ALTER_COLUMN_DEFINITION.equals(columnMixinType)) { 1931 for (String start : INLINE_COLUMN_PROPERTY_START) { 1932 if (tokens.matches(TokenStream.ANY_VALUE, start)) return true; 1933 } 1934 } 1935 return result; 1936 } 1937 1938 /** 1939 * Utility method designed to parse columns within an ALTER TABLE ADD/MODIFY statement. 1940 * 1941 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 1942 * @param tableNode 1943 * @param columnMixinType 1944 * @throws ParsingException 1945 */ 1946 protected void parseColumns( DdlTokenStream tokens, 1947 AstNode tableNode, 1948 String columnMixinType ) throws ParsingException { 1949 assert tokens != null; 1950 assert tableNode != null; 1951 1952 // TODO: Oracle has changed some things between versions 9i, and 10/11, 1953 // Basically they've added column properties (i.e. SORT option, ENCRYPT encryption_spec) 1954 // Need to 1) Override parseColumnDefinition shouldParseOracleProceduresAndFunctionsto handle these. 1955 1956 boolean isInParen = tokens.matches(L_PAREN); 1957 1958 String tableElementString = getTableElementsString(tokens, false); 1959 1960 DdlTokenStream localTokens = new DdlTokenStream(tableElementString, DdlTokenStream.ddlTokenizer(false), false); 1961 1962 localTokens.start(); 1963 1964 StringBuilder unusedTokensSB = new StringBuilder(); 1965 1966 do { 1967 if (isColumnDefinitionStart(localTokens, columnMixinType)) { 1968 parseColumnDefinition(localTokens, tableNode, columnMixinType); 1969 } else { 1970 // THIS IS AN ERROR (WARNING if it is an ALTER TABLE MODIFY part). NOTHING FOUND. 1971 // NEED TO absorb tokens 1972 while (localTokens.hasNext() && !localTokens.matches(COMMA)) { 1973 unusedTokensSB.append(SPACE).append(localTokens.consume()); 1974 } 1975 } 1976 } while (localTokens.canConsume(COMMA)); 1977 1978 // columns inside parentheses should not leave unused tokens 1979 if (isInParen) { 1980 while (localTokens.hasNext()) { 1981 unusedTokensSB.append(SPACE).append(localTokens.consume()); 1982 } 1983 } 1984 1985 if (unusedTokensSB.length() > 0 && TYPE_ADD_COLUMN_DEFINITION.equals(columnMixinType)) { 1986 String msg = DdlSequencerI18n.unusedTokensParsingColumnDefinition.text(tableNode.getName()); 1987 DdlParserProblem problem = new DdlParserProblem(Problems.WARNING, getCurrentMarkedPosition(), msg); 1988 problem.setUnusedSource(unusedTokensSB.toString()); 1989 addProblem(problem, tableNode); 1990 } 1991 } 1992 1993 /** 1994 * Overloaded version of method with default mixin type set to {@link StandardDdlLexicon#TYPE_ADD_COLUMN_DEFINITION} 1995 * 1996 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 1997 * @param tableNode 1998 * @param isAlterTable 1999 * @throws ParsingException 2000 */ 2001 protected void parseColumns( DdlTokenStream tokens, 2002 AstNode tableNode, 2003 boolean isAlterTable ) throws ParsingException { 2004 parseColumns(tokens, tableNode, (isAlterTable ? TYPE_ALTER_COLUMN_DEFINITION : TYPE_ADD_COLUMN_DEFINITION)); 2005 } 2006 2007 /** 2008 * Alternative StandardDdlParser method that can handle column definition without datatype (for MODIFY) and supports setting 2009 * mixin type of a column. 2010 * 2011 * @param tokens the {@link DdlTokenStream} representing the tokenized DDL content; may not be null 2012 * @param tableNode 2013 * @param columnMixinType 2014 * @throws ParsingException 2015 */ 2016 protected void parseColumnDefinition( DdlTokenStream tokens, 2017 AstNode tableNode, 2018 String columnMixinType ) throws ParsingException { 2019 assert tokens != null; 2020 assert tableNode != null; 2021 2022 boolean isAlterTable = columnMixinType != TYPE_COLUMN_DEFINITION; 2023 2024 tokens.canConsume("COLUMN"); 2025 String columnName = parseName(tokens); 2026 AstNode columnNode = nodeFactory().node(columnName, tableNode, columnMixinType); 2027 2028 DataType datatype = getDatatypeParser().parse(tokens); 2029 if (datatype != null) { 2030 getDatatypeParser().setPropertiesOnNode(columnNode, datatype); 2031 } 2032 2033 // Now clauses and constraints can be defined in any order, so we need to keep parsing until we get to a comma or 2034 // if statement is an alter where multiple single column add/modify clauses can be in a single command, break parsing 2035 // also on ADD/MODIFY 2036 StringBuilder unusedTokensSB = new StringBuilder(); 2037 2038 while (tokens.hasNext() && !tokens.matches(COMMA) && !(isAlterTable && tokens.matchesAnyOf("ADD", "MODIFY"))) { 2039 boolean parsedDefaultClause = parseDefaultClause(tokens, columnNode); 2040 if (!parsedDefaultClause) { 2041 boolean parsedCollate = parseCollateClause(tokens, columnNode); 2042 boolean parsedConstraint = parseColumnConstraint(tokens, columnNode, isAlterTable); 2043 if (!parsedCollate && !parsedConstraint) { 2044 // THIS IS AN ERROR. NOTHING FOUND. 2045 // NEED TO absorb tokens 2046 unusedTokensSB.append(SPACE).append(tokens.consume()); 2047 } 2048 } 2049 tokens.canConsume(DdlTokenizer.COMMENT); 2050 } 2051 2052 if ((unusedTokensSB.length() == 1 && unusedTokensSB.charAt(0) == ';') || (unusedTokensSB.length() > 1)) { 2053 String msg = DdlSequencerI18n.unusedTokensParsingColumnDefinition.text(tableNode.getName()); 2054 DdlParserProblem problem = new DdlParserProblem(Problems.WARNING, Position.EMPTY_CONTENT_POSITION, msg); 2055 problem.setUnusedSource(unusedTokensSB.toString()); 2056 addProblem(problem, tableNode); 2057 } 2058 } 2059 2060 /** 2061 * Utility method to parse a generic statement given a start phrase and statement mixin type. 2062 * 2063 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 2064 * @param stmt_start_phrase the string array statement start phrase 2065 * @param parentNode the parent {@link AstNode} node; may not be null 2066 * @param mixinType the mixin type of the newly created statement node 2067 * @return the new node 2068 */ 2069 protected AstNode parseSlashedStatement( DdlTokenStream tokens, 2070 String[] stmt_start_phrase, 2071 AstNode parentNode, 2072 String mixinType ) { 2073 assert tokens != null; 2074 assert stmt_start_phrase != null && stmt_start_phrase.length > 0; 2075 assert parentNode != null; 2076 2077 markStartOfStatement(tokens); 2078 tokens.consume(stmt_start_phrase); 2079 AstNode result = nodeFactory().node(getStatementTypeName(stmt_start_phrase), parentNode, mixinType); 2080 2081 parseUntilFwdSlash(tokens, false); 2082 2083 consumeSlash(tokens); 2084 2085 markEndOfStatement(tokens, result); 2086 2087 return result; 2088 } 2089 2090 /** 2091 * Various Oracle statements (i.e. "CREATE OR REPLACE PACKAGE", etc...) may contain multiple SQL statements that will be 2092 * terminated by the semicolon, ';'. In these cases, the terminator is now the '/' character. 2093 * 2094 * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null 2095 * @param stopAtStatementStart 2096 * @return the parsed string. 2097 * @throws ParsingException 2098 */ 2099 private String parseUntilFwdSlash( DdlTokenStream tokens, 2100 boolean stopAtStatementStart ) throws ParsingException { 2101 StringBuilder sb = new StringBuilder(); 2102 if (stopAtStatementStart) { 2103 while (tokens.hasNext() 2104 2105 && !tokens.matches(DdlTokenizer.STATEMENT_KEY) && !tokens.matches('/')) { // !tokens.matches(DdlTokenizer.STATEMENT_KEY 2106 // ) 2107 // && 2108 sb.append(SPACE).append(tokens.consume()); 2109 } 2110 } else { 2111 while (tokens.hasNext() && !isFwdSlashedStatement(tokens) && !tokens.matches('/')) { // !tokens.matches(DdlTokenizer. 2112 // STATEMENT_KEY) && 2113 sb.append(SPACE).append(tokens.consume()); 2114 } 2115 } 2116 return sb.toString(); 2117 } 2118 2119 private boolean isFwdSlashedStatement( DdlTokenStream tokens ) throws ParsingException { 2120 for (int i = 0; i < SLASHED_STMT_PHRASES.length; i++) { 2121 if (tokens.matches(SLASHED_STMT_PHRASES[i])) { 2122 return true; 2123 } 2124 } 2125 2126 return false; 2127 } 2128 2129 private void consumeSlash( DdlTokenStream tokens ) throws ParsingException { 2130 tokens.canConsume("/"); 2131 } 2132 2133 /** 2134 * {@inheritDoc} 2135 * 2136 * @see org.modeshape.sequencer.ddl.StandardDdlParser#getValidSchemaChildTypes() 2137 */ 2138 @Override 2139 protected String[] getValidSchemaChildTypes() { 2140 return VALID_SCHEMA_CHILD_STMTS; 2141 } 2142 2143 // =========================================================================================================================== 2144 // PARSE OBJECTS 2145 // =========================================================================================================================== 2146 2147 /** 2148 * This class provides custom data type parsing for Oracle-specific data types. 2149 */ 2150 class OracleDataTypeParser extends DataTypeParser { 2151 2152 /* 2153 * (non-Javadoc) 2154 * @see org.modeshape.sequencer.ddl.datatype.DataTypeParser#parseCustomType(org.modeshape.common.text.DdlTokenStream) 2155 */ 2156 @Override 2157 protected DataType parseCustomType( DdlTokenStream tokens ) throws ParsingException { 2158 DataType dataType = null; 2159 String typeName = null; 2160 2161 if (tokens.matches(OracleDataTypes.DTYPE_BINARY_FLOAT)) { 2162 dataType = new DataType(); 2163 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BINARY_FLOAT); 2164 dataType.setName(typeName); 2165 } else if (tokens.matches(OracleDataTypes.DTYPE_BINARY_DOUBLE)) { 2166 dataType = new DataType(); 2167 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BINARY_DOUBLE); 2168 dataType.setName(typeName); 2169 } else if (tokens.matches(OracleDataTypes.DTYPE_LONG)) { 2170 dataType = new DataType(); 2171 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_LONG); 2172 dataType.setName(typeName); 2173 } else if (tokens.matches(OracleDataTypes.DTYPE_LONG_RAW)) { 2174 dataType = new DataType(); 2175 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_LONG_RAW); 2176 dataType.setName(typeName); 2177 } else if (tokens.matches(OracleDataTypes.DTYPE_BLOB)) { 2178 dataType = new DataType(); 2179 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BLOB); 2180 dataType.setName(typeName); 2181 } else if (tokens.matches(OracleDataTypes.DTYPE_CLOB)) { 2182 dataType = new DataType(); 2183 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_CLOB); 2184 dataType.setName(typeName); 2185 } else if (tokens.matches(OracleDataTypes.DTYPE_NCLOB)) { 2186 dataType = new DataType(); 2187 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_NCLOB); 2188 dataType.setName(typeName); 2189 } else if (tokens.matches(OracleDataTypes.DTYPE_BFILE)) { 2190 dataType = new DataType(); 2191 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BFILE); 2192 dataType.setName(typeName); 2193 } else if (tokens.matches(OracleDataTypes.DTYPE_VARCHAR2)) { 2194 dataType = new DataType(); 2195 // VARCHAR2(size [BYTE | CHAR]) 2196 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_VARCHAR2); // VARCHAR2 2197 consume(tokens, dataType, false, L_PAREN); 2198 long length = parseLong(tokens, dataType); 2199 canConsume(tokens, dataType, true, "BYTE"); 2200 canConsume(tokens, dataType, true, "CHAR"); 2201 consume(tokens, dataType, false, R_PAREN); 2202 dataType.setName(typeName); 2203 dataType.setLength(length); 2204 } else if (tokens.matches(OracleDataTypes.DTYPE_RAW)) { 2205 dataType = new DataType(); 2206 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_RAW); 2207 long length = parseBracketedLong(tokens, dataType); 2208 dataType.setName(typeName); 2209 dataType.setLength(length); 2210 } else if (tokens.matches(OracleDataTypes.DTYPE_NVARCHAR2)) { 2211 dataType = new DataType(); 2212 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_NVARCHAR2); 2213 long length = parseBracketedLong(tokens, dataType); 2214 dataType.setName(typeName); 2215 dataType.setLength(length); 2216 } else if (tokens.matches(OracleDataTypes.DTYPE_NUMBER)) { 2217 dataType = new DataType(); 2218 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_NUMBER); 2219 int precision = 0; 2220 int scale = 0; 2221 if (tokens.matches(L_PAREN)) { 2222 consume(tokens, dataType, false, L_PAREN); 2223 precision = (int)parseLong(tokens, dataType); 2224 if (canConsume(tokens, dataType, false, COMMA)) { 2225 scale = (int)parseLong(tokens, dataType); 2226 } else { 2227 scale = getDefaultScale(); 2228 } 2229 consume(tokens, dataType, false, R_PAREN); 2230 } else { 2231 precision = getDefaultPrecision(); 2232 scale = getDefaultScale(); 2233 } 2234 dataType.setName(typeName); 2235 dataType.setPrecision(precision); 2236 dataType.setScale(scale); 2237 } else if (tokens.matches(OracleDataTypes.DTYPE_INTERVAL_YEAR)) { 2238 // INTERVAL YEAR (year_precision) TO MONTH 2239 2240 } else if (tokens.matches(OracleDataTypes.DTYPE_INTERVAL_DAY)) { 2241 // INTERVAL DAY (day_precision) TO SECOND (fractional_seconds_precision) 2242 } 2243 2244 if (dataType == null) { 2245 dataType = super.parseCustomType(tokens); 2246 } 2247 2248 return dataType; 2249 } 2250 2251 /** 2252 * Because Oracle has an additional option on the CHAR datatype, we need to override the super method, check for CHAR type 2253 * and parse, else call super.parseCharStringType(). {@inheritDoc} 2254 * 2255 * @see org.modeshape.sequencer.ddl.datatype.DataTypeParser#parseCharStringType(org.modeshape.sequencer.ddl.DdlTokenStream) 2256 */ 2257 @Override 2258 protected DataType parseCharStringType( DdlTokenStream tokens ) throws ParsingException { 2259 DataType dataType = null; 2260 2261 if (tokens.matches(OracleDataTypes.DTYPE_CHAR_ORACLE)) { // CHAR (size [BYTE | CHAR]) (i.e. CHAR (10 BYTE) ) 2262 dataType = new DataType(); 2263 String typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_CHAR_ORACLE); 2264 consume(tokens, dataType, false, L_PAREN); 2265 long length = parseLong(tokens, dataType); 2266 canConsume(tokens, dataType, true, "BYTE"); 2267 canConsume(tokens, dataType, true, "CHAR"); 2268 consume(tokens, dataType, false, R_PAREN); 2269 dataType.setName(typeName); 2270 dataType.setLength(length); 2271 } else { 2272 dataType = super.parseCharStringType(tokens); 2273 } 2274 2275 return dataType; 2276 } 2277 2278 /** 2279 * {@inheritDoc} 2280 * 2281 * @see org.modeshape.sequencer.ddl.datatype.DataTypeParser#isCustomDataType(org.modeshape.sequencer.ddl.DdlTokenStream) 2282 */ 2283 @Override 2284 protected boolean isCustomDataType( DdlTokenStream tokens ) throws ParsingException { 2285 for (String[] stmt : oracleDataTypeStrings) { 2286 if (tokens.matches(stmt)) return true; 2287 } 2288 2289 return false; 2290 } 2291 2292 } 2293 2294 /** 2295 * {@inheritDoc} 2296 * 2297 * @see org.modeshape.sequencer.ddl.StandardDdlParser#getDataTypeStartWords() 2298 */ 2299 @Override 2300 protected List<String> getCustomDataTypeStartWords() { 2301 return OracleDataTypes.CUSTOM_DATATYPE_START_WORDS; 2302 } 2303}