/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.sequencer.ddl.dialect.teiid;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.modeshape.common.text.ParsingException;
import org.modeshape.common.text.Position;
import org.modeshape.common.util.StringUtil;
import org.modeshape.sequencer.ddl.DdlTokenStream;
import org.modeshape.sequencer.ddl.StandardDdlLexicon;
import org.modeshape.sequencer.ddl.datatype.DataType;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.DdlStatement;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.SchemaElementType;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.TeiidNonReservedWord;
import org.modeshape.sequencer.ddl.dialect.teiid.TeiidDdlConstants.TeiidReservedWord;
import org.modeshape.sequencer.ddl.node.AstNode;
/**
* A parser for the Teiid <create table> DDL statement.
* <p>
* <code>
* CREATE ( FOREIGN TABLE | ( VIRTUAL )? VIEW ) <identifier> <create table body> ( AS <query expression> )?
* </code>
*/
final class CreateTableParser extends StatementParser {
protected static final String PRIMARY_KEY_PREFIX = "PK_";
protected static final String FOREIGN_KEY_PREFIX = "FK_";
protected static final String UNIQUE_CONSTRAINT_PREFIX = "UC_";
protected static final String INDEX_PREFIX = "NDX_";
protected static final String ACCESS_PATTERN_PREFIX = "AP_";
/**
* Sequence is any number of characters, word boundary, column name, word boundary, and any number of characters.
*/
private static final String REGEX = ".*\\b(?i)%s(?-i)\\b.*";
private List<UnresolvedTableReferenceNode> unresolvedTableReferences;
/**
* @param expression the expression being looked at (cannot be <code>null</code>)
* @param columnName the name of the column being looked for in the expression (cannot be <code>null</code>)
* @return <code>true</code> if the expression contains the column name
*/
static boolean contains( final String expression,
final String columnName ) {
return expression.matches(String.format(REGEX, columnName));
}
CreateTableParser( final TeiidDdlParser teiidDdlParser ) {
super(teiidDdlParser);
unresolvedTableReferences = new ArrayList<UnresolvedTableReferenceNode>(10);
}
private String createConstraintName( final String constraintType,
final AstNode tableNode ) {
String prefix = null;
if (PRIMARY_KEY.equals(constraintType)) {
prefix = PRIMARY_KEY_PREFIX;
} else if (FOREIGN_KEY.equals(constraintType)) {
prefix = FOREIGN_KEY_PREFIX;
} else if (TeiidReservedWord.UNIQUE.toDdl().equals(constraintType)) {
prefix = UNIQUE_CONSTRAINT_PREFIX;
} else if (TeiidNonReservedWord.INDEX.toDdl().equals(constraintType)) {
prefix = INDEX_PREFIX;
} else if (TeiidNonReservedWord.ACCESSPATTERN.toDdl().equals(constraintType)) {
prefix = ACCESS_PATTERN_PREFIX;
}
int index = 1;
while (true) {
final String name = prefix + index;
final List<AstNode> kids = tableNode.childrenWithName(name);
if (kids.isEmpty()) {
return name;
}
++index;
}
}
/**
* @param tableNode the table node whose specified column node child is being requested (cannot be <code>null</code>)
* @param columnName the name of the column node being requested (cannot be <code>null</code> or empty)
* @return the column node or <code>null</code> if not found
*/
private AstNode getColumnNode( final AstNode tableNode,
final String columnName ) {
assert (tableNode != null);
assert (columnName != null);
final List<AstNode> kids = tableNode.childrenWithName(columnName);
// not found
if (kids.isEmpty()) {
return null;
}
// assume child is a column
if (kids.size() == 1) {
return kids.get(0);
}
// need to find column
for (final AstNode kid : kids) {
if (kid.getMixins().contains(StandardDdlLexicon.TYPE_COLUMN_DEFINITION)) {
return kid;
}
}
return null; // not found
}
/**
* {@inheritDoc}
*
* @see org.modeshape.sequencer.ddl.dialect.teiid.StatementParser#matches(org.modeshape.sequencer.ddl.DdlTokenStream)
*/
@Override
boolean matches( final DdlTokenStream tokens ) {
return tokens.matches(DdlStatement.CREATE_FOREIGN_TABLE.tokens())
|| tokens.matches(DdlStatement.CREATE_VIRTUAL_VIEW.tokens()) || tokens.matches(DdlStatement.CREATE_VIEW.tokens());
}
/**
* {@inheritDoc}
*
* @see org.modeshape.sequencer.ddl.dialect.teiid.StatementParser#parse(org.modeshape.sequencer.ddl.DdlTokenStream,
* org.modeshape.sequencer.ddl.node.AstNode)
*/
@Override
AstNode parse( final DdlTokenStream tokens,
final AstNode parentNode ) throws ParsingException {
// CREATE FOREIGN TABLE <identifier> <create table body>
// CREATE FOREIGN TABLE <identifier> <create table body> AS <query expression>
// CREATE VIRTUAL VIEW <identifier> <create table body>
// CREATE VIRTUAL VIEW <identifier> <create table body> AS <query expression>
// CREATE VIEW <identifier> <create table body>
// CREATE VIEW <identifier> <create table body> AS <query expression>
boolean view = true;
DdlStatement stmt = null;
SchemaElementType schemaElementType = null;
if (tokens.canConsume(DdlStatement.CREATE_FOREIGN_TABLE.tokens())) {
stmt = DdlStatement.CREATE_FOREIGN_TABLE;
view = false;
schemaElementType = SchemaElementType.FOREIGN;
} else if (tokens.canConsume(DdlStatement.CREATE_VIRTUAL_VIEW.tokens())) {
stmt = DdlStatement.CREATE_VIRTUAL_VIEW;
schemaElementType = SchemaElementType.VIRTUAL;
} else if (tokens.canConsume(DdlStatement.CREATE_VIEW.tokens())) {
stmt = DdlStatement.CREATE_VIEW;
schemaElementType = SchemaElementType.FOREIGN;
} else {
throw new TeiidDdlParsingException(tokens, "Unparsable create table statement");
}
assert (stmt != null) : "Create table statement is null";
// parse identifier
final String id = parseIdentifier(tokens);
final AstNode tableNode = getNodeFactory().node(id,
parentNode,
(view ? TeiidDdlLexicon.CreateTable.VIEW_STATEMENT : TeiidDdlLexicon.CreateTable.TABLE_STATEMENT));
tableNode.setProperty(TeiidDdlLexicon.SchemaElement.TYPE, schemaElementType.toDdl());
// must have a table body
parseTableBody(tokens, tableNode);
// may have an AS clause
if (tokens.hasNext() && tokens.canConsume(TeiidReservedWord.AS.toDdl())) {
// must have a query expression
if (!parseQueryExpression(tokens, tableNode)) {
throw new TeiidDdlParsingException(tokens, "Unparsable create table statement");
}
}
return tableNode;
}
/**
* The tokens must start with a left paren, end with a right paren, and have content between. Any parens in the content must
* have matching parens.
*
* @param tokens the tokens being processed (cannot be <code>null</code> but or empty)
* @return the expression (never <code>null</code> or empty.
* @throws ParsingException if there is a problem parsing the query expression
*/
private String parseExpression( final DdlTokenStream tokens ) throws ParsingException {
if (!tokens.hasNext() || !tokens.matches(L_PAREN)) {
throw new TeiidDdlParsingException(tokens, "Unparsable expression list");
}
Position prevPosition = tokens.nextPosition();
String prevValue = tokens.consume(); // don't include first paren in expression
int numLeft = 1;
int numRight = 0;
final StringBuilder text = new StringBuilder();
while (tokens.hasNext()) {
final Position currPosition = tokens.nextPosition();
if (tokens.matches(L_PAREN)) {
++numLeft;
} else if (tokens.matches(R_PAREN)) {
if (numLeft == ++numRight) {
tokens.consume(R_PAREN); // don't include last paren in expression
if ((currPosition.getIndexInContent() - prevValue.length() + 1 - prevPosition.getIndexInContent()) > 1) {
text.append(SPACE);
}
break;
}
}
final String value = tokens.consume();
text.append(getWhitespace(currPosition, prevPosition, prevValue));
text.append(value);
prevValue = value;
prevPosition = currPosition;
}
if (numLeft != numRight) {
throw new TeiidDdlParsingException(tokens, "Unparsable expression list");
}
return text.toString();
}
/**
* Assumes tokens have a one set of parens with one or more identifiers separated by commas. All of these tokens are consumed.
* <code>
* <column list>
*
* <lparen> <identifier> ( <comma> <identifier> )* <rparen>
* </code>
*
* @param tokens the tokens being processed (cannot be <code>null</code>)
* @return the list of identifiers (never <code>null</code> and never empty)
* @throws ParsingException if there is a problem parsing the identifier list
*/
List<String> parseIdentifierList( final DdlTokenStream tokens ) throws ParsingException {
if (tokens.canConsume(L_PAREN)) {
final List<String> identifiers = new ArrayList<String>();
final String firstId = parseIdentifier(tokens);
identifiers.add(firstId);
// process remaining IDs
while (tokens.canConsume(COMMA)) {
final String nextId = parseIdentifier(tokens);
identifiers.add(nextId);
}
if (tokens.canConsume(R_PAREN)) {
return identifiers;
}
}
throw new TeiidDdlParsingException(tokens, "Unparsable identifier list");
}
/**
* <code>
* <query expression> == ( WITH <with list element> ( <comma> <with list element> )* )? <query expression body>
* <with list element> == <identifier> ( <column list> )? AS <lparen> <query expression> <rparen>
* <query expression body> == <query term> ( ( UNION | EXCEPT ) ( ALL | DISTINCT )? <query term> )* ( <order by clause> )? ( <limit clause> )? ( <option clause> )?
* <code>
*
* @param tokens the tokens being processed (cannot be <code>null</code> but can be empty)
* @param tableNode the table node owning this query expression (cannot be <code>null</code>)
* @return <code>true</code> if a query expression was successfully parsed
*/
boolean parseQueryExpression( final DdlTokenStream tokens,
final AstNode tableNode ) {
final String queryExpression = parseUntilTerminator(tokens);
if (StringUtil.isBlank(queryExpression)) {
return false;
}
tableNode.setProperty(TeiidDdlLexicon.CreateTable.QUERY_EXPRESSION, queryExpression);
return true;
}
/**
* Assumes tokens have a one set of parens with one or more identifiers separated by commas. All of these tokens are consumed.
*
* @param tokens the tokens being processed (cannot be <code>null</code> or empty)
* @param tableNode the table node used to lookup referenced column nodes (cannot be <code>null</code>)
* @param unresolvedReferencedTableNode the unresolved table references component; may be null
* @return the collection of referenced column nodes (never <code>null</code> or empty)
* @throws ParsingException if a node of a referenced column cannot be found
*/
private List<AstNode> parseReferenceList( final DdlTokenStream tokens,
final AstNode tableNode,
final UnresolvedTableReferenceNode unresolvedReferencedTableNode )
throws ParsingException {
final List<String> columns = parseIdentifierList(tokens);
final List<AstNode> references = new ArrayList<AstNode>(columns.size());
/* If tableNode == null bypass column node setting */
for (final String columnName : columns) {
if (unresolvedReferencedTableNode != null) {
unresolvedReferencedTableNode.addColumnReferenceName(columnName);
continue;
}
final AstNode referencedColumnNode = getColumnNode(tableNode, columnName);
// can't find referenced column
if (referencedColumnNode == null) {
this.logger.debug("Create table statement node of column reference '{0}' was not found", columnName);
} else {
references.add(referencedColumnNode);
}
}
return references;
}
/**
* <code>
* <create table body>
*
* ( <lparen> <table element> ( <comma> <table element> )* ( <comma> ( CONSTRAINT <identifier> )? ( <primary key> | <other constraints> | <foreign key> ) ( <options clause> )? )* <rparen> )? ( <options clause> )?
* </code>
*
* @param tokens the tokens being processed (cannot be <code>null</code>)
* @param tableNode the table node whose body is being processed (cannot be <code>null</code>)
*/
void parseTableBody( final DdlTokenStream tokens,
final AstNode tableNode ) {
// may have one or more table elements
if (tokens.canConsume(L_PAREN)) {
// process first table element
parseTableElement(tokens, tableNode);
// process any remaining table elements and constraints
while (tokens.canConsume(COMMA)) {
if (!parseTableBodyConstraint(tokens, tableNode)) {
parseTableElement(tokens, tableNode);
}
}
if (!tokens.canConsume(R_PAREN)) {
throw new TeiidDdlParsingException(tokens, "Unparsable table body");
}
}
// may have options clause
parseOptionsClause(tokens, tableNode);
}
/**
* <code>
* ( CONSTRAINT <identifier> )? ( <primary key> | <other constraints> | <foreign key> ) ( <options clause> )
* </code>
*
* @param tokens the tokens being processed (cannot be <code>null</code>)
* @param tableNode the table node whose constraint is being processed (cannot be <code>null</code>)
* @return <code>true</code> if a table body constraint was successfully parsed
* @throws ParsingException if there is a problem parsing a table body constraint
*/
boolean parseTableBodyConstraint( final DdlTokenStream tokens,
final AstNode tableNode ) throws ParsingException {
if (!tokens.hasNext()) {
return false;
}
// must find CONSTRAINT, PRIMARY KEY, FOREIGN KEY, UNIQUE, ACCESSPATTERN, INDEX
if (tokens.matches(CONSTRAINT) || tokens.matches(PRIMARY, KEY) || tokens.matches(FOREIGN, KEY) || tokens.matches(UNIQUE)
|| tokens.matches(TeiidNonReservedWord.ACCESSPATTERN.toDdl()) || tokens.matches(TeiidNonReservedWord.INDEX.toDdl())) {
String constraintType = null;
// may have identifier
String constraintId = (tokens.canConsume(CONSTRAINT) ? parseIdentifier(tokens) : null);
AstNode constraintNode = null;
String nodeType = null;
// <primary key> == PRIMARY KEY <column list>
// <foreign key> == FOREIGN KEY <column list> REFERENCES <identifier> ( <column list> )?
// <other constraints> == ( ( UNIQUE | ACCESSPATTERN ) <column list> ) or ( INDEX <lparen> <expression list> <rparen>
if (tokens.matches(PRIMARY, KEY) || tokens.matches(FOREIGN, KEY) || tokens.matches(TeiidReservedWord.UNIQUE.toDdl())
|| tokens.matches(TeiidNonReservedWord.ACCESSPATTERN.toDdl())) {
if (tokens.canConsume(PRIMARY, KEY)) {
constraintType = PRIMARY_KEY;
nodeType = TeiidDdlLexicon.Constraint.TABLE_ELEMENT;
} else if (tokens.canConsume(FOREIGN, KEY)) {
constraintType = FOREIGN_KEY;
nodeType = TeiidDdlLexicon.Constraint.FOREIGN_KEY_CONSTRAINT;
} else if (tokens.matchesAnyOf(TeiidReservedWord.UNIQUE.toDdl(), TeiidNonReservedWord.ACCESSPATTERN.toDdl())) {
constraintType = tokens.consume();
nodeType = TeiidDdlLexicon.Constraint.TABLE_ELEMENT;
}
assert (constraintType != null);
assert (nodeType != null);
// process column list and create references to their nodes
final List<AstNode> references = parseReferenceList(tokens, tableNode, null);
// set a constraint ID if needed
if (StringUtil.isBlank(constraintId)) {
constraintId = createConstraintName(constraintType, tableNode);
}
// create constraint node
constraintNode = getNodeFactory().node(constraintId, tableNode, nodeType);
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TYPE, constraintType);
constraintNode.setProperty(TeiidDdlLexicon.Constraint.REFERENCES, references);
if (FOREIGN_KEY.equals(constraintType)) {
if (tokens.canConsume(TeiidReservedWord.REFERENCES.toDdl())) {
final String referencesTableName = parseIdentifier(tokens);
final AstNode referencesTableNode = getNode(tableNode.getParent(),
referencesTableName,
TeiidDdlLexicon.CreateTable.TABLE_STATEMENT,
TeiidDdlLexicon.CreateTable.VIEW_STATEMENT);
UnresolvedTableReferenceNode unresolvedTableReferenceNode = null;
// can't find referenced table
if (referencesTableNode == null) {
unresolvedTableReferenceNode = new UnresolvedTableReferenceNode(constraintNode, referencesTableName);
unresolvedTableReferences.add(unresolvedTableReferenceNode);
} else {
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TABLE_REFERENCE, referencesTableNode);
}
// may have referenced columns so check for opening paren before parsing refs
if (tokens.matches(L_PAREN)) {
final List<AstNode> constraintReferences = parseReferenceList(tokens,
referencesTableNode,
unresolvedTableReferenceNode);
if (!constraintReferences.isEmpty()) {
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TABLE_REFERENCE_REFERENCES,
constraintReferences);
}
}
} else {
throw new TeiidDdlParsingException(tokens,
"Unparsable table body foreign key constraint (missing REFERENCES keyword)");
}
}
} else if (tokens.matches(TeiidNonReservedWord.INDEX.toDdl(), L_PAREN)) {
tokens.consume(TeiidNonReservedWord.INDEX.toDdl());
constraintType = TeiidNonReservedWord.INDEX.toDdl();
if (StringUtil.isBlank(constraintId)) {
constraintId = createConstraintName(constraintType, tableNode);
}
constraintNode = getNodeFactory().node(constraintId, tableNode, TeiidDdlLexicon.Constraint.INDEX_CONSTRAINT);
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TYPE, constraintType);
final String expression = parseExpression(tokens);
constraintNode.setProperty(TeiidDdlLexicon.Constraint.EXPRESSION, expression);
// look for table element references in the expression and set references
final List<AstNode> columns = tableNode.getChildren(TeiidDdlLexicon.CreateTable.TABLE_ELEMENT);
final Set<AstNode> referencedColumns = new HashSet<AstNode>(columns.size());
for (final AstNode column : columns) {
if (contains(expression, column.getName())) {
referencedColumns.add(column);
}
}
if (!referencedColumns.isEmpty()) {
constraintNode.setProperty(TeiidDdlLexicon.Constraint.REFERENCES, new ArrayList<AstNode>(referencedColumns));
}
} else {
throw new TeiidDdlParsingException(tokens, "Unparsable table body constraint");
}
// may have an options clause
parseOptionsClause(tokens, constraintNode);
return true;
}
return false;
}
/**
* <code>
* <table element>
*
* <identifier> <data type> ( NOT NULL )? ( UNIQUE | ( INDEX | AUTO_INCREMENT )+ | ( PRIMARY KEY ) )? ( DEFAULT <string> )? ( <options clause> )?
* </code>
*
* @param tokens the tokens being processed (cannot be <code>null</code>)
* @param tableNode the table node whose body is being processed (cannot be <code>null</code>)
* @return the table element node (never <code>null</code>)
* @throws ParsingException if the table element cannot be parsed
*/
AstNode parseTableElement( final DdlTokenStream tokens,
final AstNode tableNode ) throws ParsingException {
final String id = parseIdentifier(tokens);
final DataType datatype = getDataTypeParser().parse(tokens);
final AstNode columnNode = getNodeFactory().node(id, tableNode, TeiidDdlLexicon.CreateTable.TABLE_ELEMENT);
getDataTypeParser().setPropertiesOnNode(columnNode, datatype);
boolean foundNotNull = false;
boolean foundDefaultClause = false;
boolean foundOptionsClause = false;
boolean foundConstraintType = false;
boolean foundAutoIncrement = false;
// look for 5 optional parts in any order
while (tokens.hasNext()
&& (!foundNotNull || !foundDefaultClause || !foundOptionsClause || !foundConstraintType || !foundAutoIncrement)) {
if (!foundNotNull && tokens.canConsume(NOT_NULL)) {
foundNotNull = true;
} else if (!foundAutoIncrement && tokens.canConsume(TeiidNonReservedWord.AUTO_INCREMENT.toDdl())) {
foundAutoIncrement = true;
} else if (!foundConstraintType
&& (tokens.matches(TeiidReservedWord.UNIQUE.toDdl()) || tokens.matches(INDEX) || tokens.matches(PRIMARY,
KEY))) {
foundConstraintType = true;
String constraintType = null;
if (tokens.matches(PRIMARY, KEY) || tokens.matches(TeiidReservedWord.UNIQUE.toDdl())) {
if (tokens.canConsume(TeiidReservedWord.UNIQUE.toDdl())) {
constraintType = TeiidReservedWord.UNIQUE.toDdl();
} else {
tokens.consume(PRIMARY, KEY);
constraintType = PRIMARY_KEY;
}
// create constraint node
final AstNode constraintNode = getNodeFactory().node(constraintType,
tableNode,
TeiidDdlLexicon.Constraint.TABLE_ELEMENT);
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TYPE, constraintType);
// create a single element list since property is multi-valued
constraintNode.setProperty(TeiidDdlLexicon.Constraint.REFERENCES, Collections.singletonList(columnNode));
} else if (tokens.canConsume(INDEX)) {
constraintType = INDEX;
// create constraint node
final AstNode constraintNode = getNodeFactory().node(constraintType,
tableNode,
TeiidDdlLexicon.Constraint.INDEX_CONSTRAINT);
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TYPE, constraintType);
// create a single element list since property is multi-valued
constraintNode.setProperty(TeiidDdlLexicon.Constraint.REFERENCES, Collections.singletonList(columnNode));
} else {
throw new TeiidDdlParsingException(tokens, "Unparsable table body unnamed constraint");
}
} else if (!foundDefaultClause && parseDefaultClause(tokens, columnNode)) {
foundDefaultClause = true;
} else if (!foundOptionsClause && parseOptionsClause(tokens, columnNode)) {
foundOptionsClause = true;
} else {
break; // none of the optional clauses found
}
}
columnNode.setProperty(TeiidDdlLexicon.CreateTable.AUTO_INCREMENT, foundAutoIncrement);
columnNode.setProperty(StandardDdlLexicon.NULLABLE, (foundNotNull ? "NOT NULL" : "NULL"));
return columnNode;
}
/**
* Process the AstNode tree to look for table references from foreign keys that are not resolved
*/
@Override
protected void postProcess( AstNode rootNode ) {
for (UnresolvedTableReferenceNode node : unresolvedTableReferences) {
final AstNode constraintNode = node.getContraintNode();
final String referencesTableName = node.getTableReferenceName();
final AstNode referencesTableNode = getNode(constraintNode.getParent().getParent(),
referencesTableName,
TeiidDdlLexicon.CreateTable.TABLE_STATEMENT,
TeiidDdlLexicon.CreateTable.VIEW_STATEMENT);
// can't find referenced table
if (referencesTableNode == null) {
this.logger.debug("Create table statment foreign key reference table '{0}' was not found", referencesTableName);
} else {
// Now set the TABLE_REFERENCE property
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TABLE_REFERENCE, referencesTableNode);
final List<AstNode> references = new ArrayList<AstNode>(node.getColumnReferenceNames().size());
// If any column names references exists, go find the real objects and set the TABLE_REFERENCE_REFERENCES property
for (String columnName : node.getColumnReferenceNames()) {
final AstNode referencedColumnNode = getColumnNode(referencesTableNode, columnName);
// can't find referenced column
if (referencedColumnNode == null) {
this.logger.debug("Create table statement node of column reference '{0}' was not found", columnName);
} else {
references.add(referencedColumnNode);
}
}
if (!references.isEmpty()) {
constraintNode.setProperty(TeiidDdlLexicon.Constraint.TABLE_REFERENCE_REFERENCES, references);
}
}
}
unresolvedTableReferences.clear();
}
class UnresolvedTableReferenceNode {
AstNode contraintNode;
String tableReferenceName;
Set<String> columnReferenceNames;
public UnresolvedTableReferenceNode( AstNode node,
String name ) {
this.contraintNode = node;
this.tableReferenceName = name;
this.columnReferenceNames = new HashSet<String>();
}
public AstNode getContraintNode() {
return contraintNode;
}
public String getTableReferenceName() {
return tableReferenceName;
}
public void addColumnReferenceName( String name ) {
this.columnReferenceNames.add(name);
}
public Set<String> getColumnReferenceNames() {
return this.columnReferenceNames;
}
}
}