View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.sequencer.ddl.dialect.oracle;
25  
26  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.CREATE_VIEW_QUERY_EXPRESSION;
27  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DROP_BEHAVIOR;
28  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.DROP_OPTION_TYPE;
29  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.NEW_NAME;
30  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_ALTER_TABLE_STATEMENT;
31  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_COLUMN_REFERENCE;
32  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_CREATE_VIEW_STATEMENT;
33  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_DROP_COLUMN_DEFINITION;
34  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_DROP_TABLE_CONSTRAINT_DEFINITION;
35  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_DROP_TABLE_STATEMENT;
36  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_GRANT_STATEMENT;
37  import static org.modeshape.sequencer.ddl.StandardDdlLexicon.TYPE_UNKNOWN_STATEMENT;
38  import static org.modeshape.sequencer.ddl.dialect.oracle.OracleDdlLexicon.*;
39  import java.util.ArrayList;
40  import java.util.List;
41  import org.modeshape.common.text.ParsingException;
42  import org.modeshape.common.util.CheckArg;
43  import org.modeshape.graph.property.Name;
44  import org.modeshape.sequencer.ddl.DdlParserProblem;
45  import org.modeshape.sequencer.ddl.DdlSequencerI18n;
46  import org.modeshape.sequencer.ddl.DdlTokenStream;
47  import org.modeshape.sequencer.ddl.StandardDdlLexicon;
48  import org.modeshape.sequencer.ddl.StandardDdlParser;
49  import org.modeshape.sequencer.ddl.DdlTokenStream.DdlTokenizer;
50  import org.modeshape.sequencer.ddl.datatype.DataType;
51  import org.modeshape.sequencer.ddl.datatype.DataTypeParser;
52  import org.modeshape.sequencer.ddl.node.AstNode;
53  
54  /**
55   * Oracle-specific DDL Parser. Includes custom data types as well as custom DDL statements.
56   */
57  public class OracleDdlParser extends StandardDdlParser
58      implements OracleDdlConstants, OracleDdlConstants.OracleStatementStartPhrases {
59      private final String parserId = "ORACLE";
60  
61      static List<String[]> oracleDataTypeStrings = new ArrayList<String[]>();
62  
63      public OracleDdlParser() {
64          super();
65  
66          setDatatypeParser(new OracleDataTypeParser());
67          initialize();
68      }
69  
70      private void initialize() {
71          setTerminator(DEFAULT_TERMINATOR);
72  
73          setDoUseTerminator(true);
74  
75          oracleDataTypeStrings.addAll(OracleDataTypes.CUSTOM_DATATYPE_START_PHRASES); //
76      }
77  
78      /**
79       * {@inheritDoc}
80       * 
81       * @see org.modeshape.sequencer.ddl.StandardDdlParser#getId()
82       */
83      @Override
84      public String getId() {
85          return this.parserId;
86      }
87  
88      /**
89       * {@inheritDoc}
90       * 
91       * @see org.modeshape.sequencer.ddl.StandardDdlParser#getIdentifyingKeywords()
92       */
93      @Override
94      public String[] getIdentifyingKeywords() {
95          return new String[] {getId(), "spool.log"};
96      }
97  
98      /**
99       * {@inheritDoc}
100      * 
101      * @see org.modeshape.sequencer.ddl.StandardDdlParser#initializeTokenStream(org.modeshape.sequencer.ddl.DdlTokenStream)
102      */
103     @Override
104     protected void initializeTokenStream( DdlTokenStream tokens ) {
105         super.initializeTokenStream(tokens);
106         tokens.registerKeyWords(CUSTOM_KEYWORDS);
107         tokens.registerKeyWords(OracleDataTypes.CUSTOM_DATATYPE_START_WORDS);
108         tokens.registerStatementStartPhrase(ALTER_PHRASES);
109         tokens.registerStatementStartPhrase(CREATE_PHRASES);
110         tokens.registerStatementStartPhrase(DROP_PHRASES);
111         tokens.registerStatementStartPhrase(MISC_PHRASES);
112         tokens.registerStatementStartPhrase(SET_PHRASES);
113     }
114 
115     @Override
116     protected void rewrite( DdlTokenStream tokens,
117                             AstNode rootNode ) {
118         assert tokens != null;
119         assert rootNode != null;
120 
121         // We may have a prepare statement that is followed by a missing terminator node
122         // We also may have nodes that have an extra terminator node representing the '/' backslash
123         // These nodes will have type "TYPE_BACKSLASH_TERMINATOR".
124 
125         List<AstNode> copyOfNodes = new ArrayList<AstNode>(rootNode.getChildren());
126 
127         AstNode complexNode = null;
128 
129         for (AstNode child : copyOfNodes) {
130 
131             if ((complexNode != null && nodeFactory().hasMixinType(child, TYPE_UNKNOWN_STATEMENT))
132                 || (complexNode != null && nodeFactory().hasMixinType(child, TYPE_BACKSLASH_TERMINATOR))) {
133                 mergeNodes(tokens, complexNode, child);
134                 rootNode.removeChild(child);
135             } else {
136                 complexNode = null;
137             }
138 
139             if (nodeFactory().hasMixinType(child, TYPE_CREATE_FUNCTION_STATEMENT)
140                 || nodeFactory().hasMixinType(child, TYPE_CREATE_TRIGGER_STATEMENT)
141                 || nodeFactory().hasMixinType(child, TYPE_CREATE_LIBRARY_STATEMENT)
142                 || nodeFactory().hasMixinType(child, TYPE_CREATE_PACKAGE_STATEMENT)
143                 || nodeFactory().hasMixinType(child, TYPE_CREATE_PROCEDURE_STATEMENT)) {
144                 complexNode = child;
145             }
146         }
147 
148         // We also may have nodes that have an extra terminator node representing the '/' backslash
149         // These nodes will have type "TYPE_BACKSLASH_TERMINATOR".
150 
151         super.rewrite(tokens, rootNode); // Removes all extra "missing terminator" nodes
152 
153         // Now we need to walk the tree again looking for unknown nodes under the root
154         // and attach them to the previous node, assuming the node can contain multiple nested statements.
155         // CREATE FUNCTION is one of those types
156 
157         copyOfNodes = new ArrayList<AstNode>(rootNode.getChildren());
158         boolean foundComplexNode = false;
159         complexNode = null;
160         for (AstNode child : copyOfNodes) {
161             if (matchesComplexNode(child)) {
162                 foundComplexNode = true;
163                 complexNode = child;
164             } else if (foundComplexNode) {
165                 if (complexNode != null && nodeFactory().hasMixinType(child, TYPE_UNKNOWN_STATEMENT)) {
166                     mergeNodes(tokens, complexNode, child);
167                     rootNode.removeChild(child);
168                 } else {
169                     foundComplexNode = false;
170                     complexNode = null;
171                 }
172             }
173         }
174     }
175 
176     private boolean matchesComplexNode( AstNode node ) {
177         assert node != null;
178 
179         for (Name mixin : COMPLEX_STMT_TYPES) {
180             if (nodeFactory().hasMixinType(node, mixin)) {
181                 return true;
182             }
183         }
184 
185         return false;
186     }
187 
188     /**
189      * {@inheritDoc}
190      * 
191      * @see org.modeshape.sequencer.ddl.StandardDdlParser#handleUnknownToken(org.modeshape.sequencer.ddl.DdlTokenStream,
192      *      java.lang.String)
193      */
194     @Override
195     public AstNode handleUnknownToken( DdlTokenStream tokens,
196                                        String tokenValue ) throws ParsingException {
197         if (tokenValue.equals("/")) {
198             return nodeFactory().node("backslashTerminator", getRootNode(), TYPE_BACKSLASH_TERMINATOR);
199         }
200 
201         return null;
202     }
203 
204     /**
205      * {@inheritDoc} The CREATE SCHEMA statement can include CREATE TABLE, CREATE VIEW, and GRANT statements.
206      * 
207      * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCreateSchemaStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
208      *      org.modeshape.sequencer.ddl.node.AstNode)
209      */
210     @Override
211     protected AstNode parseCreateSchemaStatement( DdlTokenStream tokens,
212                                                   AstNode parentNode ) throws ParsingException {
213         assert tokens != null;
214         assert parentNode != null;
215 
216         return super.parseCreateSchemaStatement(tokens, parentNode);
217     }
218 
219     /**
220      * {@inheritDoc}
221      * 
222      * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCustomStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
223      *      org.modeshape.sequencer.ddl.node.AstNode)
224      */
225     @Override
226     protected AstNode parseCustomStatement( DdlTokenStream tokens,
227                                             AstNode parentNode ) throws ParsingException {
228         assert tokens != null;
229         assert parentNode != null;
230 
231         AstNode result = null;
232 
233         if (tokens.matches(STMT_COMMENT_ON)) {
234             result = parseCommentStatement(tokens, parentNode);
235         } else if (tokens.matches(STMT_ANALYZE)) {
236             return parseStatement(tokens, STMT_ANALYZE, parentNode, TYPE_ANALYZE_STATEMENT);
237         } else if (tokens.matches(STMT_ASSOCIATE_STATISTICS)) {
238             return parseStatement(tokens, STMT_ASSOCIATE_STATISTICS, parentNode, TYPE_ASSOCIATE_STATISTICS_STATEMENT);
239         } else if (tokens.matches(STMT_AUDIT)) {
240             return parseStatement(tokens, STMT_AUDIT, parentNode, TYPE_AUDIT_STATEMENT);
241         } else if (tokens.matches(STMT_COMMIT_FORCE) || tokens.matches(STMT_COMMIT_WORK) || tokens.matches(STMT_COMMIT_WRITE)) {
242             return parseStatement(tokens, STMT_COMMIT, parentNode, TYPE_COMMIT_STATEMENT);
243         } else if (tokens.matches(STMT_DISASSOCIATE_STATISTICS)) {
244             return parseStatement(tokens, STMT_DISASSOCIATE_STATISTICS, parentNode, TYPE_DISASSOCIATE_STATISTICS_STATEMENT);
245         } else if (tokens.matches(STMT_EXPLAIN_PLAN)) {
246             return parseStatement(tokens, STMT_EXPLAIN_PLAN, parentNode, TYPE_EXPLAIN_PLAN_STATEMENT);
247         } else if (tokens.matches(STMT_FLASHBACK)) {
248             return parseStatement(tokens, STMT_FLASHBACK, parentNode, TYPE_FLASHBACK_STATEMENT);
249         } else if (tokens.matches(STMT_LOCK_TABLE)) {
250             return parseStatement(tokens, STMT_LOCK_TABLE, parentNode, TYPE_LOCK_TABLE_STATEMENT);
251         } else if (tokens.matches(STMT_MERGE)) {
252             return parseStatement(tokens, STMT_MERGE, parentNode, TYPE_MERGE_STATEMENT);
253         } else if (tokens.matches(STMT_NOAUDIT)) {
254             return parseStatement(tokens, STMT_NOAUDIT, parentNode, TYPE_NOAUDIT_STATEMENT);
255         } else if (tokens.matches(STMT_PURGE)) {
256             return parseStatement(tokens, STMT_PURGE, parentNode, TYPE_PURGE_STATEMENT);
257         } else if (tokens.matches(STMT_RENAME)) {
258             return parseStatement(tokens, STMT_RENAME, parentNode, TYPE_RENAME_STATEMENT);
259         } else if (tokens.matches(STMT_ROLLBACK)) {
260             return parseStatement(tokens, STMT_ROLLBACK, parentNode, TYPE_ROLLBACK_STATEMENT);
261         } else if (tokens.matches(STMT_ROLLBACK_WORK)) {
262             return parseStatement(tokens, STMT_ROLLBACK_WORK, parentNode, TYPE_ROLLBACK_STATEMENT);
263         } else if (tokens.matches(STMT_ROLLBACK_TO_SAVEPOINT)) {
264             return parseStatement(tokens, STMT_ROLLBACK_TO_SAVEPOINT, parentNode, TYPE_ROLLBACK_STATEMENT);
265         } else if (tokens.matches(STMT_SAVEPOINT)) {
266             return parseStatement(tokens, STMT_SAVEPOINT, parentNode, TYPE_SAVEPOINT_STATEMENT);
267         } else if (tokens.matches(STMT_TRUNCATE)) {
268             return parseStatement(tokens, STMT_TRUNCATE, parentNode, TYPE_TRUNCATE_STATEMENT);
269         }
270 
271         if (result == null) {
272             result = super.parseCustomStatement(tokens, parentNode);
273         }
274         return result;
275     }
276 
277     @Override
278     protected AstNode parseCreateStatement( DdlTokenStream tokens,
279                                             AstNode parentNode ) throws ParsingException {
280         assert tokens != null;
281         assert parentNode != null;
282 
283         if (tokens.matches(STMT_CREATE_INDEX) || tokens.matches(STMT_CREATE_UNIQUE_INDEX)
284             || tokens.matches(STMT_CREATE_BITMAP_INDEX)) {
285             return parseCreateIndex(tokens, parentNode);
286         } else if (tokens.matches(STMT_CREATE_CLUSTER)) {
287             return parseCreateClusterStatement(tokens, parentNode);
288         } else if (tokens.matches(STMT_CREATE_CONTEXT)) {
289             return parseStatement(tokens, STMT_CREATE_CONTEXT, parentNode, TYPE_CREATE_CONTEXT_STATEMENT);
290         } else if (tokens.matches(STMT_CREATE_CONTROLFILE)) {
291             return parseStatement(tokens, STMT_CREATE_CONTROLFILE, parentNode, TYPE_CREATE_CONTROLFILE_STATEMENT);
292         } else if (tokens.matches(STMT_CREATE_DATABASE)) {
293             return parseStatement(tokens, STMT_CREATE_DATABASE, parentNode, TYPE_CREATE_DATABASE_STATEMENT);
294         } else if (tokens.matches(STMT_CREATE_PUBLIC_DATABASE)) {
295             return parseStatement(tokens, STMT_CREATE_PUBLIC_DATABASE, parentNode, TYPE_CREATE_DATABASE_STATEMENT);
296         } else if (tokens.matches(STMT_CREATE_DIMENSION)) {
297             return parseCreateDimensionStatement(tokens, parentNode);
298         } else if (tokens.matches(STMT_CREATE_DIRECTORY)) {
299             return parseStatement(tokens, STMT_CREATE_DIRECTORY, parentNode, TYPE_CREATE_DIRECTORY_STATEMENT);
300         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_DIRECTORY)) {
301             return parseStatement(tokens, STMT_CREATE_OR_REPLACE_DIRECTORY, parentNode, TYPE_CREATE_DIRECTORY_STATEMENT);
302         } else if (tokens.matches(STMT_CREATE_DISKGROUP)) {
303             return parseStatement(tokens, STMT_CREATE_DISKGROUP, parentNode, TYPE_CREATE_DISKGROUP_STATEMENT);
304         } else if (tokens.matches(STMT_CREATE_FUNCTION)) {
305             // ============ > PARSE UNTIL '/' for STMT_CREATE_FUNCTION
306             return parseCreateFunctionStatement(tokens, parentNode);
307         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_FUNCTION)) {
308             // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_FUNCTION
309             return parseCreateFunctionStatement(tokens, parentNode);
310         } else if (tokens.matches(STMT_CREATE_INDEXTYPE)) {
311             return parseStatement(tokens, STMT_CREATE_INDEXTYPE, parentNode, TYPE_CREATE_INDEXTYPE_STATEMENT);
312         } else if (tokens.matches(STMT_CREATE_JAVA)) {
313             return parseCreateJavaStatement(tokens, parentNode);
314         } else if (tokens.matches(STMT_CREATE_LIBRARY)) {
315             // ============ > PARSE UNTIL '/' for STMT_CREATE_LIBRARY
316             return parseStatement(tokens, STMT_CREATE_LIBRARY, parentNode, TYPE_CREATE_LIBRARY_STATEMENT);
317         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_LIBRARY)) {
318             // ============ > PARSE UNTIL '/' STMT_CREATE_OR_REPLACE_LIBRARY
319             return parseStatement(tokens, STMT_CREATE_OR_REPLACE_LIBRARY, parentNode, TYPE_CREATE_LIBRARY_STATEMENT);
320         } else if (tokens.matches(STMT_CREATE_MATERIALIZED_VIEW)) {
321             return parseMaterializedViewStatement(tokens, parentNode);
322         } else if (tokens.matches(STMT_CREATE_OPERATOR)) {
323             return parseStatement(tokens, STMT_CREATE_OPERATOR, parentNode, TYPE_CREATE_OPERATOR_STATEMENT);
324         } else if (tokens.matches(STMT_CREATE_OUTLINE)) {
325             return parseStatement(tokens, STMT_CREATE_OUTLINE, parentNode, TYPE_CREATE_OUTLINE_STATEMENT);
326         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_OUTLINE)) {
327             return parseStatement(tokens, STMT_CREATE_OR_REPLACE_OUTLINE, parentNode, TYPE_CREATE_OUTLINE_STATEMENT);
328         } else if (tokens.matches(STMT_CREATE_PACKAGE)) {
329             // ============ > PARSE UNTIL '/' for STMT_CREATE_PACKAGE
330             return parseStatement(tokens, STMT_CREATE_PACKAGE, parentNode, TYPE_CREATE_PACKAGE_STATEMENT);
331         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_PACKAGE)) {
332             // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_PACKAGE
333             return parseStatement(tokens, STMT_CREATE_OR_REPLACE_PACKAGE, parentNode, TYPE_CREATE_PACKAGE_STATEMENT);
334         } else if (tokens.matches(STMT_CREATE_PFILE)) {
335             return parseStatement(tokens, STMT_CREATE_PFILE, parentNode, TYPE_CREATE_PFILE_STATEMENT);
336         } else if (tokens.matches(STMT_CREATE_PROCEDURE)) {
337             // ============ > PARSE UNTIL '/' for STMT_CREATE_PROCEDURE
338             return parseCreateProcedureStatement(tokens, parentNode);
339         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_PROCEDURE)) {
340             // ============ > PARSE UNTIL '/' for STMT_CREATE_PROCEDURE
341             return parseCreateProcedureStatement(tokens, parentNode);
342         } else if (tokens.matches(STMT_CREATE_PROFILE)) {
343             return parseStatement(tokens, STMT_CREATE_PROFILE, parentNode, TYPE_CREATE_PROFILE_STATEMENT);
344         } else if (tokens.matches(STMT_CREATE_ROLE)) {
345             return parseStatement(tokens, STMT_CREATE_ROLE, parentNode, TYPE_CREATE_ROLE_STATEMENT);
346         } else if (tokens.matches(STMT_CREATE_ROLLBACK)) {
347             return parseStatement(tokens, STMT_CREATE_ROLLBACK, parentNode, TYPE_CREATE_ROLLBACK_STATEMENT);
348         } else if (tokens.matches(STMT_CREATE_PUBLIC_ROLLBACK)) {
349             return parseStatement(tokens, STMT_CREATE_PUBLIC_ROLLBACK, parentNode, TYPE_CREATE_ROLLBACK_STATEMENT);
350         } else if (tokens.matches(STMT_CREATE_SEQUENCE)) {
351             return parseStatement(tokens, STMT_CREATE_SEQUENCE, parentNode, TYPE_CREATE_SEQUENCE_STATEMENT);
352         } else if (tokens.matches(STMT_CREATE_SPFILE)) {
353             return parseStatement(tokens, STMT_CREATE_SPFILE, parentNode, TYPE_CREATE_SPFILE_STATEMENT);
354         } else if (tokens.matches(STMT_CREATE_SYNONYM)) {
355             return parseStatement(tokens, STMT_CREATE_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT);
356         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_SYNONYM)) {
357             return parseStatement(tokens, STMT_CREATE_OR_REPLACE_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT);
358         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_PUBLIC_SYNONYM)) {
359             return parseStatement(tokens, STMT_CREATE_OR_REPLACE_PUBLIC_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT);
360         } else if (tokens.matches(STMT_CREATE_PUBLIC_SYNONYM)) {
361             return parseStatement(tokens, STMT_CREATE_PUBLIC_SYNONYM, parentNode, TYPE_CREATE_SYNONYM_STATEMENT);
362         } else if (tokens.matches(STMT_CREATE_TABLESPACE)) {
363             return parseStatement(tokens, STMT_CREATE_TABLESPACE, parentNode, TYPE_CREATE_TABLESPACE_STATEMENT);
364         } else if (tokens.matches(STMT_CREATE_TRIGGER)) {
365             // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_TRIGGER
366             return parseSlashedStatement(tokens, STMT_CREATE_TRIGGER, parentNode, TYPE_CREATE_TRIGGER_STATEMENT);
367         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_TRIGGER)) {
368             // ============ > PARSE UNTIL '/' for STMT_CREATE_OR_REPLACE_TRIGGER
369             return parseSlashedStatement(tokens, STMT_CREATE_OR_REPLACE_TRIGGER, parentNode, TYPE_CREATE_TRIGGER_STATEMENT);
370         } else if (tokens.matches(STMT_CREATE_TYPE)) {
371             return parseStatement(tokens, STMT_CREATE_TYPE, parentNode, TYPE_CREATE_TYPE_STATEMENT);
372         } else if (tokens.matches(STMT_CREATE_OR_REPLACE_TYPE)) {
373             return parseStatement(tokens, STMT_CREATE_OR_REPLACE_TYPE, parentNode, TYPE_CREATE_TYPE_STATEMENT);
374         } else if (tokens.matches(STMT_CREATE_USER)) {
375             return parseStatement(tokens, STMT_CREATE_USER, parentNode, TYPE_CREATE_USER_STATEMENT);
376         }
377 
378         return super.parseCreateStatement(tokens, parentNode);
379     }
380 
381     private AstNode parseCreateClusterStatement( DdlTokenStream tokens,
382                                                  AstNode parentNode ) throws ParsingException {
383         markStartOfStatement(tokens);
384         tokens.consume(STMT_CREATE_CLUSTER);
385         String name = parseName(tokens);
386 
387         AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_CLUSTER_STATEMENT);
388 
389         parseUntilTerminator(tokens);
390 
391         markEndOfStatement(tokens, node);
392 
393         return node;
394     }
395 
396     private AstNode parseCreateDimensionStatement( DdlTokenStream tokens,
397                                                    AstNode parentNode ) throws ParsingException {
398         markStartOfStatement(tokens);
399         tokens.consume(STMT_CREATE_DIMENSION);
400         String name = parseName(tokens);
401 
402         AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_DIMENSION_STATEMENT);
403 
404         parseUntilTerminator(tokens);
405 
406         markEndOfStatement(tokens, node);
407 
408         return node;
409     }
410 
411     /**
412      * Parses DDL CREATE FUNCTION statement
413      * 
414      * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null
415      * @param parentNode the parent {@link AstNode} node; may not be null
416      * @return the parsed CREATE FUNCTION statement node
417      * @throws ParsingException
418      */
419     protected AstNode parseCreateFunctionStatement( DdlTokenStream tokens,
420                                                     AstNode parentNode ) throws ParsingException {
421         assert tokens != null;
422         assert parentNode != null;
423 
424         markStartOfStatement(tokens);
425 
426         /* ----------------------------------------------------------------------
427             CREATE [ OR REPLACE ] FUNCTION [ schema. ] function_name
428               [ ( parameter_declaration [, parameter_declaration] ) 
429               ]
430               RETURN datatype
431               [ { invoker_rights_clause
432                 | DETERMINISTIC
433                 | parallel_enable_clause
434                 | result_cache_clause
435                 }...
436               ]
437               { { AGGREGATE | PIPELINED }
438                 USING [ schema. ] implementation_type
439               | [ PIPELINED ] { IS | AS } { [ declare_section ] body | call_spec }
440               } ;
441             
442             parameter_declaration = parameter_name [ IN | { { OUT | { IN OUT }} [ NOCOPY ] } ] datatype [ { := | DEFAULT } expression ]
443         ---------------------------------------------------------------------- */
444 
445         boolean isReplace = tokens.canConsume(STMT_CREATE_OR_REPLACE_FUNCTION);
446 
447         tokens.canConsume(STMT_CREATE_FUNCTION);
448 
449         String name = parseName(tokens);
450 
451         AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_FUNCTION_STATEMENT);
452 
453         if (isReplace) {
454             // TODO: SET isReplace = TRUE to node (possibly a cnd mixin of "replaceable"
455         }
456 
457         boolean ok = parseParameters(tokens, node);
458 
459         if (ok) {
460             if (tokens.canConsume("RETURN")) {
461                 DataType dType = getDatatypeParser().parse(tokens);
462                 if (dType != null) {
463                     getDatatypeParser().setPropertiesOnNode(node, dType);
464                 }
465             }
466         }
467 
468         parseUntilFwdSlash(tokens, false);
469 
470         tokens.canConsume("/");
471 
472         markEndOfStatement(tokens, node);
473 
474         return node;
475     }
476 
477     /**
478      * Parses DDL CREATE PROCEDURE statement
479      * 
480      * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null
481      * @param parentNode the parent {@link AstNode} node; may not be null
482      * @return the parsed CREATE PROCEDURE statement node
483      * @throws ParsingException
484      */
485     protected AstNode parseCreateProcedureStatement( DdlTokenStream tokens,
486                                                      AstNode parentNode ) throws ParsingException {
487         assert tokens != null;
488         assert parentNode != null;
489 
490         markStartOfStatement(tokens);
491         /* ----------------------------------------------------------------------
492             CREATE [ OR REPLACE ] PROCEDURE [ schema. ] procedure_name
493                 [ ( parameter_declaration [, parameter_declaration ] ) ]
494                 [ AUTHID { CURRENT_USER | DEFINER  ]
495                 { IS | AS }
496                 { [ declare_section ] body | call_spec | EXTERNAL} ;
497             
498              call_spec = LANGUAGE { Java_declaration | C_declaration }
499             
500              Java_declaration = JAVA NAME string
501             
502              C_declaration = 
503                 C [ NAME name ]
504                     LIBRARY lib_name
505                     [ AGENT IN (argument[, argument ]...) ]
506                     [ WITH CONTEXT ]
507                     [ PARAMETERS (parameter[, parameter ]...) ]
508                     
509             parameter_declaration = parameter_name [ IN | { { OUT | { IN OUT }} [ NOCOPY ] } ] datatype [ { := | DEFAULT } expression ]
510         ---------------------------------------------------------------------- */
511 
512         boolean isReplace = tokens.canConsume(STMT_CREATE_OR_REPLACE_PROCEDURE);
513 
514         tokens.canConsume(STMT_CREATE_PROCEDURE);
515 
516         String name = parseName(tokens);
517 
518         AstNode node = nodeFactory().node(name, parentNode, TYPE_CREATE_PROCEDURE_STATEMENT);
519 
520         if (isReplace) {
521             // TODO: SET isReplace = TRUE to node (possibly a cnd mixin of "replaceable"
522         }
523 
524         boolean ok = parseParameters(tokens, node);
525 
526         if (ok) {
527             if (tokens.canConsume("AUTHID")) {
528                 if (tokens.canConsume("CURRENT_USER")) {
529                     node.setProperty(AUTHID_VALUE, "AUTHID CURRENT_USER");
530                 } else {
531                     tokens.consume("DEFINER");
532                     node.setProperty(AUTHID_VALUE, "DEFINER");
533                 }
534             }
535         }
536 
537         parseUntilFwdSlash(tokens, false);
538 
539         tokens.canConsume("/");
540 
541         markEndOfStatement(tokens, node);
542 
543         return node;
544     }
545 
546     private boolean parseParameters( DdlTokenStream tokens,
547                                      AstNode procedureNode ) throws ParsingException {
548         assert tokens != null;
549         assert procedureNode != null;
550 
551         // parameter_declaration = parameter_name [ IN | { { OUT | { IN OUT }} [ NOCOPY ] } ] datatype [ { := | DEFAULT }
552         // expression ]
553         // Assume we start with open parenthesis '(', then we parse comma separated list of function parameters
554         // which have the form: [ parameter-Name ] DataType
555         // So, try getting datatype, if datatype == NULL, then parseName() & parse datatype, then repeat as long as next token is
556         // ","
557 
558         tokens.consume(L_PAREN); // EXPECTED
559 
560         while (!tokens.canConsume(R_PAREN)) {
561 
562             String paramName = parseName(tokens);
563             String inOutStr = null;
564             if (tokens.matches("IN")) {
565                 if (tokens.canConsume("IN", "OUT")) {
566                     if (tokens.canConsume("NOCOPY")) {
567                         inOutStr = "IN OUT NOCOPY";
568                     } else {
569                         inOutStr = "IN OUT";
570                     }
571                 } else {
572                     tokens.consume("IN");
573                     inOutStr = "IN";
574                 }
575 
576             } else if (tokens.matches("OUT")) {
577                 if (tokens.canConsume("OUT", "NOCOPY")) {
578                     inOutStr = "OUT NOCOPY";
579                 } else {
580                     tokens.consume("OUT");
581                     inOutStr = "OUT";
582                 }
583             }
584 
585             DataType datatype = getDatatypeParser().parse(tokens);
586             AstNode paramNode = nodeFactory().node(paramName, procedureNode, TYPE_FUNCTION_PARAMETER);
587             if (datatype != null) {
588                 getDatatypeParser().setPropertiesOnNode(paramNode, datatype);
589             }
590 
591             if (tokens.matchesAnyOf(":=", "DEFAULT") || !tokens.matchesAnyOf(COMMA, R_PAREN)) {
592                 String msg = DdlSequencerI18n.unsupportedProcedureParameterDeclaration.text(procedureNode.getProperty(StandardDdlLexicon.NAME));
593                 DdlParserProblem problem = new DdlParserProblem(Problems.WARNING, getCurrentMarkedPosition(), msg);
594                 addProblem(problem, procedureNode);
595                 return false;
596             }
597 
598             if (inOutStr != null) {
599                 paramNode.setProperty(IN_OUT_NO_COPY, inOutStr);
600             }
601 
602             tokens.canConsume(COMMA);
603         }
604 
605         return true;
606     }
607 
608     /**
609      * Parses DDL CREATE MATERIALIZED VIEW statement This could either be a standard view or a VIEW LOG ON statement.
610      * 
611      * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null
612      * @param parentNode the parent {@link AstNode} node; may not be null
613      * @return the parsed CREATE MATERIALIZED VIEW statement node
614      * @throws ParsingException
615      */
616     protected AstNode parseMaterializedViewStatement( DdlTokenStream tokens,
617                                                       AstNode parentNode ) throws ParsingException {
618         assert tokens != null;
619         assert parentNode != null;
620 
621         markStartOfStatement(tokens);
622 
623         /* ----------------------------------------------------------------------
624             CREATE MATERIALIZED VIEW
625               [ schema. ]materialized_view
626               [ column_alias [, column_alias]... ]
627               [ OF [ schema. ]object_type ] .................... (MORE...)
628 
629             EXAMPLES:
630             
631                 CREATE MATERIALIZED VIEW LOG ON products
632                    WITH ROWID, SEQUENCE (prod_id)
633                    INCLUDING NEW VALUES;
634                 
635                 CREATE MATERIALIZED VIEW sales_mv
636                    BUILD IMMEDIATE
637                    REFRESH FAST ON COMMIT
638                    AS SELECT t.calendar_year, p.prod_id, 
639                       SUM(s.amount_sold) AS sum_sales
640                       FROM times t, products p, sales s
641                       WHERE t.time_id = s.time_id AND p.prod_id = s.prod_id
642                       GROUP BY t.calendar_year, p.prod_id;
643         ---------------------------------------------------------------------- */
644 
645         boolean isLog = tokens.canConsume(STMT_CREATE_MATERIALIZED_VEIW_LOG);
646 
647         tokens.canConsume(STMT_CREATE_MATERIALIZED_VIEW);
648 
649         String name = parseName(tokens);
650 
651         AstNode node = null;
652 
653         if (isLog) {
654             node = nodeFactory().node(name, parentNode, TYPE_CREATE_MATERIALIZED_VIEW_LOG_STATEMENT);
655         } else {
656             node = nodeFactory().node(name, parentNode, TYPE_CREATE_MATERIALIZED_VIEW_STATEMENT);
657         }
658 
659         parseUntilTerminator(tokens);
660 
661         markEndOfStatement(tokens, node);
662 
663         return node;
664     }
665 
666     @Override
667     protected AstNode parseGrantStatement( DdlTokenStream tokens,
668                                            AstNode parentNode ) throws ParsingException {
669         CheckArg.isNotNull(tokens, "tokens");
670         CheckArg.isNotNull(parentNode, "parentNode");
671 
672         // GRANT { grant_system_privileges | grant_object_privileges } ;
673         //
674         // ** grant_system_privileges **
675         //        
676         // { system_privilege | role | ALL PRIVILEGES } [, { system_privilege | role | ALL PRIVILEGES } ]...
677         // TO grantee_clause [ WITH ADMIN OPTION ]
678         //
679         // ** grant_object_privileges **
680         //
681         // { object_privilege | ALL [ PRIVILEGES ] } [ (column [, column ]...) ] [, { object_privilege | ALL [ PRIVILEGES ] } [
682         // (column [, column ]...) ] ]...
683         // on_object_clause
684         // TO grantee_clause [ WITH HIERARCHY OPTION ] [ WITH GRANT OPTION ]
685 
686         // ** on_object_clause **
687         //
688         // { [ schema. ] object | { DIRECTORY directory_name | JAVA { SOURCE | RESOURCE } [ schema. ] object } }
689         //
690         // ** grantee_clause **
691         //
692         // { user [ IDENTIFIED BY password ] | role | PUBLIC } [, { user [ IDENTIFIED BY password ] | role | PUBLIC } ]...
693 
694         AstNode node = null;
695 
696         // Original implementation does NOT parse Insert statement, but just returns a generic TypedStatement
697         markStartOfStatement(tokens);
698 
699         tokens.consume(GRANT);
700         String name = GRANT;
701 
702         tokens.consume(); // First Privilege token
703 
704         node = nodeFactory().node(name, parentNode, TYPE_GRANT_STATEMENT);
705 
706         while (tokens.hasNext()
707                && !isTerminator(tokens)
708                && (!tokens.matches(DdlTokenizer.STATEMENT_KEY) || (tokens.matches(DdlTokenizer.STATEMENT_KEY) && tokens.matches("GRANT",
709                                                                                                                                 "OPTION")))) {
710             tokens.consume();
711         }
712 
713         markEndOfStatement(tokens, node);
714 
715         return node;
716 
717         // if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "TO")) {
718         // markStartOfStatement(tokens);
719         // tokens.consume(GRANT);
720         // String privilege = tokens.consume();
721         // tokens.consume("TO");
722         // tokens.consume(); // TO Value
723         // && problem != null
724         // String value = parseUntilTerminator(tokens);
725         //			
726         // AstNode grantNode = nodeFactory().node("GRANT", parentNode, TYPE_GRANT_STATEMENT);
727         // markEndOfStatement(tokens, grantNode);
728         //			
729         // return grantNode;
730         // } else if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, COMMA)) {
731         // markStartOfStatement(tokens);
732         // tokens.consume(GRANT);
733         // String privilege = tokens.consume();
734         // // Assume Multiple Privileges
735         // while( tokens.canConsume(COMMA)) {
736         // tokens.consume(); // Next privilege
737         // }
738         //			
739         // tokens.consume("ON");
740         // tokens.consume(); // TO Value
741         //			
742         // tokens.canConsume("WITH", GRANT);
743         //			
744         // String value = parseUntilTerminator(tokens);
745         //			
746         // AstNode grantNode = nodeFactory().node("GRANT", parentNode, TYPE_GRANT_STATEMENT);
747         // markEndOfStatement(tokens, grantNode);
748         //			
749         // return grantNode;
750         // } else if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "ON", DdlTokenStream.ANY_VALUE, "TO",
751         // DdlTokenStream.ANY_VALUE, "WITH", GRANT)) {
752         // markStartOfStatement(tokens);
753         // //GRANT ALL ON bonuses TO hr WITH GRANT OPTION;
754         //			
755         // tokens.consume(GRANT);
756         // String privilege = tokens.consume();
757         // tokens.consume("ON");
758         // tokens.consume(); // ON Value
759         // tokens.consume("TO");
760         // tokens.consume();
761         // tokens.consume("WITH", GRANT);
762         // String value = parseUntilTerminator(tokens);
763         //
764         // AstNode grantNode = nodeFactory().node("GRANT", parentNode, TYPE_GRANT_STATEMENT);
765         // markEndOfStatement(tokens, grantNode);
766         //			
767         // return grantNode;
768         // }
769         // else if( tokens.matches(GRANT, DdlTokenStream.ANY_VALUE, "ON")) {
770         // tokens.consume(GRANT);
771         // String privilege = tokens.consume();
772         // tokens.consume("ON");
773         // tokens.consume(); // ON Value
774         //			
775         // String value = parseUntilTerminator(tokens);
776         // stmt.appendSource(true, value);&& problem != null
777         // stmt.setType("GRANT" + SPACE + privilege + SPACE + "ON");
778         // consumeTerminator(tokens);
779         // } else if( tokens.matches(GRANT, CREATE) ||
780         // tokens.matches(GRANT, ALTER) ||
781         // tokens.matches(GRANT, DROP) ||
782         // tokens.matches(GRANT, "EXECUTE") ||
783         // tokens.matches(GRANT, "MANAGE") ||
784         // tokens.matches(GRANT, "QUERY") ||
785         // tokens.matches(GRANT, "ON", "COMMIT") ||
786         // tokens.matches(GRANT, "ANY") ||
787         // tokens.matches(GRANT, "SELECT") ||
788         // tokens.matches(GRANT, "RESTRICTED") ||
789         // //
790         // ========================================================================================================================
791         // ===
792         // //
793         // ========================================================================================================================
794         // ===
795         // tokens.matches(GRANT, "FLASHBACK") ||
796         // tokens.matches(GRANT, "GLOBAL") ||
797         // tokens.matches(GRANT, "DEBUG") ||
798         // tokens.matches(GRANT, "GLOBAL") ||
799         // tokens.matches(GRANT, "ADVISOR") ||
800         // tokens.matches(GRANT, "ADMINISTER") ||
801         // tokens.matches(GRANT, "BACKUP") ||
802         // tokens.matches(GRANT, "LOCK") ||
803         // tokens.matches(GRANT, "UPDATE") ||
804         // tokens.matches(GRANT, "DELETE") ||
805         // tokens.matches(GRANT, "INSERT") ||
806         // tokens.matches(GRANT, "UNLIMITED") ||
807         // tokens.matches(GRANT, "UNDER") ||
808         // tokens.matches(GRANT, "ANALYZE") ||
809         // tokens.matches(GRANT, "AUDIT") ||
810         // tokens.matches(GRANT, "COMMENT") ||
811         // tokens.matches(GRANT, "EXEMPT") ||
812         // tokens.matches(GRANT, "FORCE") ||
813         // tokens.matches(GRANT, "RESUMABLE") ||
814         // tokens.matches(GRANT, "SYSDBA") ||
815         // tokens.matches(GRANT, "REFERENCES") ||
816         // tokens.matches(GRANT, "SYSOPER") ||
817         // tokens.matches(GRANT, "WRITE") ) {
818         // tokens.consume(GRANT);
819         //			
820         // String nextTok = tokens.consume() + SPACE + tokens.consume() + SPACE + tokens.consume();
821         //
822         // String value = parseUntilTerminator(tokens);
823         // stmt.setType("GRANT" + SPACE + nextTok);
824         // consumeTerminator(tokens);
825         // }
826         //		
827         //		
828         // return grantNode;
829 
830         // return super.parseGrantStatement(tokens, parentNode);
831     }
832 
833     /**
834      * {@inheritDoc}
835      * 
836      * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseRevokeStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
837      *      org.modeshape.sequencer.ddl.node.AstNode)
838      */
839     @Override
840     protected AstNode parseRevokeStatement( DdlTokenStream tokens,
841                                             AstNode parentNode ) throws ParsingException {
842         return parseStatement(tokens, STMT_REVOKE, parentNode, TYPE_REVOKE_STATEMENT);
843     }
844 
845     @Override
846     protected AstNode parseAlterTableStatement( DdlTokenStream tokens,
847                                                 AstNode parentNode ) throws ParsingException {
848         assert tokens != null;
849         assert parentNode != null;
850 
851         if (tokens.matches("ALTER", "TABLE", DdlTokenStream.ANY_VALUE, "ADD")) {
852 
853             // ALTER TABLE
854             // ADD ( {column_definition | virtual_column_definition
855             // [, column_definition | virtual_column_definition] ... } [ column_properties ]
856 
857             markStartOfStatement(tokens);
858 
859             tokens.consume(ALTER, TABLE);
860 
861             String tableName = parseName(tokens);
862 
863             AstNode alterTableNode = nodeFactory().node(tableName, parentNode, TYPE_ALTER_TABLE_STATEMENT);
864 
865             tokens.consume("ADD");
866 
867             // System.out.println("  >> PARSING ALTER STATEMENT >>  TABLE Name = " + tableName);
868 
869             if (isTableConstraint(tokens)) {
870                 parseTableConstraint(tokens, alterTableNode, true);
871             } else {
872                 // This segment can also be enclosed in "()" brackets to handle multiple ColumnDefinition ADDs
873                 if (tokens.matches(L_PAREN, "REF")) {
874                     // ALTER TABLE staff ADD (REF(dept) WITH ROWID);
875                     tokens.consume(L_PAREN, "REF", L_PAREN);
876                     parseName(tokens);
877                     tokens.consume(R_PAREN, "WITH", "ROWID", R_PAREN);
878 
879                 } else if (tokens.matches(L_PAREN, "SCOPE")) {
880                     // ALTER TABLE staff ADD (SCOPE FOR (dept) IS offices);
881                     tokens.consume(L_PAREN, "SCOPE", "FOR", L_PAREN);
882                     parseName(tokens);
883                     tokens.consume(R_PAREN, "IS");
884                     parseName(tokens);
885                     tokens.consume(R_PAREN);
886                 } else if (tokens.matches(L_PAREN)) {
887                     parseColumns(tokens, alterTableNode, true);
888                 } else {
889                     // Assume single ADD COLUMN
890                     parseSingleTerminatedColumnDefinition(tokens, alterTableNode, true);
891                 }
892             }
893 
894             parseUntilTerminator(tokens); // COULD BE "NESTED TABLE xxxxxxxx" option clause
895 
896             markEndOfStatement(tokens, alterTableNode);
897 
898             return alterTableNode;
899         } else if (tokens.matches("ALTER", "TABLE", DdlTokenStream.ANY_VALUE, "DROP")) {
900             markStartOfStatement(tokens);
901 
902             tokens.consume(ALTER, TABLE);
903 
904             String tableName = parseName(tokens);
905 
906             AstNode alterTableNode = nodeFactory().node(tableName, parentNode, TYPE_ALTER_TABLE_STATEMENT);
907 
908             tokens.consume(DROP);
909 
910             if (tokens.canConsume("CONSTRAINT")) {
911                 String constraintName = parseName(tokens); // constraint name
912 
913                 AstNode constraintNode = nodeFactory().node(constraintName, alterTableNode, TYPE_DROP_TABLE_CONSTRAINT_DEFINITION);
914 
915                 if (tokens.canConsume(DropBehavior.CASCADE)) {
916                     constraintNode.setProperty(DROP_BEHAVIOR, DropBehavior.CASCADE);
917                 } else if (tokens.canConsume(DropBehavior.RESTRICT)) {
918                     constraintNode.setProperty(DROP_BEHAVIOR, DropBehavior.RESTRICT);
919                 }
920             } else if (tokens.canConsume("COLUMN")) {
921                 // ALTER TABLE supplier
922                 // DROP COLUMN supplier_name;
923 
924                 String columnName = parseName(tokens);
925 
926                 AstNode columnNode = nodeFactory().node(columnName, alterTableNode, TYPE_DROP_COLUMN_DEFINITION);
927 
928                 if (tokens.canConsume(DropBehavior.CASCADE)) {
929                     columnNode.setProperty(DROP_BEHAVIOR, DropBehavior.CASCADE);
930                 } else if (tokens.canConsume(DropBehavior.RESTRICT)) {
931                     columnNode.setProperty(DROP_BEHAVIOR, DropBehavior.RESTRICT);
932                 }
933             } else {
934                 parseUntilTerminator(tokens); // EXAMPLE: "DROP UNIQUE (email)", or "DROP (col_1, col_2)"
935             }
936 
937             markEndOfStatement(tokens, alterTableNode);
938 
939             return alterTableNode;
940         } else if (tokens.matches("ALTER", "TABLE", DdlTokenStream.ANY_VALUE, "RENAME")) {
941 
942             // ALTER TABLE customers RENAME TO my_customers;
943             // ALTER TABLE customers RENAME CONSTRAINT cust_fname_nn TO cust_firstname_nn;
944             markStartOfStatement(tokens);
945 
946             tokens.consume(ALTER, TABLE);
947 
948             String oldName = parseName(tokens);
949             AstNode alterTableNode = nodeFactory().node(oldName, parentNode, TYPE_ALTER_TABLE_STATEMENT);
950 
951             if (tokens.canConsume("RENAME", "TO")) {
952                 String newName = parseName(tokens);
953                 alterTableNode.setProperty(NEW_NAME, newName);
954 
955                 parseUntilTerminator(tokens);
956 
957             } else if (tokens.canConsume("RENAME", "COLUMN")) {
958                 String oldColumnName = parseName(tokens);
959                 tokens.consume("TO");
960                 String newColumnName = parseName(tokens);
961 
962                 parseUntilTerminator(tokens);
963 
964                 AstNode renameColumnNode = nodeFactory().node(oldColumnName, alterTableNode, TYPE_RENAME_COLUMN);
965                 renameColumnNode.setProperty(NEW_NAME, newColumnName);
966 
967             } else if (tokens.canConsume("RENAME", "CONSTRAINT")) {
968                 String oldConstraintName = parseName(tokens);
969                 tokens.consume("TO");
970                 String newConstraintName = parseName(tokens);
971 
972                 parseUntilTerminator(tokens);
973 
974                 AstNode renameColumnNode = nodeFactory().node(oldConstraintName, alterTableNode, TYPE_RENAME_CONSTRAINT);
975                 renameColumnNode.setProperty(NEW_NAME, newConstraintName);
976             }
977 
978             markEndOfStatement(tokens, alterTableNode);
979 
980             return alterTableNode;
981         } else if (tokens.matches("ALTER", "TABLE", DdlTokenStream.ANY_VALUE, "MODIFY")) {
982 
983         }
984 
985         return super.parseAlterTableStatement(tokens, parentNode);
986     }
987 
988     @Override
989     protected AstNode parseAlterStatement( DdlTokenStream tokens,
990                                            AstNode parentNode ) throws ParsingException {
991         assert tokens != null;
992         assert parentNode != null;
993 
994         if (tokens.matches(ALTER, TABLE)) {
995             return parseAlterTableStatement(tokens, parentNode);
996         } else if (tokens.matches(STMT_ALTER_CLUSTER)) {
997             return parseStatement(tokens, STMT_ALTER_CLUSTER, parentNode, TYPE_ALTER_CLUSTER_STATEMENT);
998         } else if (tokens.matches(STMT_ALTER_DATABASE)) {
999             // could encounter: ALTER DATABASE RENAME FILE 'diskc:log3.log' TO 'diskb:log3.log';
1000             // So need to parse up past the RENAME check
1001             markStartOfStatement(tokens);
1002             tokens.consume(STMT_ALTER_DATABASE);
1003             AstNode result = nodeFactory().node("database", parentNode, TYPE_ALTER_DATABASE_STATEMENT);
1004             tokens.canConsume("RENAME");
1005             parseUntilTerminator(tokens);
1006             markEndOfStatement(tokens, result);
1007             return result;
1008         } else if (tokens.matches(STMT_ALTER_DIMENSION)) {
1009             return parseStatement(tokens, STMT_ALTER_DIMENSION, parentNode, TYPE_ALTER_DIMENSION_STATEMENT);
1010         } else if (tokens.matches(STMT_ALTER_DISKGROUP)) {
1011             return parseStatement(tokens, STMT_ALTER_DISKGROUP, parentNode, TYPE_ALTER_DISKGROUP_STATEMENT);
1012         } else if (tokens.matches(STMT_ALTER_FUNCTION)) {
1013             return parseStatement(tokens, STMT_ALTER_FUNCTION, parentNode, TYPE_ALTER_FUNCTION_STATEMENT);
1014         } else if (tokens.matches(STMT_ALTER_INDEX)) {
1015             // could encounter: ALTER INDEX upper_ix RENAME TO xxxxxx
1016             // So need to parse up past the RENAME check
1017             markStartOfStatement(tokens);
1018             tokens.consume(ALTER, INDEX);
1019             String indexName = parseName(tokens);
1020             AstNode result = nodeFactory().node(indexName, parentNode, TYPE_ALTER_INDEX_STATEMENT);
1021             tokens.canConsume("RENAME");
1022             parseUntilTerminator(tokens);
1023             markEndOfStatement(tokens, result);
1024             return result;
1025         } else if (tokens.matches(STMT_ALTER_INDEXTYPE)) {
1026             return parseStatement(tokens, STMT_ALTER_INDEXTYPE, parentNode, TYPE_ALTER_INDEXTYPE_STATEMENT);
1027         } else if (tokens.matches(STMT_ALTER_JAVA)) {
1028             return parseStatement(tokens, STMT_ALTER_JAVA, parentNode, TYPE_ALTER_JAVA_STATEMENT);
1029         } else if (tokens.matches(STMT_ALTER_MATERIALIZED)) {
1030             return parseStatement(tokens, STMT_ALTER_MATERIALIZED, parentNode, TYPE_ALTER_MATERIALIZED_STATEMENT);
1031         } else if (tokens.matches(STMT_ALTER_OPERATOR)) {
1032             return parseStatement(tokens, STMT_ALTER_OPERATOR, parentNode, TYPE_ALTER_OPERATOR_STATEMENT);
1033         } else if (tokens.matches(STMT_ALTER_OUTLINE)) {
1034             return parseStatement(tokens, STMT_ALTER_OUTLINE, parentNode, TYPE_ALTER_OUTLINE_STATEMENT);
1035         } else if (tokens.matches(STMT_ALTER_PACKAGE)) {
1036             return parseStatement(tokens, STMT_ALTER_PACKAGE, parentNode, TYPE_ALTER_PACKAGE_STATEMENT);
1037         } else if (tokens.matches(STMT_ALTER_PROCEDURE)) {
1038             return parseStatement(tokens, STMT_ALTER_PROCEDURE, parentNode, TYPE_ALTER_PROCEDURE_STATEMENT);
1039         } else if (tokens.matches(STMT_ALTER_PROFILE)) {
1040             return parseStatement(tokens, STMT_ALTER_PROFILE, parentNode, TYPE_ALTER_PROFILE_STATEMENT);
1041         } else if (tokens.matches(STMT_ALTER_RESOURCE)) {
1042             return parseStatement(tokens, STMT_ALTER_RESOURCE, parentNode, TYPE_ALTER_RESOURCE_STATEMENT);
1043         } else if (tokens.matches(STMT_ALTER_ROLE)) {
1044             return parseStatement(tokens, STMT_ALTER_ROLE, parentNode, TYPE_ALTER_ROLE_STATEMENT);
1045         } else if (tokens.matches(STMT_ALTER_ROLLBACK)) {
1046             return parseStatement(tokens, STMT_ALTER_ROLLBACK, parentNode, TYPE_ALTER_ROLLBACK_STATEMENT);
1047         } else if (tokens.matches(STMT_ALTER_SEQUENCE)) {
1048             return parseStatement(tokens, STMT_ALTER_SEQUENCE, parentNode, TYPE_ALTER_SEQUENCE_STATEMENT);
1049         } else if (tokens.matches(STMT_ALTER_SESSION)) {
1050             return parseStatement(tokens, STMT_ALTER_SESSION, parentNode, TYPE_ALTER_SESSION_STATEMENT);
1051         } else if (tokens.matches(STMT_ALTER_SYSTEM)) {
1052             return parseStatement(tokens, STMT_ALTER_SYSTEM, parentNode, TYPE_ALTER_SYSTEM_STATEMENT);
1053         } else if (tokens.matches(STMT_ALTER_TABLESPACE)) {
1054             return parseStatement(tokens, STMT_ALTER_TABLESPACE, parentNode, TYPE_ALTER_TABLESPACE_STATEMENT);
1055         } else if (tokens.matches(STMT_ALTER_TRIGGER)) {
1056             return parseStatement(tokens, STMT_ALTER_TRIGGER, parentNode, TYPE_ALTER_TRIGGER_STATEMENT);
1057         } else if (tokens.matches(STMT_ALTER_TYPE)) {
1058             return parseStatement(tokens, STMT_ALTER_TYPE, parentNode, TYPE_ALTER_TYPE_STATEMENT);
1059         } else if (tokens.matches(STMT_ALTER_USER)) {
1060             // could encounter: ALTER USER app_user1 GRANT .....
1061             // So need to parse up past the GRANT check
1062             markStartOfStatement(tokens);
1063             tokens.consume(STMT_ALTER_USER);
1064             String name = parseName(tokens);
1065             AstNode result = nodeFactory().node(name, parentNode, TYPE_ALTER_USER_STATEMENT);
1066             tokens.canConsume("GRANT");
1067             parseUntilTerminator(tokens);
1068             markEndOfStatement(tokens, result);
1069             return result;
1070         } else if (tokens.matches(STMT_ALTER_VIEW)) {
1071             return parseStatement(tokens, STMT_ALTER_VIEW, parentNode, TYPE_ALTER_VIEW_STATEMENT);
1072         }
1073 
1074         return super.parseAlterStatement(tokens, parentNode);
1075     }
1076 
1077     /**
1078      * {@inheritDoc}
1079      * 
1080      * @see org.modeshape.sequencer.ddl.StandardDdlParser#parseCreateViewStatement(org.modeshape.sequencer.ddl.DdlTokenStream,
1081      *      org.modeshape.sequencer.ddl.node.AstNode)
1082      */
1083     @Override
1084     protected AstNode parseCreateViewStatement( DdlTokenStream tokens,
1085                                                 AstNode parentNode ) throws ParsingException {
1086         assert tokens != null;
1087         assert parentNode != null;
1088 
1089         markStartOfStatement(tokens);
1090         // CREATE [OR REPLACE]
1091         // [[NO] FORCE] VIEW [schema.] view
1092         // [ ( { alias [ inline_constraint... ]
1093         // | out_of_line_constraint
1094         // }
1095         // [, { alias [ inline_constraint...]
1096         // | out_of_line_constraint
1097         // }
1098         // ]
1099         // )
1100         // | object_view_clause
1101         // | XMLType_view_clause
1102         // ]
1103         // AS subquery [ subquery_restriction_clause ] ;
1104 
1105         // NOTE: the query expression along with the CHECK OPTION clause require no SQL statement terminator.
1106         // So the CHECK OPTION clause will NOT
1107 
1108         String stmtType = "CREATE";
1109         tokens.consume("CREATE");
1110         if (tokens.canConsume("OR", "REPLACE")) {
1111             stmtType = stmtType + SPACE + "OR REPLACE";
1112         } else if (tokens.canConsume("NO", "FORCE")) {
1113             stmtType = stmtType + SPACE + "NO FORCE";
1114         } else if (tokens.canConsume("FORCE")) {
1115             stmtType = stmtType + SPACE + "FORCE";
1116         }
1117 
1118         tokens.consume("VIEW");
1119         stmtType = stmtType + SPACE + "VIEW";
1120 
1121         String name = parseName(tokens);
1122 
1123         AstNode createViewNode = nodeFactory().node(name, parentNode, TYPE_CREATE_VIEW_STATEMENT);
1124 
1125         // CONSUME COLUMNS
1126         parseColumnNameList(tokens, createViewNode, TYPE_COLUMN_REFERENCE);
1127 
1128         // (object_view_clause)
1129         //
1130         // OF [ schema. ] type_name
1131         // { WITH OBJECT IDENTIFIER
1132         // { DEFAULT | ( attribute [, attribute ]... ) }
1133         // | UNDER [ schema. ] superview
1134         // }
1135         // ( { out_of_line_constraint
1136         // | attribute { inline_constraint }...
1137         // } [, { out_of_line_constraint
1138         // | attribute { inline_constraint }...
1139         // }
1140         // ]...
1141         // )
1142 
1143         // (XMLType_view_clause)
1144         //
1145         // OF XMLTYPE [ XMLSchema_spec ]
1146         // WITH OBJECT IDENTIFIER
1147         // { DEFAULT | ( expr [, expr ]...) }
1148 
1149         // Basically, if next token matches "OF", then parse until token matches "AS"
1150 
1151         if (tokens.matches("OF")) {
1152             do {
1153                 tokens.consume();
1154             } while (!tokens.matches("AS"));
1155         }
1156 
1157         tokens.consume("AS");
1158 
1159         String queryExpression = parseUntilTerminator(tokens);
1160 
1161         createViewNode.setProperty(CREATE_VIEW_QUERY_EXPRESSION, queryExpression);
1162 
1163         markEndOfStatement(tokens, createViewNode);
1164 
1165         return createViewNode;
1166     }
1167 
1168     /**
1169      * Parses DDL CREATE INDEX
1170      * 
1171      * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null
1172      * @param parentNode the parent {@link AstNode} node; may not be null
1173      * @return the parsed CREATE INDEX
1174      * @throws ParsingException
1175      */
1176     private AstNode parseCreateIndex( DdlTokenStream tokens,
1177                                       AstNode parentNode ) throws ParsingException {
1178         assert tokens != null;
1179         assert parentNode != null;
1180 
1181         // markStartOfStatement(tokens);
1182         //    	
1183         // // CREATE [ UNIQUE | BITMAP ] INDEX index-name ON
1184         // // { cluster_index_clause | table_index_clause | bitmap_join_index_clause }
1185         // // cluster_index_clause = CLUSTER cluster-name index_attributes
1186         // // table_index_clause =
1187         // // CREATE [UNIQUE] INDEX index-Name
1188         // // ON table-Name ( Simple-column-Name [ ASC | DESC ] [ , Simple-column-Name [ ASC | DESC ]] * )
1189         // tokens.consume(CREATE); // CREATE
1190         //    	
1191         // boolean isUnique = tokens.canConsume("UNIQUE");
1192         // boolean isBitmap = tokens.canConsume("BITMAP");
1193         // tokens.consume("INDEX");
1194         // String indexName = parseName(tokens);
1195         //    	
1196         // AstNode indexNode = nodeFactory().node(indexName, parentNode, TYPE_CREATE_INDEX_STATEMENT);
1197         //		
1198         // tokens.consume("ON");
1199         // boolean isCluster = tokens.canConsume("CLUSTER");
1200         //    	
1201         // String objName = parseName(tokens);
1202         //    	
1203         // indexNode.setProperty(UNIQUE_INDEX, isUnique);
1204         // indexNode.setProperty(BITMAP_INDEX, isBitmap);
1205         // if( !isCluster ) {
1206         // indexNode.setProperty(OracleDdlLexicon.TABLE_NAME, objName);
1207         // }
1208         //    	
1209         // parseUntilTerminator(tokens);
1210         //        
1211         // markEndOfStatement(tokens, indexNode);
1212         //        
1213         // return indexNode;
1214 
1215         return parseStatement(tokens, STMT_CREATE_INDEX, parentNode, TYPE_CREATE_INDEX_STATEMENT);
1216     }
1217 
1218     private AstNode parseCommentStatement( DdlTokenStream tokens,
1219                                            AstNode parentNode ) throws ParsingException {
1220         assert tokens != null;
1221         assert parentNode != null;
1222 
1223         markStartOfStatement(tokens);
1224 
1225         // COMMENT ON COLUMN CS_EXT_FILES.FILE_UID IS
1226         // 'UNIQUE INTERNAL IDENTIFIER, NOT EXPOSED'
1227         // %
1228 
1229         /*
1230         	commentCommand
1231         		:	'COMMENT' 'ON' 
1232         		  ( 'TABLE' tableName 
1233         		  | 'VIEW' tableName
1234         		  | 'INDEX' indexName
1235         		  | 'COLUMN' columnName 
1236         		  | 'PROCEDURE' procedureId
1237         		  | 'MATERIALIZED'? 'VIEW' tableName 
1238         		  ) 'IS' ('NULL' | (CharLiteral)+) commandEnd? //CharLiteral (',' unicodeCharLiteral)*) commandEnd?
1239         		;
1240          */
1241         tokens.consume("COMMENT"); // consumes 'COMMENT' 'ON'
1242         tokens.consume("ON");
1243         String obj = tokens.consume();
1244         String objName = parseName(tokens);
1245 
1246         // System.out.println("  >> FOUND [COMMENT ON] STATEMENT >>  TABLE Name = " + objName);
1247         String commentString = null;
1248 
1249         tokens.consume("IS");
1250         if (tokens.matches("NULL")) {
1251             tokens.consume("NULL");
1252             commentString = "NULL";
1253         } else {
1254             commentString = parseUntilTerminator(tokens).trim();
1255         }
1256 
1257         AstNode commentNode = nodeFactory().node(objName, parentNode, TYPE_COMMENT_ON_STATEMENT);
1258         commentNode.setProperty(OracleDdlLexicon.COMMENT, commentString);
1259         commentNode.setProperty(OracleDdlLexicon.TARGET_OBJECT_TYPE, obj);
1260 
1261         markEndOfStatement(tokens, commentNode);
1262 
1263         return commentNode;
1264     }
1265 
1266     @Override
1267     protected AstNode parseSetStatement( DdlTokenStream tokens,
1268                                          AstNode parentNode ) throws ParsingException {
1269         assert tokens != null;
1270         assert parentNode != null;
1271 
1272         if (tokens.matches(STMT_SET_CONSTRAINT)) {
1273             return parseStatement(tokens, STMT_SET_CONSTRAINT, parentNode, TYPE_SET_CONSTRAINT_STATEMENT);
1274         } else if (tokens.matches(STMT_SET_CONSTRAINTS)) {
1275             return parseStatement(tokens, STMT_SET_CONSTRAINTS, parentNode, TYPE_SET_CONSTRAINTS_STATEMENT);
1276         } else if (tokens.matches(STMT_SET_ROLE)) {
1277             return parseStatement(tokens, STMT_SET_ROLE, parentNode, TYPE_SET_ROLE_STATEMENT);
1278         } else if (tokens.matches(STMT_SET_TRANSACTION)) {
1279             return parseStatement(tokens, STMT_SET_TRANSACTION, parentNode, TYPE_SET_TRANSACTION_STATEMENT);
1280         }
1281 
1282         return super.parseSetStatement(tokens, parentNode);
1283     }
1284 
1285     @Override
1286     protected AstNode parseDropStatement( DdlTokenStream tokens,
1287                                           AstNode parentNode ) throws ParsingException {
1288         assert tokens != null;
1289         assert parentNode != null;
1290 
1291         AstNode dropNode = null;
1292 
1293         if (tokens.matches(StatementStartPhrases.STMT_DROP_TABLE)) {
1294             markStartOfStatement(tokens);
1295 
1296             // DROP TABLE [ schema. ]table [ CASCADE CONSTRAINTS ] [ PURGE ] ;
1297 
1298             tokens.consume(DROP, TABLE);
1299 
1300             String name = parseName(tokens);
1301             dropNode = nodeFactory().node(name, parentNode, TYPE_DROP_TABLE_STATEMENT);
1302 
1303             dropNode.setProperty(DROP_OPTION_TYPE, "TABLE");
1304 
1305             if (tokens.matchesAnyOf("CASCADE", "RESTRICT")) {
1306                 StringBuffer sb = new StringBuffer();
1307 
1308                 if (tokens.canConsume("CASCADE")) {
1309                     sb.append("CASCADE");
1310                     tokens.consume("CONSTRAINTS");
1311                     sb.append(SPACE).append("CONSTRAINTS");
1312                     AstNode optionNode = nodeFactory().node("OPTION", dropNode, StandardDdlLexicon.DROP_OPTION_TYPE);
1313                     optionNode.setProperty(StandardDdlLexicon.NAME, "CASCADE OR RESTRICT");
1314                     optionNode.setProperty(StandardDdlLexicon.VALUE, sb.toString());
1315                 } else {
1316                     tokens.consume("RESTRICT");
1317                     sb.append("RESTRICT");
1318                     tokens.consume("CONSTRAINTS");
1319                     sb.append(SPACE).append("CONSTRAINTS");
1320                     AstNode optionNode = nodeFactory().node("OPTION", dropNode, StandardDdlLexicon.DROP_OPTION_TYPE);
1321                     optionNode.setProperty(StandardDdlLexicon.NAME, "CASCADE OR RESTRICT");
1322                     optionNode.setProperty(StandardDdlLexicon.VALUE, sb.toString());
1323                 }
1324 
1325             }
1326 
1327             if (tokens.canConsume("PURGE")) {
1328                 AstNode optionNode = nodeFactory().node("OPTION", dropNode, StandardDdlLexicon.DROP_OPTION_TYPE);
1329                 optionNode.setProperty(StandardDdlLexicon.NAME, "PURGE");
1330                 optionNode.setProperty(StandardDdlLexicon.VALUE, "PURGE");
1331             }
1332 
1333             markEndOfStatement(tokens, dropNode);
1334 
1335             return dropNode;
1336         } else if (tokens.matches(STMT_DROP_CLUSTER)) {
1337             return parseStatement(tokens, STMT_DROP_CLUSTER, parentNode, TYPE_DROP_CLUSTER_STATEMENT);
1338         } else if (tokens.matches(STMT_DROP_CONTEXT)) {
1339             return parseStatement(tokens, STMT_DROP_CONTEXT, parentNode, TYPE_DROP_CONTEXT_STATEMENT);
1340         } else if (tokens.matches(STMT_DROP_DATABASE)) {
1341             return parseStatement(tokens, STMT_DROP_DATABASE, parentNode, TYPE_DROP_DATABASE_STATEMENT);
1342         } else if (tokens.matches(STMT_DROP_PUBLIC_DATABASE)) {
1343             return parseStatement(tokens, STMT_DROP_PUBLIC_DATABASE, parentNode, TYPE_DROP_DATABASE_STATEMENT);
1344         } else if (tokens.matches(STMT_DROP_DIMENSION)) {
1345             return parseStatement(tokens, STMT_DROP_DIMENSION, parentNode, TYPE_DROP_DIMENSION_STATEMENT);
1346         } else if (tokens.matches(STMT_DROP_DIRECTORY)) {
1347             return parseStatement(tokens, STMT_DROP_DIRECTORY, parentNode, TYPE_DROP_DIRECTORY_STATEMENT);
1348         } else if (tokens.matches(STMT_DROP_DISKGROUP)) {
1349             return parseStatement(tokens, STMT_DROP_DISKGROUP, parentNode, TYPE_DROP_DISKGROUP_STATEMENT);
1350         } else if (tokens.matches(STMT_DROP_FUNCTION)) {
1351             return parseStatement(tokens, STMT_DROP_FUNCTION, parentNode, TYPE_DROP_FUNCTION_STATEMENT);
1352         } else if (tokens.matches(STMT_DROP_INDEX)) {
1353             return parseStatement(tokens, STMT_DROP_INDEX, parentNode, TYPE_DROP_INDEX_STATEMENT);
1354         } else if (tokens.matches(STMT_DROP_INDEXTYPE)) {
1355             return parseStatement(tokens, STMT_DROP_INDEXTYPE, parentNode, TYPE_DROP_INDEXTYPE_STATEMENT);
1356         } else if (tokens.matches(STMT_DROP_JAVA)) {
1357             return parseStatement(tokens, STMT_DROP_JAVA, parentNode, TYPE_DROP_JAVA_STATEMENT);
1358         } else if (tokens.matches(STMT_DROP_LIBRARY)) {
1359             return parseStatement(tokens, STMT_DROP_LIBRARY, parentNode, TYPE_DROP_LIBRARY_STATEMENT);
1360         } else if (tokens.matches(STMT_DROP_MATERIALIZED)) {
1361             return parseStatement(tokens, STMT_DROP_MATERIALIZED, parentNode, TYPE_DROP_MATERIALIZED_STATEMENT);
1362         } else if (tokens.matches(STMT_DROP_OPERATOR)) {
1363             return parseStatement(tokens, STMT_DROP_OPERATOR, parentNode, TYPE_DROP_OPERATOR_STATEMENT);
1364         } else if (tokens.matches(STMT_DROP_OUTLINE)) {
1365             return parseStatement(tokens, STMT_DROP_OUTLINE, parentNode, TYPE_DROP_OUTLINE_STATEMENT);
1366         } else if (tokens.matches(STMT_DROP_PACKAGE)) {
1367             return parseStatement(tokens, STMT_DROP_PACKAGE, parentNode, TYPE_DROP_PACKAGE_STATEMENT);
1368         } else if (tokens.matches(STMT_DROP_PROCEDURE)) {
1369             return parseStatement(tokens, STMT_DROP_PROCEDURE, parentNode, TYPE_DROP_PROCEDURE_STATEMENT);
1370         } else if (tokens.matches(STMT_DROP_PROFILE)) {
1371             return parseStatement(tokens, STMT_DROP_PROFILE, parentNode, TYPE_DROP_PROFILE_STATEMENT);
1372         } else if (tokens.matches(STMT_DROP_ROLE)) {
1373             return parseStatement(tokens, STMT_DROP_ROLE, parentNode, TYPE_DROP_ROLE_STATEMENT);
1374         } else if (tokens.matches(STMT_DROP_ROLLBACK)) {
1375             return parseStatement(tokens, STMT_DROP_ROLLBACK, parentNode, TYPE_DROP_ROLLBACK_STATEMENT);
1376         } else if (tokens.matches(STMT_DROP_SEQUENCE)) {
1377             return parseStatement(tokens, STMT_DROP_SEQUENCE, parentNode, TYPE_DROP_SEQUENCE_STATEMENT);
1378         } else if (tokens.matches(STMT_DROP_SYNONYM)) {
1379             return parseStatement(tokens, STMT_DROP_SYNONYM, parentNode, TYPE_DROP_SYNONYM_STATEMENT);
1380         } else if (tokens.matches(STMT_DROP_PUBLIC_SYNONYM)) {
1381             return parseStatement(tokens, STMT_DROP_PUBLIC_SYNONYM, parentNode, TYPE_DROP_SYNONYM_STATEMENT);
1382         } else if (tokens.matches(STMT_DROP_TABLESPACE)) {
1383             return parseStatement(tokens, STMT_DROP_TABLESPACE, parentNode, TYPE_DROP_TABLESPACE_STATEMENT);
1384         } else if (tokens.matches(STMT_DROP_TRIGGER)) {
1385             return parseStatement(tokens, STMT_DROP_TRIGGER, parentNode, TYPE_DROP_TRIGGER_STATEMENT);
1386         } else if (tokens.matches(STMT_DROP_TYPE)) {
1387             return parseStatement(tokens, STMT_DROP_TYPE, parentNode, TYPE_DROP_TYPE_STATEMENT);
1388         } else if (tokens.matches(STMT_DROP_USER)) {
1389             return parseStatement(tokens, STMT_DROP_USER, parentNode, TYPE_DROP_USER_STATEMENT);
1390         }
1391 
1392         return super.parseDropStatement(tokens, parentNode);
1393     }
1394 
1395     private AstNode parseCreateJavaStatement( DdlTokenStream tokens,
1396                                               AstNode parentNode ) throws ParsingException {
1397         assert tokens != null;
1398         assert parentNode != null;
1399 
1400         markStartOfStatement(tokens);
1401         tokens.consume(STMT_CREATE_JAVA);
1402         AstNode result = nodeFactory().node(getStatementTypeName(STMT_CREATE_JAVA), parentNode, TYPE_CREATE_JAVA_STATEMENT);
1403 
1404         // We want to parse until we find a terminator AND all brackets are matched.
1405 
1406         // Assume we start with open parenthesis '{', then we can count on walking through ALL tokens until we find the close
1407         // parenthesis '}'. If there are intermediate parenthesis, we can count on them being pairs.
1408 
1409         int iParen = 0;
1410 
1411         while (tokens.hasNext()) {
1412             if (tokens.matches('{')) {
1413                 iParen++;
1414             } else if (tokens.matches('}')) {
1415                 iParen--;
1416             }
1417             tokens.consume();
1418 
1419             if (isTerminator(tokens) && iParen == 0) {
1420                 break;
1421             }
1422         }
1423 
1424         markEndOfStatement(tokens, result);
1425 
1426         return result;
1427     }
1428 
1429     /**
1430      * Utility method designed to parse columns within an ALTER TABLE ADD statement.
1431      * 
1432      * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null
1433      * @param tableNode
1434      * @param isAlterTable
1435      * @throws ParsingException
1436      */
1437     protected void parseColumns( DdlTokenStream tokens,
1438                                  AstNode tableNode,
1439                                  boolean isAlterTable ) throws ParsingException {
1440         assert tokens != null;
1441         assert tableNode != null;
1442 
1443         // TODO: Oracle has changed some things between versions 9i, and 10/11,
1444         // Basically they've added column properties (i.e. SORT option, ENCRYPT encryption_spec)
1445         // Need to 1) Override parseColumnDefinition shouldParseOracleProceduresAndFunctionsto handle these.
1446 
1447         String tableElementString = getTableElementsString(tokens, false);
1448 
1449         DdlTokenStream localTokens = new DdlTokenStream(tableElementString, DdlTokenStream.ddlTokenizer(false), false);
1450 
1451         localTokens.start();
1452 
1453         StringBuffer unusedTokensSB = new StringBuffer();
1454 
1455         do {
1456             if (isColumnDefinitionStart(localTokens)) {
1457                 parseColumnDefinition(localTokens, tableNode, true);
1458             } else {
1459                 // THIS IS AN ERROR. NOTHING FOUND.
1460                 // NEED TO absorb tokens
1461                 while (localTokens.hasNext() && !localTokens.matches(COMMA)) {
1462                     unusedTokensSB.append(SPACE).append(localTokens.consume());
1463                 }
1464             }
1465         } while (localTokens.canConsume(COMMA));
1466 
1467         if (unusedTokensSB.length() > 0) {
1468             String msg = DdlSequencerI18n.unusedTokensParsingColumnDefinition.text(tableNode.getProperty(StandardDdlLexicon.NAME));
1469             DdlParserProblem problem = new DdlParserProblem(Problems.WARNING, getCurrentMarkedPosition(), msg);
1470             problem.setUnusedSource(unusedTokensSB.toString());
1471             addProblem(problem, tableNode);
1472         }
1473     }
1474 
1475     /**
1476      * Utility method to parse a generic statement given a start phrase and statement mixin type.
1477      * 
1478      * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null
1479      * @param stmt_start_phrase the string array statement start phrase
1480      * @param parentNode the parent {@link AstNode} node; may not be null
1481      * @param mixinType the mixin type of the newly created statement node
1482      * @return the new node
1483      */
1484     protected AstNode parseSlashedStatement( DdlTokenStream tokens,
1485                                              String[] stmt_start_phrase,
1486                                              AstNode parentNode,
1487                                              Name mixinType ) {
1488         assert tokens != null;
1489         assert stmt_start_phrase != null && stmt_start_phrase.length > 0;
1490         assert parentNode != null;
1491 
1492         markStartOfStatement(tokens);
1493         tokens.consume(stmt_start_phrase);
1494         AstNode result = nodeFactory().node(getStatementTypeName(stmt_start_phrase), parentNode, mixinType);
1495 
1496         parseUntilFwdSlash(tokens, false);
1497 
1498         consumeSlash(tokens);
1499 
1500         markEndOfStatement(tokens, result);
1501 
1502         return result;
1503     }
1504 
1505     /**
1506      * Various Oracle statements (i.e. "CREATE OR REPLACE PACKAGE", etc...) may contain multiple SQL statements that will be
1507      * terminated by the semicolon, ';'. In these cases, the terminator is now the '/' character.
1508      * 
1509      * @param tokens the tokenized {@link DdlTokenStream} of the DDL input content; may not be null
1510      * @param stopAtStatementStart
1511      * @return the parsed string.
1512      * @throws ParsingException
1513      */
1514     private String parseUntilFwdSlash( DdlTokenStream tokens,
1515                                        boolean stopAtStatementStart ) throws ParsingException {
1516         StringBuffer sb = new StringBuffer();
1517         if (stopAtStatementStart) {
1518             while (tokens.hasNext()
1519 
1520             && !tokens.matches(DdlTokenizer.STATEMENT_KEY) && !tokens.matches('/')) { // !tokens.matches(DdlTokenizer.STATEMENT_KEY
1521                 // )
1522                 // &&
1523                 sb.append(SPACE).append(tokens.consume());
1524             }
1525         } else {
1526             while (tokens.hasNext() && !isFwdSlashedStatement(tokens) && !tokens.matches('/')) { // !tokens.matches(DdlTokenizer.
1527                 // STATEMENT_KEY) &&
1528                 sb.append(SPACE).append(tokens.consume());
1529             }
1530         }
1531         return sb.toString();
1532     }
1533 
1534     private boolean isFwdSlashedStatement( DdlTokenStream tokens ) throws ParsingException {
1535         for (int i = 0; i < SLASHED_STMT_PHRASES.length; i++) {
1536             if (tokens.matches(SLASHED_STMT_PHRASES[i])) {
1537                 return true;
1538             }
1539         }
1540 
1541         return false;
1542     }
1543 
1544     private void consumeSlash( DdlTokenStream tokens ) throws ParsingException {
1545         tokens.canConsume("/");
1546     }
1547 
1548     /**
1549      * {@inheritDoc}
1550      * 
1551      * @see org.modeshape.sequencer.ddl.StandardDdlParser#getValidSchemaChildTypes()
1552      */
1553     @Override
1554     protected Name[] getValidSchemaChildTypes() {
1555         return VALID_SCHEMA_CHILD_STMTS;
1556     }
1557 
1558     // ===========================================================================================================================
1559     // PARSE OBJECTS
1560     // ===========================================================================================================================
1561 
1562     /**
1563      * This class provides custom data type parsing for Oracle-specific data types.
1564      */
1565     class OracleDataTypeParser extends DataTypeParser {
1566 
1567         /*
1568          * (non-Javadoc)
1569          * @see org.modeshape.sequencer.ddl.datatype.DataTypeParser#parseCustomType(org.modeshape.common.text.DdlTokenStream)
1570          */
1571         @Override
1572         protected DataType parseCustomType( DdlTokenStream tokens ) throws ParsingException {
1573             DataType dataType = null;
1574             String typeName = null;
1575 
1576             if (tokens.matches(OracleDataTypes.DTYPE_BINARY_FLOAT)) {
1577                 dataType = new DataType();
1578                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BINARY_FLOAT);
1579                 dataType.setName(typeName);
1580             } else if (tokens.matches(OracleDataTypes.DTYPE_BINARY_DOUBLE)) {
1581                 dataType = new DataType();
1582                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BINARY_DOUBLE);
1583                 dataType.setName(typeName);
1584             } else if (tokens.matches(OracleDataTypes.DTYPE_LONG)) {
1585                 dataType = new DataType();
1586                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_LONG);
1587                 dataType.setName(typeName);
1588             } else if (tokens.matches(OracleDataTypes.DTYPE_LONG_RAW)) {
1589                 dataType = new DataType();
1590                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_LONG_RAW);
1591                 dataType.setName(typeName);
1592             } else if (tokens.matches(OracleDataTypes.DTYPE_BLOB)) {
1593                 dataType = new DataType();
1594                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BLOB);
1595                 dataType.setName(typeName);
1596             } else if (tokens.matches(OracleDataTypes.DTYPE_CLOB)) {
1597                 dataType = new DataType();
1598                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_CLOB);
1599                 dataType.setName(typeName);
1600             } else if (tokens.matches(OracleDataTypes.DTYPE_NCLOB)) {
1601                 dataType = new DataType();
1602                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_NCLOB);
1603                 dataType.setName(typeName);
1604             } else if (tokens.matches(OracleDataTypes.DTYPE_BFILE)) {
1605                 dataType = new DataType();
1606                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_BFILE);
1607                 dataType.setName(typeName);
1608             } else if (tokens.matches(OracleDataTypes.DTYPE_VARCHAR2)) {
1609                 dataType = new DataType();
1610                 // VARCHAR2(size [BYTE | CHAR])
1611                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_VARCHAR2); // VARCHAR2
1612                 consume(tokens, dataType, false, L_PAREN);
1613                 long length = parseLong(tokens, dataType);
1614                 canConsume(tokens, dataType, true, "BYTE");
1615                 canConsume(tokens, dataType, true, "CHAR");
1616                 consume(tokens, dataType, false, R_PAREN);
1617                 dataType.setName(typeName);
1618                 dataType.setLength(length);
1619             } else if (tokens.matches(OracleDataTypes.DTYPE_RAW)) {
1620                 dataType = new DataType();
1621                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_RAW);
1622                 long length = parseBracketedLong(tokens, dataType);
1623                 dataType.setName(typeName);
1624                 dataType.setLength(length);
1625             } else if (tokens.matches(OracleDataTypes.DTYPE_NVARCHAR2)) {
1626                 dataType = new DataType();
1627                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_NVARCHAR2);
1628                 long length = parseBracketedLong(tokens, dataType);
1629                 dataType.setName(typeName);
1630                 dataType.setLength(length);
1631             } else if (tokens.matches(OracleDataTypes.DTYPE_NUMBER)) {
1632                 dataType = new DataType();
1633                 typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_NUMBER);
1634                 int precision = 0;
1635                 int scale = 0;
1636                 if (tokens.matches(L_PAREN)) {
1637                     consume(tokens, dataType, false, L_PAREN);
1638                     precision = (int)parseLong(tokens, dataType);
1639                     if (canConsume(tokens, dataType, false, COMMA)) {
1640                         scale = (int)parseLong(tokens, dataType);
1641                     } else {
1642                         scale = getDefaultScale();
1643                     }
1644                     consume(tokens, dataType, false, R_PAREN);
1645                 } else {
1646                     precision = getDefaultPrecision();
1647                     scale = getDefaultScale();
1648                 }
1649                 dataType.setName(typeName);
1650                 dataType.setPrecision(precision);
1651                 dataType.setScale(scale);
1652             } else if (tokens.matches(OracleDataTypes.DTYPE_INTERVAL_YEAR)) {
1653                 // INTERVAL YEAR (year_precision) TO MONTH
1654 
1655             } else if (tokens.matches(OracleDataTypes.DTYPE_INTERVAL_DAY)) {
1656                 // INTERVAL DAY (day_precision) TO SECOND (fractional_seconds_precision)
1657             }
1658 
1659             if (dataType == null) {
1660                 dataType = super.parseCustomType(tokens);
1661             }
1662 
1663             return dataType;
1664         }
1665 
1666         /**
1667          * Because Oracle has an additional option on the CHAR datatype, we need to override the super method, check for CHAR type
1668          * and parse, else call super.parseCharStringType(). {@inheritDoc}
1669          * 
1670          * @see org.modeshape.sequencer.ddl.datatype.DataTypeParser#parseCharStringType(org.modeshape.sequencer.ddl.DdlTokenStream)
1671          */
1672         @Override
1673         protected DataType parseCharStringType( DdlTokenStream tokens ) throws ParsingException {
1674             DataType dataType = null;
1675 
1676             if (tokens.matches(OracleDataTypes.DTYPE_CHAR_ORACLE)) { // CHAR (size [BYTE | CHAR]) (i.e. CHAR (10 BYTE) )
1677                 dataType = new DataType();
1678                 String typeName = consume(tokens, dataType, true, OracleDataTypes.DTYPE_CHAR_ORACLE);
1679                 consume(tokens, dataType, false, L_PAREN);
1680                 long length = parseLong(tokens, dataType);
1681                 canConsume(tokens, dataType, true, "BYTE");
1682                 canConsume(tokens, dataType, true, "CHAR");
1683                 consume(tokens, dataType, false, R_PAREN);
1684                 dataType.setName(typeName);
1685                 dataType.setLength(length);
1686             } else {
1687                 dataType = super.parseCharStringType(tokens);
1688             }
1689 
1690             return dataType;
1691         }
1692 
1693         /**
1694          * {@inheritDoc}
1695          * 
1696          * @see org.modeshape.sequencer.ddl.datatype.DataTypeParser#isCustomDataType(org.modeshape.sequencer.ddl.DdlTokenStream)
1697          */
1698         @Override
1699         protected boolean isCustomDataType( DdlTokenStream tokens ) throws ParsingException {
1700             for (String[] stmt : oracleDataTypeStrings) {
1701                 if (tokens.matches(stmt)) return true;
1702             }
1703 
1704             return false;
1705         }
1706 
1707     }
1708 
1709     /**
1710      * {@inheritDoc}
1711      * 
1712      * @see org.modeshape.sequencer.ddl.StandardDdlParser#getDataTypeStartWords()
1713      */
1714     @Override
1715     protected List<String> getCustomDataTypeStartWords() {
1716         return OracleDataTypes.CUSTOM_DATATYPE_START_WORDS;
1717     }
1718 }