/
JoinQueryVisitor.java
191 lines (170 loc) · 6.47 KB
/
JoinQueryVisitor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package org.teiid.translator.salesforce.execution.visitors;
import org.teiid.core.util.StringUtil;
import org.teiid.language.AggregateFunction;
import org.teiid.language.ColumnReference;
import org.teiid.language.Comparison;
import org.teiid.language.DerivedColumn;
import org.teiid.language.Expression;
import org.teiid.language.Join;
import org.teiid.language.NamedTable;
import org.teiid.language.TableReference;
import org.teiid.metadata.Column;
import org.teiid.metadata.ForeignKey;
import org.teiid.metadata.RuntimeMetadata;
import org.teiid.metadata.Table;
import org.teiid.translator.TranslatorException;
/**
* Salesforce supports joins only on primary key/foreign key relationships. The connector
* is supporting these joins through the OUTER JOIN syntax. All RIGHT OUTER JOINS are
* rewritten by the query processor as LEFT OUTER JOINS, so that is all this visitor has
* to expect.
*
* Salesforce also requires a different syntax depending upon if you are joining from parent
* to child, or from child to parent.
* http://www.salesforce.com/us/developer/docs/api/index_Left.htm#StartTopic=Content/sforce_api_calls_soql_relationships.htm
*
*/
public class JoinQueryVisitor extends SelectVisitor {
private Table leftTableInJoin;
private Table rightTableInJoin;
private Table childTable;
private String parentName;
private ForeignKey foreignKey;
public JoinQueryVisitor(RuntimeMetadata metadata) {
super(metadata);
}
// Has to be a left outer join of only 2 tables. criteria must be a compare
@Override
public void visit(Join join) {
try {
TableReference left = join.getLeftItem();
NamedTable leftGroup = (NamedTable) left;
leftTableInJoin = leftGroup.getMetadataObject();
loadColumnMetadata(leftGroup);
TableReference right = join.getRightItem();
NamedTable rightGroup = (NamedTable) right;
rightTableInJoin = rightGroup.getMetadataObject();
loadColumnMetadata((NamedTable) right);
Comparison criteria = (Comparison) join.getCondition();
Expression lExp = criteria.getLeftExpression();
Expression rExp = criteria.getRightExpression();
if (isIdColumn(rExp) || isIdColumn(lExp)) {
Column rColumn = ((ColumnReference) rExp).getMetadataObject();
String rTableName = rColumn.getParent().getNameInSource();
Column lColumn = ((ColumnReference) lExp).getMetadataObject();
String lTableName = lColumn.getParent().getNameInSource();
if (leftTableInJoin.getNameInSource().equals(rTableName)
|| leftTableInJoin.getNameInSource().equals(lTableName)
&& rightTableInJoin.getNameInSource().equals(rTableName)
|| rightTableInJoin.getNameInSource().equals(lTableName)
&& !rTableName.equals(lTableName)) {
// This is the join criteria, the one that is the ID is the parent.
Expression fKey = !isIdColumn(lExp) ? lExp : rExp;
ColumnReference columnReference = (ColumnReference) fKey;
table = childTable = (Table)columnReference.getMetadataObject().getParent();
String name = columnReference.getMetadataObject().getNameInSource();
if (StringUtil.endsWithIgnoreCase(name, "id")) {
this.parentName = name.substring(0, name.length() - 2);
}
Table parent = leftTableInJoin;
if (isChildToParentJoin()) {
parent = rightTableInJoin;
}
for (ForeignKey fk : childTable.getForeignKeys()) {
if (fk.getReferenceKey().equals(parent.getPrimaryKey())) {
foreignKey = fk;
break;
}
}
} else {
// Only add the criteria to the query if it is not the join criteria.
// The join criteria is implicit in the salesforce syntax.
super.visit(criteria); //TODO: not valid
}
} else {
super.visit(criteria); //TODO: not valid
}
} catch (TranslatorException ce) {
exceptions.add(ce);
}
}
@Override
public String getQuery() throws TranslatorException {
if (isChildToParentJoin()) {
return super.getQuery();
}
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
StringBuilder select = new StringBuilder();
select.append(SELECT).append(SPACE);
addSelect(leftTableInJoin.getNameInSource(), select, true);
select.append(OPEN);
StringBuilder subselect = new StringBuilder();
subselect.append(SELECT).append(SPACE);
addSelect(rightTableInJoin.getNameInSource(), subselect, false);
subselect.append(SPACE);
subselect.append(FROM).append(SPACE);
String pluralName = null;
if (this.foreignKey != null && this.foreignKey.getNameInSource() != null) {
pluralName = this.foreignKey.getNameInSource();
} else {
pluralName = rightTableInJoin.getNameInSource() + "s"; //$NON-NLS-1$
}
subselect.append(pluralName);
subselect.append(CLOSE).append(SPACE);
select.append(subselect);
select.append(FROM).append(SPACE);
select.append(leftTableInJoin.getNameInSource()).append(SPACE);
addCriteriaString(select);
appendGroupByHaving(select);
select.append(limitClause);
return select.toString();
}
@Override
void appendColumnReference(StringBuilder queryString, ColumnReference ref) {
if (isChildToParentJoin() && this.rightTableInJoin.equals(ref.getMetadataObject().getParent())
&& this.parentName != null) {
//TODO: a self join won't work with this logic
queryString.append(parentName);
queryString.append('.');
queryString.append(ref.getMetadataObject().getNameInSource());
} else {
super.appendColumnReference(queryString, ref);
}
}
public boolean isChildToParentJoin() {
return childTable.equals(leftTableInJoin);
}
void addSelect(String tableNameInSource, StringBuilder result, boolean addComma) {
boolean firstTime = true;
for (DerivedColumn symbol : selectSymbols) {
Expression expression = symbol.getExpression();
if (expression instanceof ColumnReference) {
Column element = ((ColumnReference) expression).getMetadataObject();
String tableName = element.getParent().getNameInSource();
if(!isChildToParentJoin() && !tableNameInSource.equals(tableName)) {
continue;
}
if (!firstTime) {
result.append(", "); //$NON-NLS-1$
} else {
firstTime = false;
}
appendColumnReference(result, (ColumnReference) expression);
} else if (expression instanceof AggregateFunction) {
if (!firstTime) {
result.append(", "); //$NON-NLS-1$
} else {
firstTime = false;
}
appendAggregateFunction(result, (AggregateFunction)expression);
} else {
throw new AssertionError("Unknown select symbol type " + symbol); //$NON-NLS-1$
}
}
if (!firstTime && addComma) {
result.append(", "); //$NON-NLS-1$
}
}
}