[teiid-commits] teiid SVN: r1267 - in trunk/connectors/connector-salesforce/src: main/java/com/metamatrix/connector/salesforce/connection and 4 other directories.
teiid-commits at lists.jboss.org
teiid-commits at lists.jboss.org
Fri Aug 21 14:59:08 EDT 2009
Author: jdoyle
Date: 2009-08-21 14:59:08 -0400 (Fri, 21 Aug 2009)
New Revision: 1267
Added:
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/Constants.java
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/JoinQueryVisitor.java
Modified:
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/SalesforceCapabilities.java
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/SalesforceConnection.java
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/impl/ConnectionImpl.java
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/QueryExecutionImpl.java
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/CriteriaVisitor.java
trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/SelectVisitor.java
trunk/connectors/connector-salesforce/src/test/java/com/metamatrix/connector/salesforce/execution/visitors/TestVisitors.java
Log:
TEIID-202
TEIID-209
Support for SOQL relationship queries via outer joins.
Removed 'ping timeout' from the connection.
Added: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/Constants.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/Constants.java (rev 0)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/Constants.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -0,0 +1,49 @@
+package com.metamatrix.connector.salesforce;
+
+public interface Constants {
+
+ public static final String PICKLIST_TYPE = "picklist"; //$NON-NLS-1$
+
+ public static final String MULTIPICKLIST_TYPE = "multipicklist"; //$NON-NLS-1$
+
+ public static final String COMBOBOX_TYPE = "combobox"; //$NON-NLS-1$
+
+ public static final String ANYTYPE_TYPE = "anyType"; //$NON-NLS-1$
+
+ public static final String REFERENCE_TYPE = "reference"; //$NON-NLS-1$
+
+ public static final String STRING_TYPE = "string"; //$NON-NLS-1$
+
+ public static final String BASE64_TYPE = "base64"; //$NON-NLS-1$
+
+ public static final String BOOLEAN_TYPE = "boolean"; //$NON-NLS-1$
+
+ public static final String CURRENCY_TYPE = "currency"; //$NON-NLS-1$
+
+ public static final String TEXTAREA_TYPE = "textarea"; //$NON-NLS-1$
+
+ public static final String INT_TYPE = "int"; //$NON-NLS-1$
+
+ public static final String DOUBLE_TYPE = "double"; //$NON-NLS-1$
+
+ public static final String PERCENT_TYPE = "percent"; //$NON-NLS-1$
+
+ public static final String PHONE_TYPE = "phone"; //$NON-NLS-1$
+
+ public static final String ID_TYPE = "id"; //$NON-NLS-1$
+
+ public static final String DATE_TYPE = "date"; //$NON-NLS-1$
+
+ public static final String DATETIME_TYPE = "datetime"; //$NON-NLS-1$
+
+ public static final String URL_TYPE = "url"; //$NON-NLS-1$
+
+ public static final String EMAIL_TYPE = "email"; //$NON-NLS-1$
+
+ public static final String RESTRICTED_PICKLIST_TYPE = "restrictedpicklist"; //$NON-NLS-1$
+
+ public static final String RESTRICTED_MULTISELECT_PICKLIST_TYPE = "restrictedmultiselectpicklist"; //$NON-NLS-1$
+
+ public static final String SUPPORTS_QUERY = "Supports Query";
+
+}
Modified: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/SalesforceCapabilities.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/SalesforceCapabilities.java 2009-08-20 13:14:46 UTC (rev 1266)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/SalesforceCapabilities.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -80,4 +80,16 @@
return true;
}
+ @Override
+ public SupportedJoinCriteria getSupportedJoinCriteria() {
+ return SupportedJoinCriteria.KEY;
+ }
+
+ @Override
+ public boolean supportsOuterJoins() {
+ return true;
+ }
+
+
+
}
Modified: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/SalesforceConnection.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/SalesforceConnection.java 2009-08-20 13:14:46 UTC (rev 1266)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/SalesforceConnection.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -64,8 +64,19 @@
}
} catch (NumberFormatException e) {
throw new ConnectorException(Messages.getString("SalesforceConnection.bad.ping.value"));
- }
- connection = new ConnectionImpl(username, password, url, pingInterval, env.getLogger());
+ }
+ //600000 - 10 minutes
+ int timeout = 120000; // two minutes
+ try {
+ String timeoutString = env.getProperties().getProperty("SourceConnectionTimeout");
+ if(null != timeoutString) {
+ timeout = Integer.decode(timeoutString).intValue();
+ }
+ } catch (NumberFormatException e) {
+ throw new ConnectorException(Messages.getString("SalesforceConnection.bad.timeout.value"));
+ }
+
+ connection = new ConnectionImpl(username, password, url, pingInterval, env.getLogger(), timeout);
} catch(Throwable t) {
env.getLogger().logError("SalesforceConnection() ErrorMessage: " + t.getMessage());
if(t instanceof ConnectorException) {
Modified: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/impl/ConnectionImpl.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/impl/ConnectionImpl.java 2009-08-20 13:14:46 UTC (rev 1266)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/connection/impl/ConnectionImpl.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -58,22 +58,18 @@
import com.sforce.soap.partner.SforceServiceLocator;
import com.sforce.soap.partner.SoapBindingStub;
import com.sforce.soap.partner.fault.ApiFault;
+import com.sforce.soap.partner.fault.InvalidQueryLocatorFault;
import com.sforce.soap.partner.fault.InvalidSObjectFault;
import com.sforce.soap.partner.fault.UnexpectedErrorFault;
-import com.sforce.soap.partner.fault.InvalidQueryLocatorFault;
import com.sforce.soap.partner.sobject.SObject;
public class ConnectionImpl {
private SoapBindingStub binding;
private ConnectorLogger logger;
- private long prevTime;
- private long pingInterval;
- public ConnectionImpl(String username, String password, URL url, long pingInterval, ConnectorLogger logger) throws ConnectorException {
- this.pingInterval = pingInterval;
+ public ConnectionImpl(String username, String password, URL url, long pingInterval, ConnectorLogger logger, int timeout) throws ConnectorException {
this.logger = logger;
- login(username, password, url);
- prevTime = System.currentTimeMillis();
+ login(username, password, url, timeout);
}
String getUserName() throws ConnectorException {
@@ -90,7 +86,7 @@
return binding;
}
- private void login(String username, String password, URL url)
+ private void login(String username, String password, URL url, int timeout)
throws ConnectorException {
if (!isAlive()) {
LoginResult loginResult = null;
@@ -108,6 +104,7 @@
CallOptions co = new CallOptions();
co.setClient("RedHat/MetaMatrix/");
binding.setHeader("SforceService", "CallOptions", co);
+ binding.setTimeout(timeout);
loginResult = binding.login(username, password);
} catch (ApiFault ex) {
throw new ConnectorException(ex.getExceptionMessage());
@@ -157,20 +154,15 @@
public boolean isAlive() {
boolean result = true;
- if(null != binding) {
+ if(binding == null) {
+ result = false;
+ } else {
try {
- long currentTime = System.currentTimeMillis();
- if ((currentTime - prevTime)/1000 > pingInterval) {
- prevTime = currentTime;
- binding.getServerTimestamp();
- }
- } catch (UnexpectedErrorFault e) {
+ binding.getServerTimestamp();
+ } catch (Throwable t) {
+ logger.logDetail("Caught Throwable in isAlive", t);
result = false;
- } catch (RemoteException e) {
- result = false;
}
- } else {
- result = false;
}
return result;
}
Modified: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/QueryExecutionImpl.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/QueryExecutionImpl.java 2009-08-20 13:14:46 UTC (rev 1266)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/QueryExecutionImpl.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -27,9 +27,12 @@
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import javax.xml.namespace.QName;
+
import org.apache.axis.message.MessageElement;
import org.teiid.connector.api.ConnectorEnvironment;
import org.teiid.connector.api.ConnectorException;
@@ -39,6 +42,9 @@
import org.teiid.connector.api.ResultSetExecution;
import org.teiid.connector.basic.BasicExecution;
import org.teiid.connector.language.IAggregate;
+import org.teiid.connector.language.IFrom;
+import org.teiid.connector.language.IJoin;
+import org.teiid.connector.language.IQuery;
import org.teiid.connector.language.IQueryCommand;
import org.teiid.connector.metadata.runtime.Element;
import org.teiid.connector.metadata.runtime.RuntimeMetadata;
@@ -46,6 +52,7 @@
import com.metamatrix.connector.salesforce.Messages;
import com.metamatrix.connector.salesforce.Util;
import com.metamatrix.connector.salesforce.connection.SalesforceConnection;
+import com.metamatrix.connector.salesforce.execution.visitors.JoinQueryVisitor;
import com.metamatrix.connector.salesforce.execution.visitors.SelectVisitor;
import com.sforce.soap.partner.QueryResult;
import com.sforce.soap.partner.sobject.SObject;
@@ -63,6 +70,8 @@
private SelectVisitor visitor;
private QueryResult results;
+
+ private List<List<Object>> resultBatch;
// Identifying values
private String connectionIdentifier;
@@ -75,11 +84,11 @@
private String logPreamble;
- private Map<String,Integer> fieldMap;
-
private IQueryCommand query;
- private int i;
+ Map<String, Map<String,Integer>> sObjectToResponseField = new HashMap<String, Map<String,Integer>>();
+
+ private int topResultIndex = 0;
public QueryExecutionImpl(IQueryCommand command, SalesforceConnection connection,
RuntimeMetadata metadata, ExecutionContext context,
@@ -108,7 +117,12 @@
public void execute() throws ConnectorException {
connectorEnv.getLogger().logInfo(
getLogPreamble() + "Incoming Query: " + query.toString());
- visitor = new SelectVisitor(metadata);
+ IFrom from = ((IQuery)query).getFrom();
+ if(from.getItems().get(0) instanceof IJoin) {
+ visitor = new JoinQueryVisitor(metadata);
+ } else {
+ visitor = new SelectVisitor(metadata);
+ }
visitor.visitNode(query);
String finalQuery;
finalQuery = visitor.getQuery().trim();
@@ -118,66 +132,138 @@
results = connection.query(finalQuery, this.context.getBatchSize(), visitor.getQueryAll());
}
+ @SuppressWarnings("unchecked")
@Override
public List next() throws ConnectorException, DataNotAvailableException {
- while (results != null) {
- if (query.getProjectedQuery().getSelect().getSelectSymbols().get(0).getExpression() instanceof IAggregate) {
- List<?> result = Arrays.asList(results.getSize());
- results = null;
- return result;
- }
- if (i < results.getSize()) {
- SObject sObject = results.getRecords(i++);
- org.apache.axis.message.MessageElement[] fields = sObject.get_any();
- if (null == fieldMap) {
- logAndMapFields(fields);
+ List<?> result;
+ if (query.getProjectedQuery().getSelect().getSelectSymbols().get(0)
+ .getExpression() instanceof IAggregate) {
+ result = Arrays.asList(results.getSize());
+ results = null;
+
+ } else {
+ result = getRow(results);
+ }
+ return result;
+ }
+
+ private List<Object> getRow(QueryResult result) throws ConnectorException {
+ List<Object> row;
+ if(null == resultBatch) {
+ loadBatch();
+ }
+ if(resultBatch.size() == topResultIndex) {
+ row = null;
+ } else {
+ row = resultBatch.get(topResultIndex);
+ topResultIndex++;
+ if(resultBatch.size() == topResultIndex) {
+ if(!result.isDone()) {
+ loadBatch();
}
- return extractRowFromFields(sObject, fields);
}
-
- if (results.isDone()) { // no more batches on sf.
- results = null;
- break;
- }
- results = connection.queryMore(results.getQueryLocator());
- i = 0;
+
}
- return null;
+ return row;
}
- private List<Object> extractRowFromFields(SObject sObject, MessageElement[] fields) throws ConnectorException {
- List<Object> row = new ArrayList<Object>();
- for (int j = 0; j < visitor.getSelectSymbolCount(); j++) {
- Element element = visitor.getSelectSymbolMetadata(j);
- Integer index = fieldMap.get(element.getNameInSource());
- // id gets dropped from the result if it is not the
- // first field in the querystring. Add it back in.
- if (null == index) {
- if (element.getNameInSource().equalsIgnoreCase("id")) {
- row.add(sObject.getId());
- } else {
- throw new ConnectorException("SalesforceQueryExecutionImpl.missing.field"
- + element.getNameInSource());
+ private void loadBatch() throws ConnectorException {
+ if(null != resultBatch) { // if we have an old batch, then we have to get new results
+ results = connection.queryMore(results.getQueryLocator());
+ }
+ resultBatch = new ArrayList<List<Object>>();
+
+ for(int resultIndex = 0; resultIndex < results.getSize(); resultIndex++) {
+ SObject sObject = results.getRecords(resultIndex);
+ List<Object[]> result = getObjectData(sObject);
+ for(Iterator<Object[]> i = result.iterator(); i.hasNext(); ) {
+ resultBatch.add(Arrays.asList(i.next()));
}
- } else {
- Object cell;
- cell = getCellDatum(element, fields[index]);
- row.add(cell);
}
}
- return row;
+
+ private List<Object[]> getObjectData(SObject sObject) throws ConnectorException {
+ org.apache.axis.message.MessageElement[] topFields = sObject.get_any();
+ logAndMapFields(sObject.getType(), topFields);
+ List<Object[]> result = new ArrayList<Object[]>();
+ for(int i = 0; i < topFields.length; i++) {
+ QName qName = topFields[i].getType();
+ if(null != qName) {
+ String type = qName.getLocalPart();
+ if(type.equals("sObject")) {
+ SObject parent = (SObject)topFields[i].getObjectValue();
+ result.addAll(getObjectData(parent));
+ } else if(type.equals("QueryResult")) {
+ QueryResult subResult = (QueryResult)topFields[i].getObjectValue();
+ for(int resultIndex = 0; resultIndex < subResult.getSize(); resultIndex++) {
+ SObject subObject = subResult.getRecords(resultIndex);
+ result.addAll(getObjectData(subObject));
+ }
+ }
+ }
+ }
+ return extractDataFromFields(sObject, topFields,result);
+
+ }
+
+ private List<Object[]> extractDataFromFields(SObject sObject,
+ MessageElement[] fields, List<Object[]> result) throws ConnectorException {
+ Map<String,Integer> fieldToIndexMap = sObjectToResponseField.get(sObject.getType());
+ for (int j = 0; j < visitor.getSelectSymbolCount(); j++) {
+ Element element = visitor.getSelectSymbolMetadata(j);
+ if(element.getParent().getNameInSource().equals(sObject.getType())) {
+ Integer index = fieldToIndexMap.get(element.getNameInSource());
+ // id gets dropped from the result if it is not the
+ // first field in the querystring. Add it back in.
+ if (null == index) {
+ if (element.getNameInSource().equalsIgnoreCase("id")) {
+ setValueInColumn(j, sObject.getId(), result);
+ } else {
+ throw new ConnectorException("SalesforceQueryExecutionImpl.missing.field"
+ + element.getNameInSource());
+ }
+ } else {
+ Object cell;
+ cell = getCellDatum(element, fields[index]);
+ setValueInColumn(j, cell, result);
+ }
+ }
+ }
+ return result;
}
+
+ private void setValueInColumn(int columnIndex, Object value, List<Object[]> result) {
+ if(result.isEmpty()) {
+ Object[] row = new Object[visitor.getSelectSymbolCount()];
+ result.add(row);
+ }
+ Iterator<Object[]> iter = result.iterator();
+ while (iter.hasNext()) {
+ Object[] row = iter.next();
+ row[columnIndex] = value;
+ }
+ }
- private void logAndMapFields(org.apache.axis.message.MessageElement[] fields) {
- logFields(fields);
- fieldMap = new HashMap<String, Integer>();
- for (int x = 0; x < fields.length; x++) {
- fieldMap.put(fields[x].getLocalName(), x);
+ /**
+ * Load the map of response field names to index.
+ * @param fields
+ */
+ private void logAndMapFields(String sObjectName,
+ org.apache.axis.message.MessageElement[] fields) {
+ if (!sObjectToResponseField.containsKey(sObjectName)) {
+ logFields(sObjectName, fields);
+ Map<String, Integer> responseFieldToIndexMap;
+ responseFieldToIndexMap = new HashMap<String, Integer>();
+ for (int x = 0; x < fields.length; x++) {
+ responseFieldToIndexMap.put(fields[x].getLocalName(), x);
+ }
+ sObjectToResponseField.put(sObjectName, responseFieldToIndexMap);
}
}
- private void logFields(MessageElement[] fields) {
+ private void logFields(String sObjectName, MessageElement[] fields) {
ConnectorLogger logger = connectorEnv.getLogger();
+ logger.logDetail("SalesForce Object Name = " + sObjectName);
logger.logDetail("FieldCount = " + fields.length);
for(int i = 0; i < fields.length; i++) {
logger.logDetail("Field # " + i + " is " + fields[i].getLocalName());
Modified: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/CriteriaVisitor.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/CriteriaVisitor.java 2009-08-20 13:14:46 UTC (rev 1266)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/CriteriaVisitor.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -277,25 +277,35 @@
Element column = left.getMetadataObject();
String columnName = column.getNameInSource();
StringBuffer queryString = new StringBuffer();
+ queryString.append(column.getParent().getNameInSource());
+ queryString.append('.');
queryString.append(columnName).append(SPACE);
queryString.append(comparisonOperators.get(compCriteria.getOperator()));
queryString.append(' ');
- ILiteral literal = (ILiteral)compCriteria.getRightExpression();
- if (column.getJavaType().equals(Boolean.class)) {
- queryString.append(((Boolean)literal.getValue()).toString());
- } else if (column.getJavaType().equals(java.sql.Timestamp.class)) {
- Timestamp datetime = (java.sql.Timestamp)literal.getValue();
- String value = Util.getSalesforceDateTimeFormat().format(datetime);
- String zoneValue = Util.getTimeZoneOffsetFormat().format(datetime);
- queryString.append(value).append(zoneValue.subSequence(0, 3)).append(':').append(zoneValue.subSequence(3, 5));
- } else if (column.getJavaType().equals(java.sql.Time.class)) {
- String value = Util.getSalesforceDateTimeFormat().format((java.sql.Time)literal.getValue());
- queryString.append(value);
- } else if (column.getJavaType().equals(java.sql.Date.class)) {
- String value = Util.getSalesforceDateFormat().format((java.sql.Date)literal.getValue());
- queryString.append(value);
- } else {
- queryString.append(compCriteria.getRightExpression().toString());
+ IExpression rExp = compCriteria.getRightExpression();
+ if(rExp instanceof ILiteral) {
+ ILiteral literal = (ILiteral)rExp;
+ if (column.getJavaType().equals(Boolean.class)) {
+ queryString.append(((Boolean)literal.getValue()).toString());
+ } else if (column.getJavaType().equals(java.sql.Timestamp.class)) {
+ Timestamp datetime = (java.sql.Timestamp)literal.getValue();
+ String value = Util.getSalesforceDateTimeFormat().format(datetime);
+ String zoneValue = Util.getTimeZoneOffsetFormat().format(datetime);
+ queryString.append(value).append(zoneValue.subSequence(0, 3)).append(':').append(zoneValue.subSequence(3, 5));
+ } else if (column.getJavaType().equals(java.sql.Time.class)) {
+ String value = Util.getSalesforceDateTimeFormat().format((java.sql.Time)literal.getValue());
+ queryString.append(value);
+ } else if (column.getJavaType().equals(java.sql.Date.class)) {
+ String value = Util.getSalesforceDateFormat().format((java.sql.Date)literal.getValue());
+ queryString.append(value);
+ } else {
+ queryString.append(compCriteria.getRightExpression().toString());
+ }
+ } else if(rExp instanceof IElement) {
+ IElement right = (IElement)lExpr;
+ column = left.getMetadataObject();
+ columnName = column.getNameInSource();
+ queryString.append(columnName);
}
criteriaList.add(queryString.toString());
Added: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/JoinQueryVisitor.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/JoinQueryVisitor.java (rev 0)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/JoinQueryVisitor.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -0,0 +1,167 @@
+package com.metamatrix.connector.salesforce.execution.visitors;
+
+import org.teiid.connector.api.ConnectorException;
+import org.teiid.connector.language.IAggregate;
+import org.teiid.connector.language.ICompareCriteria;
+import org.teiid.connector.language.IElement;
+import org.teiid.connector.language.IExpression;
+import org.teiid.connector.language.IFromItem;
+import org.teiid.connector.language.IGroup;
+import org.teiid.connector.language.IJoin;
+import org.teiid.connector.language.ISelectSymbol;
+import org.teiid.connector.metadata.runtime.Element;
+import org.teiid.connector.metadata.runtime.Group;
+import org.teiid.connector.metadata.runtime.RuntimeMetadata;
+
+import com.metamatrix.connector.salesforce.Util;
+
+/**
+ * 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 implements
+ IQueryProvidingVisitor {
+
+ private Group leftTableInJoin;
+ private Group rightTableInJoin;
+ private Group childTable;
+
+ public JoinQueryVisitor(RuntimeMetadata metadata) {
+ super(metadata);
+ }
+
+ // Has to be a left outer join
+ @Override
+ public void visit(IJoin join) {
+ try {
+ IFromItem left = join.getLeftItem();
+ if (left instanceof IGroup) {
+ IGroup leftGroup = (IGroup) left;
+ leftTableInJoin = leftGroup.getMetadataObject();
+ loadColumnMetadata(leftGroup);
+ }
+
+ IFromItem right = join.getRightItem();
+ if (right instanceof IGroup) {
+ IGroup rightGroup = (IGroup) right;
+ rightTableInJoin = rightGroup.getMetadataObject();
+ loadColumnMetadata((IGroup) right);
+ }
+ super.visit(join);
+ } catch (ConnectorException ce) {
+ exceptions.add(ce);
+ }
+
+ }
+
+ @Override
+ public void visit(ICompareCriteria criteria) {
+
+ // Find the criteria that joins the two tables
+ try {
+ IExpression rExp = criteria.getRightExpression();
+ if (rExp instanceof IElement) {
+ IExpression lExp = criteria.getLeftExpression();
+ if (isIdColumn((IExpression) rExp) || isIdColumn(lExp)) {
+
+ Element rColumn = ((IElement) rExp).getMetadataObject();
+ String rTableName = rColumn.getParent().getNameInSource();
+
+ Element lColumn = ((IElement) 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.
+ IExpression fKey = !isIdColumn(lExp) ? lExp : rExp;
+ table = childTable = ((IElement) fKey).getMetadataObject().getParent();
+ } 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);
+ }
+ }
+ } else {
+ super.visit(criteria);
+ }
+ } catch (ConnectorException e) {
+ exceptions.add(e);
+ }
+ }
+
+ @Override
+ public String getQuery() throws ConnectorException {
+
+ if (isParentToChildJoin()) {
+ return super.getQuery();
+ } else {
+ if (!exceptions.isEmpty()) {
+ throw ((ConnectorException) exceptions.get(0));
+ }
+ StringBuffer select = new StringBuffer();
+ select.append(SELECT).append(SPACE);
+ addSelectSymbols(leftTableInJoin.getNameInSource(), select);
+ select.append(COMMA).append(SPACE).append(OPEN);
+
+ StringBuffer subselect = new StringBuffer();
+ subselect.append(SELECT).append(SPACE);
+ addSelectSymbols(rightTableInJoin.getNameInSource(), subselect);
+ subselect.append(SPACE);
+ subselect.append(FROM).append(SPACE);
+ subselect.append(rightTableInJoin.getNameInSource()).append('s');
+ subselect.append(CLOSE).append(SPACE);
+ select.append(subselect);
+ select.append(FROM).append(SPACE);
+ select.append(leftTableInJoin.getNameInSource()).append(SPACE);
+ addCriteriaString(select);
+ select.append(limitClause);
+ Util.validateQueryLength(select);
+ return select.toString();
+ }
+ }
+
+ public boolean isParentToChildJoin() {
+ return childTable.equals(leftTableInJoin);
+ }
+
+ protected void addSelectSymbols(String tableNameInSource, StringBuffer result) throws ConnectorException {
+ boolean firstTime = true;
+ for (ISelectSymbol symbol : selectSymbols) {
+ IExpression expression = symbol.getExpression();
+ if (expression instanceof IElement) {
+ Element element = ((IElement) expression).getMetadataObject();
+ String tableName = element.getParent().getNameInSource();
+ if(!isParentToChildJoin() && tableNameInSource.equals(tableName) ||
+ isParentToChildJoin()) {
+ if (!firstTime) {
+ result.append(", ");
+ } else {
+ firstTime = false;
+ }
+ result.append(tableName);
+ result.append('.');
+ result.append(element.getNameInSource());
+ }
+ } else if (expression instanceof IAggregate) {
+ if (!firstTime) {
+ result.append(", ");
+ } else {
+ firstTime = false;
+ }
+ result.append("count()"); //$NON-NLS-1$
+ }
+ }
+ }
+
+}
Modified: trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/SelectVisitor.java
===================================================================
--- trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/SelectVisitor.java 2009-08-20 13:14:46 UTC (rev 1266)
+++ trunk/connectors/connector-salesforce/src/main/java/com/metamatrix/connector/salesforce/execution/visitors/SelectVisitor.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -31,6 +31,7 @@
import org.teiid.connector.language.IElement;
import org.teiid.connector.language.IExpression;
import org.teiid.connector.language.IFrom;
+import org.teiid.connector.language.IFromItem;
import org.teiid.connector.language.IGroup;
import org.teiid.connector.language.IQuery;
import org.teiid.connector.language.ISelect;
@@ -38,6 +39,7 @@
import org.teiid.connector.metadata.runtime.Element;
import org.teiid.connector.metadata.runtime.RuntimeMetadata;
+import com.metamatrix.connector.salesforce.Constants;
import com.metamatrix.connector.salesforce.Messages;
import com.metamatrix.connector.salesforce.Util;
@@ -47,10 +49,9 @@
private Map<String, Integer> selectSymbolNameToIndex = new HashMap<String, Integer>();
private int selectSymbolCount;
private int idIndex = -1; // index of the ID select symbol.
- private StringBuffer selectSymbols = new StringBuffer();
- private StringBuffer limitClause = new StringBuffer();
- //private StringBuffer orderByClause = new StringBuffer();
-
+ protected List<ISelectSymbol> selectSymbols;
+ protected StringBuffer limitClause = new StringBuffer();
+
public SelectVisitor(RuntimeMetadata metadata) {
super(metadata);
}
@@ -71,10 +72,9 @@
Messages.getString("SelectVisitor.distinct.not.supported")));
}
try {
- List<ISelectSymbol> symbols = select.getSelectSymbols();
- selectSymbolCount = symbols.size();
- Iterator<ISelectSymbol> symbolIter = symbols.iterator();
- boolean firstTime = true;
+ selectSymbols = select.getSelectSymbols();
+ selectSymbolCount = selectSymbols.size();
+ Iterator<ISelectSymbol> symbolIter = selectSymbols.iterator();
int index = 0;
while (symbolIter.hasNext()) {
ISelectSymbol symbol = symbolIter.next();
@@ -94,14 +94,6 @@
if (nameInSource.equalsIgnoreCase("id")) {
idIndex = index;
}
- if (!firstTime) {
- selectSymbols.append(", ");
- } else {
- firstTime = false;
- }
- selectSymbols.append(nameInSource);
- } else if (expression instanceof IAggregate) {
- selectSymbols.append("count()"); //$NON-NLS-1$
}
++index;
}
@@ -114,27 +106,23 @@
public void visit(IFrom from) {
super.visit(from);
try {
- IGroup group = (IGroup) from.getItems().get(0);
- loadColumnMetadata(group);
+ // could be a join here, but if so we do nothing and handle
+ // it in visit(IJoin join).
+ IFromItem fromItem = (IFromItem) from.getItems().get(0);
+ if(fromItem instanceof IGroup) {
+ table = ((IGroup)fromItem).getMetadataObject();
+ String supportsQuery = (String)table.getProperties().get(Constants.SUPPORTS_QUERY);
+ if (!Boolean.valueOf(supportsQuery)) {
+ throw new ConnectorException(table.getNameInSource() + " "
+ + Messages.getString("CriteriaVisitor.query.not.supported"));
+ }
+ loadColumnMetadata((IGroup)fromItem);
+ }
} catch (ConnectorException ce) {
exceptions.add(ce);
}
}
-
-/*
- @Override
- public void visit(IOrderBy orderBy) {
- super.visit(orderBy);
- List items = orderBy.getItems();
- if(items.size() > 1) {
- exceptions.add(new ConnectorException("Salesforce cannot support more than one item in the ORDER BY clause"));
- }
- StringBuffer result = new StringBuffer();
- result.append(ORDER_BY).append(SPACE);
- result.append(items.get(0));
- orderByClause = result;
- }
-*/
+
/*
* The SOQL SELECT command uses the following syntax: SELECT fieldList FROM
* objectType [WHERE The Condition Expression (WHERE Clause)] [ORDER BY]
@@ -147,7 +135,8 @@
}
StringBuffer result = new StringBuffer();
result.append(SELECT).append(SPACE);
- result.append(selectSymbols).append(SPACE);
+ addSelectSymbols(table.getNameInSource(), result);
+ result.append(SPACE);
result.append(FROM).append(SPACE);
result.append(table.getNameInSource()).append(SPACE);
addCriteriaString(result);
@@ -157,6 +146,28 @@
return result.toString();
}
+ protected void addSelectSymbols(String tableNameInSource, StringBuffer result) throws ConnectorException {
+ boolean firstTime = true;
+ for (ISelectSymbol symbol : selectSymbols) {
+ if (!firstTime) {
+ result.append(", ");
+ } else {
+ firstTime = false;
+ }
+ IExpression expression = symbol.getExpression();
+ if (expression instanceof IElement) {
+ Element element = ((IElement) expression).getMetadataObject();
+ String tableName = element.getParent().getNameInSource();
+ result.append(tableName);
+ result.append('.');
+ result.append(element.getNameInSource());
+ } else if (expression instanceof IAggregate) {
+ result.append("count()"); //$NON-NLS-1$
+ }
+ }
+ }
+
+
public int getSelectSymbolCount() {
return selectSymbolCount;
}
Modified: trunk/connectors/connector-salesforce/src/test/java/com/metamatrix/connector/salesforce/execution/visitors/TestVisitors.java
===================================================================
--- trunk/connectors/connector-salesforce/src/test/java/com/metamatrix/connector/salesforce/execution/visitors/TestVisitors.java 2009-08-20 13:14:46 UTC (rev 1266)
+++ trunk/connectors/connector-salesforce/src/test/java/com/metamatrix/connector/salesforce/execution/visitors/TestVisitors.java 2009-08-21 18:59:08 UTC (rev 1267)
@@ -40,37 +40,64 @@
public static FakeMetadataFacade exampleSalesforce() {
// Create models
FakeMetadataObject salesforceModel = FakeMetadataFactory.createPhysicalModel("SalesforceModel"); //$NON-NLS-1$
+
+ // Create Account group
+ FakeMetadataObject accounTable = FakeMetadataFactory.createPhysicalGroup("SalesforceModel.Account", salesforceModel); //$NON-NLS-1$
+ accounTable.putProperty(FakeMetadataObject.Props.NAME_IN_SOURCE, "Account"); //$NON-NLS-1$
+ accounTable.setExtensionProp("Supports Query", Boolean.TRUE.toString()); //$NON-NLS-1$
+ // Create Account Columns
+ String[] acctNames = new String[] {
+ "ID", "Name", "Stuff", "Industry" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ };
+ String[] acctTypes = new String[] {
+ DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING
+ };
- // Create physical groups
- FakeMetadataObject table = FakeMetadataFactory.createPhysicalGroup("SalesforceModel.Account", salesforceModel); //$NON-NLS-1$
- table.putProperty(FakeMetadataObject.Props.NAME_IN_SOURCE, "Account"); //$NON-NLS-1$
- table.setExtensionProp("Supports Query", Boolean.TRUE.toString()); //$NON-NLS-1$
- // Create physical elements
+ List<FakeMetadataObject> acctCols = FakeMetadataFactory.createElements(accounTable, acctNames, acctTypes);
+ acctCols.get(2).putProperty(FakeMetadataObject.Props.NATIVE_TYPE, "multipicklist"); //$NON-NLS-1$
+ acctCols.get(2).putProperty(FakeMetadataObject.Props.SEARCHABLE_COMPARE, false);
+ acctCols.get(2).putProperty(FakeMetadataObject.Props.SEARCHABLE_LIKE, true);
+ // Set name in source on each column
+ String[] accountNameInSource = new String[] {
+ "id", "AccountName", "Stuff", "Industry" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ };
+ for(int i=0; i<2; i++) {
+ FakeMetadataObject obj = acctCols.get(i);
+ obj.putProperty(FakeMetadataObject.Props.NAME_IN_SOURCE, accountNameInSource[i]);
+ }
+
+ // Add all Account to the store
+ FakeMetadataStore store = new FakeMetadataStore();
+ store.addObject(salesforceModel);
+ store.addObject(accounTable);
+ store.addObjects(acctCols);
+
+ // Create Contact group
+ FakeMetadataObject contactTable = FakeMetadataFactory.createPhysicalGroup("SalesforceModel.Contact", salesforceModel); //$NON-NLS-1$
+ contactTable.putProperty(FakeMetadataObject.Props.NAME_IN_SOURCE, "Contact"); //$NON-NLS-1$
+ contactTable.setExtensionProp("Supports Query", Boolean.TRUE.toString()); //$NON-NLS-1$
+ // Create Contact Columns
String[] elemNames = new String[] {
- "AccountID", "Name", "Stuff" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "ContactID", "Name", "AccountId" //$NON-NLS-1$ //$NON-NLS-2$
};
String[] elemTypes = new String[] {
DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING, DataTypeManager.DefaultDataTypes.STRING
};
- List<FakeMetadataObject> cols = FakeMetadataFactory.createElements(table, elemNames, elemTypes);
- cols.get(2).putProperty(FakeMetadataObject.Props.NATIVE_TYPE, "multipicklist"); //$NON-NLS-1$
- cols.get(2).putProperty(FakeMetadataObject.Props.SEARCHABLE_COMPARE, false);
- cols.get(2).putProperty(FakeMetadataObject.Props.SEARCHABLE_LIKE, true);
+ List<FakeMetadataObject> contactCols = FakeMetadataFactory.createElements(contactTable, elemNames, elemTypes);
// Set name in source on each column
- String[] nameInSource = new String[] {
- "id", "AccountName", "Stuff" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ String[] contactNameInSource = new String[] {
+ "id", "ContactName", "accountid" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
};
for(int i=0; i<2; i++) {
- FakeMetadataObject obj = cols.get(i);
- obj.putProperty(FakeMetadataObject.Props.NAME_IN_SOURCE, nameInSource[i]);
+ FakeMetadataObject obj = contactCols.get(i);
+ obj.putProperty(FakeMetadataObject.Props.NAME_IN_SOURCE, contactNameInSource[i]);
}
- // Add all objects to the store
- FakeMetadataStore store = new FakeMetadataStore();
+ // Add all Account to the store
store.addObject(salesforceModel);
- store.addObject(table);
- store.addObjects(cols);
+ store.addObject(contactTable);
+ store.addObjects(contactCols);
// Create the facade from the store
return new FakeMetadataFacade(store);
@@ -82,14 +109,14 @@
IQuery command = (IQuery)translationUtility.parseCommand("select * from Account where Name = 'foo' or Stuff = 'bar'"); //$NON-NLS-1$
SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata());
visitor.visit(command);
- assertEquals("SELECT id, AccountName, Stuff FROM Account WHERE (AccountName = 'foo') OR (Stuff = 'bar')", visitor.getQuery().toString().trim()); //$NON-NLS-1$
+ assertEquals("SELECT Account.id, Account.AccountName, Account.Stuff, Account.Industry FROM Account WHERE (Account.AccountName = 'foo') OR (Account.Stuff = 'bar')", visitor.getQuery().toString().trim()); //$NON-NLS-1$
}
@Test public void testNot() throws Exception {
IQuery command = (IQuery)translationUtility.parseCommand("select * from Account where not (Name = 'foo' and Stuff = 'bar')"); //$NON-NLS-1$
SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata());
visitor.visit(command);
- assertEquals("SELECT id, AccountName, Stuff FROM Account WHERE (AccountName != 'foo') OR (Stuff != 'bar')", visitor.getQuery().toString().trim()); //$NON-NLS-1$
+ assertEquals("SELECT Account.id, Account.AccountName, Account.Stuff, Account.Industry FROM Account WHERE NOT ((Account.AccountName = 'foo') AND (Account.Stuff = 'bar'))", visitor.getQuery().toString().trim()); //$NON-NLS-1$
}
@Test public void testCountStart() throws Exception {
@@ -103,7 +130,28 @@
IQuery command = (IQuery)translationUtility.parseCommand("select * from Account where Name not like '%foo' or Stuff = 'bar'"); //$NON-NLS-1$
SelectVisitor visitor = new SelectVisitor(translationUtility.createRuntimeMetadata());
visitor.visit(command);
- assertEquals("SELECT id, AccountName, Stuff FROM Account WHERE (NOT (Account.AccountName LIKE '%foo')) OR (Stuff = 'bar')", visitor.getQuery().toString().trim()); //$NON-NLS-1$
+ assertEquals("SELECT Account.id, Account.AccountName, Account.Stuff, Account.Industry FROM Account WHERE (NOT (Account.AccountName LIKE '%foo')) OR (Account.Stuff = 'bar')", visitor.getQuery().toString().trim()); //$NON-NLS-1$
}
+
+ // I can't really write this test until I see what teiid is going to pass the connectro based upon the user query.
+ // SELECT Account.Name AS c_0, Contact.Name AS c_1 FROM Contact LEFT OUTER JOIN Account ON Account.Id = Contact.AccountId
+ @Test public void testJoin() throws Exception {
+ IQuery command = (IQuery)translationUtility.parseCommand("SELECT Account.Name, Contact.Name FROM Contact LEFT OUTER JOIN Account ON Account.Id = Contact.AccountId"); //$NON-NLS-1$
+
+ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata());
+ visitor.visit(command);
+ assertEquals("SELECT Account.AccountName, Contact.ContactName FROM Contact", visitor.getQuery().toString().trim()); //$NON-NLS-1$
+ // SELECT Contact.Id, Contact.Name, Account.Name FROM Contact WHERE Account.Industry != 'media'
+ // Looks like using q names will work, but we need to distinguish between the parent and child table somehow cannot send
+ // SELECT Contact.Id, Contact.Name, Account.Name FROM Contact, Account WHERE Account.Industry != 'media'
+ }
+
+ @Test public void testJoin2() throws Exception {
+ IQuery command = (IQuery)translationUtility.parseCommand("SELECT Account.Name, Contact.Name FROM Account LEFT OUTER JOIN Contact ON Account.Id = Contact.AccountId"); //$NON-NLS-1$
+
+ SelectVisitor visitor = new JoinQueryVisitor(translationUtility.createRuntimeMetadata());
+ visitor.visit(command);
+ assertEquals("SELECT Account.AccountName, (SELECT Contact.ContactName FROM Contacts) FROM Account", visitor.getQuery().toString().trim()); //$NON-NLS-1$
+ }
}
More information about the teiid-commits
mailing list