/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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.wso2.carbon.dataservices.core;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.databinding.utils.ConverterUtil;
import org.apache.axis2.description.java2wsdl.TypeTable;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.core.multitenancy.SuperTenantCarbonContext;
import org.wso2.carbon.dataservices.common.DBConstants;
import org.wso2.carbon.dataservices.common.DBConstants.DBSFields;
import org.wso2.carbon.dataservices.common.DBConstants.RDBMSEngines;
import org.wso2.carbon.dataservices.common.RDBMSUtils;
import org.wso2.carbon.dataservices.core.engine.DataService;
import org.wso2.carbon.dataservices.core.engine.ExternalParam;
import org.wso2.carbon.dataservices.core.engine.ExternalParamCollection;
import org.wso2.carbon.dataservices.core.internal.DataServicesDSComponent;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.user.core.UserRealm;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.xml.XMLPrettyPrinter;
import org.wso2.securevault.SecretResolver;
import javax.naming.InitialContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.transaction.TransactionManager;
import javax.xml.namespace.QName;
import java.io.*;
import java.net.URL;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Utility class for data services based operations.
*/
public class DBUtils {
private static final Log log = LogFactory.getLog(DBUtils.class);
private static ScheduledExecutorService globalExecutorService = Executors
.newSingleThreadScheduledExecutor();
/** thread local variable to track the status of batch processing */
private static ThreadLocal<Boolean> isBatchProcessing = new ThreadLocal<Boolean>() {
protected synchronized Boolean initialValue() {
return false;
}
};
/** thread local variable to keep the current batch request size */
private static ThreadLocal<Integer> batchRequestCount = new ThreadLocal<Integer>() {
protected synchronized Integer initialValue() {
return 0;
}
};
/** thread local variable to keep the current batch request number, 0 based */
private static ThreadLocal<Integer> batchRequestNumber = new ThreadLocal<Integer>() {
protected synchronized Integer initialValue() {
return 0;
}
};
/** thread local variable to track the current tenant id in service deployment */
private static ThreadLocal<Integer> deploymentTimeTenantId = new ThreadLocal<Integer>() {
protected synchronized Integer initialValue() {
/* default is super tenant id */
return MultitenantConstants.SUPER_TENANT_ID;
}
};
private static HashMap<String, String> conversionTypes = null;
private static HashMap<String, String> xsdSqlTypeMap = null;
/* initialize the conversion types */
static {
conversionTypes = new HashMap<String,String>();
conversionTypes.put(DBConstants.DataTypes.CHAR, "java.lang.String");
conversionTypes.put(DBConstants.DataTypes.STRING, "java.lang.String");
conversionTypes.put(DBConstants.DataTypes.VARCHAR, "java.lang.String");
conversionTypes.put(DBConstants.DataTypes.TEXT, "java.lang.String");
conversionTypes.put(DBConstants.DataTypes.NUMERIC, "java.math.BigDecimal");
conversionTypes.put(DBConstants.DataTypes.DECIMAL, "java.math.BigDecimal");
conversionTypes.put(DBConstants.DataTypes.MONEY, "java.math.BigDecimal");
conversionTypes.put(DBConstants.DataTypes.SMALLMONEY, "java.math.BigDecimal");
conversionTypes.put(DBConstants.DataTypes.BIT, "boolean");
conversionTypes.put(DBConstants.DataTypes.BOOLEAN, "boolean");
conversionTypes.put(DBConstants.DataTypes.TINYINT, "byte");
conversionTypes.put(DBConstants.DataTypes.SMALLINT, "short");
conversionTypes.put(DBConstants.DataTypes.INTEGER, "int");
conversionTypes.put(DBConstants.DataTypes.BIGINT, "long");
conversionTypes.put(DBConstants.DataTypes.REAL, "float");
conversionTypes.put(DBConstants.DataTypes.FLOAT, "double");
conversionTypes.put(DBConstants.DataTypes.DOUBLE, "double");
conversionTypes.put(DBConstants.DataTypes.BINARY, "base64Binary"); /* byte[] */
conversionTypes.put(DBConstants.DataTypes.VARBINARY, "base64Binary"); /* byte[] */
conversionTypes.put(DBConstants.DataTypes.LONG_VARBINARY, "base64Binary"); /* byte [] */
conversionTypes.put(DBConstants.DataTypes.IMAGE, "base64Binary"); /* byte[] */
conversionTypes.put(DBConstants.DataTypes.DATE, "java.sql.Date");
conversionTypes.put(DBConstants.DataTypes.TIME, "java.sql.Time");
conversionTypes.put(DBConstants.DataTypes.TIMESTAMP, "java.sql.Timestamp");
conversionTypes.put(DBConstants.DataTypes.ANYURI, "java.net.URI");
xsdSqlTypeMap = new HashMap<String,String>();
xsdSqlTypeMap.put("string", DBConstants.DataTypes.STRING);
xsdSqlTypeMap.put("boolean", DBConstants.DataTypes.BOOLEAN);
xsdSqlTypeMap.put("int", DBConstants.DataTypes.INTEGER);
xsdSqlTypeMap.put("integer", DBConstants.DataTypes.INTEGER);
xsdSqlTypeMap.put("long", DBConstants.DataTypes.LONG);
xsdSqlTypeMap.put("float", DBConstants.DataTypes.FLOAT);
xsdSqlTypeMap.put("double", DBConstants.DataTypes.DOUBLE);
xsdSqlTypeMap.put("decimal", DBConstants.DataTypes.DECIMAL);
xsdSqlTypeMap.put("dateTime", DBConstants.DataTypes.TIMESTAMP);
xsdSqlTypeMap.put("time", DBConstants.DataTypes.TIME);
xsdSqlTypeMap.put("date", DBConstants.DataTypes.DATE);
xsdSqlTypeMap.put("base64Binary", DBConstants.DataTypes.BINARY);
xsdSqlTypeMap.put("binary", DBConstants.DataTypes.BINARY);
}
/**
* Converts from DS SQL types to Java types, e.g. "STRING" -> "java.lang.String".
*/
public static String getJavaTypeFromSQLType(String sqlType) {
return conversionTypes.get(sqlType);
}
/**
* Converts from XML schema types to DS SQL types, e.g. "string" -> "STRING".
*/
public static String getSQLTypeFromXsdType(String xsdType) {
String sqlType = xsdSqlTypeMap.get(xsdType);
if (sqlType == null) {
sqlType = DBConstants.DataTypes.STRING;
}
return sqlType;
}
public static boolean isBatchProcessing() {
return isBatchProcessing.get();
}
public static void setBatchProcessing(boolean val) {
isBatchProcessing.set(val);
}
public static int getBatchRequestCount() {
return batchRequestCount.get();
}
public static void setBatchRequestCount(int val) {
batchRequestCount.set(val);
}
public static int getBatchRequestNumber() {
return batchRequestNumber.get();
}
public static void setBatchRequestNumber(int val) {
batchRequestNumber.set(val);
}
public static String getUsername(MessageContext msgContext) {
String userName = (String) msgContext.getProperty(
DBConstants.MSG_CONTEXT_USERNAME_PROPERTY);
return userName;
}
/**
* Retrieves the current user's roles.
*/
public static String[] getUserRoles(MessageContext msgContext)
throws DataServiceFault, Exception {
HttpServletRequest request = (HttpServletRequest) msgContext
.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
HttpSession httpSession = request.getSession(false);
String userName = DBUtils.getUsername(msgContext);
RealmService realmService = DataServicesDSComponent.getRealmService();
RegistryService registryService = DataServicesDSComponent.getRegistryService();
/* if session does not exist, return empty array of roles */
if (httpSession == null) {
return new String[0];
}
/* first return the tenant id from the tenant domain */
SuperTenantCarbonContext carbonContext = SuperTenantCarbonContext.getCurrentContext(httpSession);
String tenantDomain = carbonContext.getTenantDomain();
int tenantId = carbonContext.getTenantId();
if (tenantId < 0) {
tenantId = realmService.getTenantManager().getTenantId(tenantDomain);
}
if (tenantId < 0) {
/* the tenant doesn't exist. */
log.error("The tenant doesn't exist. Tenant domain:" + tenantDomain);
throw new DataServiceFault("Access Denied. You are not authorized.");
}
if (!realmService.getTenantManager().isTenantActive(tenantId)) {
/* the tenant is not active. */
log.error("The tenant is not active. Tenant domain:" + tenantDomain);
throw new DataServiceFault("The tenant is not active. Tenant domain:" + tenantDomain);
}
UserRealm realm;
String roles[];
try {
realm = registryService.getUserRealm(tenantId);
roles = realm.getUserStoreManager().getRoleListOfUser(userName);
} catch (Exception e) {
String msg = "Error in retrieving the realm for the tenant id: " + tenantId
+ ", username: " + userName + ". " + e.getMessage();
log.error(msg);
throw new DataServiceFault(msg);
}
return roles;
}
public static boolean isRegistryPath(String path) {
if (path.startsWith(DBConstants.CONF_REGISTRY_PATH_PREFIX) || path.startsWith(DBConstants.GOV_REGISTRY_PATH_PREFIX)) {
return true;
} else {
return false;
}
}
/**
* Creates and returns an InputStream from the file path / http location given.
* @throws DataServiceFault
* @see InputStream
*/
public static InputStream getInputStreamFromPath(String path) throws IOException,
DataServiceFault {
InputStream ins;
if (path.startsWith("http://")) {
/* This is a url file path */
URL url = new URL(path);
ins = url.openStream();
} else if (isRegistryPath(path)) {
try {
RegistryService registryService = DataServicesDSComponent.getRegistryService();
if (registryService == null) {
throw new DataServiceFault("DBUtils.getInputStreamFromPath(): Registry service is not available");
}
Registry registry;
if (path.startsWith(DBConstants.CONF_REGISTRY_PATH_PREFIX)) {
if (path.length() > DBConstants.CONF_REGISTRY_PATH_PREFIX.length()) {
path = path.substring(DBConstants.CONF_REGISTRY_PATH_PREFIX.length());
registry = registryService.getConfigSystemRegistry(getCurrentTenantId());
} else {
throw new DataServiceFault("Empty configuration registry path given");
}
} else {
if (path.length() > DBConstants.GOV_REGISTRY_PATH_PREFIX.length()) {
path = path.substring(DBConstants.GOV_REGISTRY_PATH_PREFIX.length());
registry = registryService.getGovernanceSystemRegistry(getCurrentTenantId());
} else {
throw new DataServiceFault("Empty governance registry path given");
}
}
if (registry.resourceExists(path)) {
Resource serviceResource = registry.get(path);
ins = serviceResource.getContentStream();
} else {
throw new DataServiceFault(
"The given XSLT resource path at '" + path + "' does not exist");
}
} catch (RegistryException e) {
String msg = "Error in retrieving the resource: " + path;
log.error(msg, e);
throw new DataServiceFault(e, msg);
}
} else {
File csvFile = new File(path);
if (path.startsWith("." + File.separator) || path.startsWith(".." + File.separator)) {
/* this is a relative path */
path = csvFile.getAbsolutePath();
}
/* local file */
ins = new FileInputStream(path);
}
return ins;
}
/**
* create a map which maps the column numbers to column names,
* column numbers starts with 1 (1 based).
*/
public static Map<Integer, String> createColumnMappings(String[] header) throws IOException {
Map<Integer, String> mappings = null;
if (header != null) {
mappings = new HashMap<Integer, String>();
/* add mappings: column index -> column name */
for (int i = 0; i < header.length; i++) {
mappings.put(i + 1, header[i]);
}
} else {
mappings = new StringNumberMap();
}
return mappings;
}
/**
* This class represents a Map class which always returns the value same as the key.
*/
private static class StringNumberMap extends AbstractMap<Integer, String> {
public Set<Map.Entry<Integer, String>> entrySet() {
return null;
}
@Override
public String get(Object key) {
return key.toString();
}
}
/**
* Utility method that returns a string which contains the stack trace of the given
* Exception object.
*/
public static String getStacktraceFromException(Throwable e) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(byteOut);
e.printStackTrace(writer);
writer.close();
String message = new String(byteOut.toByteArray());
return message;
}
/**
* Returns the most suitable value for the JDBC Result Set FetchSize property,
* for the DBMS engine of the given JDBC URL.
*/
public static int getOptimalRSFetchSizeForRDBMS(String jdbcUrl) {
if (jdbcUrl == null) {
return 1;
}
String rdbms = RDBMSUtils.getRDBMSEngine(jdbcUrl);
if (rdbms.equals(RDBMSEngines.MYSQL)) {
return Integer.MIN_VALUE;
} else {
return 1;
}
}
/**
* Create a Timestamp object from the given timestamp string.
*/
public static Timestamp getTimestamp(String value) throws ParseException {
return new Timestamp(ConverterUtil.convertToDateTime(value).getTimeInMillis());
}
public static void main(String[] args) throws Exception {
Timestamp ts = getTimestamp("2011-12-01T00:00:00.000+01:00");
System.out.println(ts.toString());
}
/**
* Create a Time object from the given time string.
*/
public static Time getTime(String value) throws ParseException {
return new Time(ConverterUtil.convertToTime(value).getAsCalendar().getTimeInMillis());
}
/**
* Create a Date object from the given date string.
*/
public static Date getDate(String value) {
/* if something goes wrong with converting the value to a date,
* try with dateTime and get the date out it, this is because,
* some service clients send a full date-time string for a date */
try {
return new Date(ConverterUtil.convertToDate(value).getTime());
} catch (Exception e) {
return new Date(ConverterUtil.convertToDateTime(value).getTimeInMillis());
}
}
/**
* Prettify a given XML string
*/
public static String prettifyXML(String xmlContent) {
ByteArrayInputStream byteIn = new ByteArrayInputStream(xmlContent.getBytes());
XMLPrettyPrinter prettyPrinter = new XMLPrettyPrinter(byteIn);
return prettyPrinter.xmlFormat().trim();
}
/**
* Prettify a given XML file
*/
public static void prettifyXMLFile(String filePath) throws IOException {
XMLPrettyPrinter prettyPrinter = new XMLPrettyPrinter(new FileInputStream(filePath));
String prettyXML = prettyPrinter.xmlFormat().trim();
if (prettyXML != null && prettyXML.trim().length() > 0) {
FileUtils.writeStringToFile(new File(filePath), prettyXML);
}
}
/**
* Encode the given string with base64 encoding.
*/
public static String encodeBase64(String value) {
try {
return new String(Base64.encodeBase64(value.getBytes(
DBConstants.DEFAULT_CHAR_SET_TYPE)),
DBConstants.DEFAULT_CHAR_SET_TYPE);
} catch (UnsupportedEncodingException ueo) {
throw new RuntimeException(ueo);
}
}
/**
* Creates an AxisFault.
*/
public static AxisFault createAxisFault(Exception e) {
AxisFault fault;
Throwable cause = e.getCause();
if (cause != null) {
fault = new AxisFault(e.getMessage(), cause);
} else {
fault = new AxisFault(e.getMessage());
}
fault.setDetail(DBUtils.createDSFaultOM(e.getMessage()));
fault.setFaultCode(new QName(DBConstants.WSO2_DS_NAMESPACE,
DataServiceFault.extractFaultCode(e)));
return fault;
}
/**
* Creates OMElement using error details.
*/
public static OMElement createDSFaultOM(String msg) {
OMFactory fac = OMAbstractFactory.getOMFactory();
OMElement ele = fac.createOMElement(new QName(
DBConstants.WSO2_DS_NAMESPACE, DBConstants.DS_FAULT_ELEMENT));
ele.setText(msg);
return ele;
}
public static String evaluateString(String source,
ExternalParamCollection params) throws DataServiceFault {
StringBuilder builder = new StringBuilder();
/* http://www.product.fake/cd/{productCode} */
int leftBracketIndex = source.indexOf('{', 0);
int rightBracketIndex = source.indexOf('}', leftBracketIndex);
if (leftBracketIndex == -1 || rightBracketIndex == -1) {
throw new DataServiceFault("The source string: " + source + " is not parameterized.");
}
String paramName = source.substring(leftBracketIndex + 1, rightBracketIndex);
/* workaround for different character case issues in column names */
paramName = paramName.toLowerCase();
ExternalParam exParam = params.getParam(paramName);
if (exParam == null) {
throw new DataServiceFault("The parameter: " + paramName +
" cannot be found for the source string: " + source);
}
String paramValue = exParam.getValue().getValueAsString();
builder.append(source.subSequence(0, leftBracketIndex));
builder.append(paramValue);
builder.append(source.substring(rightBracketIndex + 1));
return builder.toString();
}
/**
* Schedules a given task for one-time execution using the executer framework.
* @param task The task to be executed
* @param delay The delay in milliseconds for the task to be executed
*/
public static void scheduleTask(Runnable task, long delay) {
globalExecutorService.schedule(task, delay, TimeUnit.MILLISECONDS);
}
/**
* Check the given text is empty or not.
* @param text The text to be checked
* @return true if text is null or trimmed text length is empty, or else false
*/
public static boolean isEmptyString(String text){
if (text != null && text.trim().length() > 0) {
return false;
} else {
return true;
}
}
/**
* Check the given password is encrypted or not, if its encrypted resolve the password.
* @param dataService Data service object
* @param password Password before resolving
* @return Resolved password
*/
public static String resolvePasswordValue(DataService dataService, String password) {
SecretResolver secretResolver = dataService.getSecretResolver();
if (secretResolver != null && secretResolver.isTokenProtected(password)) {
return secretResolver.resolve(password);
} else {
return password;
}
}
public static void setDeploymentTimeTenantId(int tenantId) {
deploymentTimeTenantId.set(tenantId);
}
public static int getDeploymentTimeTenantId() {
return deploymentTimeTenantId.get();
}
/**
* Returns the best effort way of finding the current tenant id,
* even if this is not in a current message request, i.e. deploying services.
* Assumption: when tenants other than the super tenant is activated,
* the registry service must be available. So, the service deployment and accessing the registry,
* will happen in the same thread, without the callbacks being used.
* @return The tenant id
*/
public static int getCurrentTenantId() {
int tenantId = CarbonContext.getCurrentContext().getTenantId();
if (tenantId == -1) {
tenantId = getDeploymentTimeTenantId();
}
return tenantId;
}
/**
* Returns the simple schema type from the type name,
*/
public static QName getSimpleSchemaTypeName(TypeTable typeTable, String typeName) {
if (typeName.equals("java.net.URI")) {
return new QName(DBConstants.XSD_NAMESPACE, "anyURI");
}
return typeTable.getSimpleSchemaTypeName(typeName);
}
@SuppressWarnings("unchecked")
public static Map<String, String> extractProperties(OMElement propsParentEl) {
Map<String, String> properties = new HashMap<String, String>();
OMElement propEl = null;
Iterator<OMElement> itr = propsParentEl.getChildrenWithName(new QName(DBSFields.PROPERTY));
String text;
while (itr.hasNext()) {
propEl = itr.next();
if (propEl.getChildElements().hasNext()) {
text = propEl.toString();
} else {
text = propEl.getText();
}
properties.put(propEl.getAttributeValue(new QName(DBSFields.NAME)), text);
}
return properties;
}
/**
* Get the container managed transaction manager; if a JNDI name is given,
* that name is looked for a TransactionManager object, if not, the standard JNDI
* names are checked.
* @param txManagerJNDIName The user given JNDI name of the TransactionManager
* @return The TransactionManager object
* @throws DataServiceFault
*/
public static TransactionManager getContainerTransactionManager(String txManagerJNDIName)
throws DataServiceFault {
TransactionManager txManager = null;
if (txManagerJNDIName != null) {
try {
txManager = InitialContext.doLookup(txManagerJNDIName);
} catch (Exception e) {
throw new DataServiceFault(e,
"Cannot find TransactionManager with the given JNDI name '" +
txManagerJNDIName + "'");
}
}
if (txManager == null) {
try {
Object txObj = InitialContext.doLookup(
DBConstants.STANDARD_USER_TRANSACTION_JNDI_NAME);
if (txObj instanceof TransactionManager) {
txManager = (TransactionManager) txObj;
}
} catch (Exception ignore) {
/* move onto next step */
}
if (txManager == null) {
try {
txManager = InitialContext.doLookup(
DBConstants.STANDARD_TRANSACTION_MANAGER_JNDI_NAME);
} catch (Exception e) {
throw new DataServiceFault(e,
"Cannot find container managed TransactionManager");
}
}
}
return txManager;
}
/**
* Creates a new OMElement from the given element and build it and return.
* @param result The object to be cloned and built
* @return The new cloned and built OMElement
*/
public static OMElement cloneAndReturnBuiltElement(OMElement result) {
StAXOMBuilder builder = new StAXOMBuilder(result.getXMLStreamReaderWithoutCaching());
result = builder.getDocumentElement();
result.build();
return result;
}
/**
* This util method is used to retrieve the string tokens resides in a particular
* udt parameter.
* @param param Name of the parameter
* @return
*/
public static Queue<String> getTokens(String param) {
boolean isString = false;
Queue<String> tokens = new LinkedBlockingQueue<String>();
char[] chars = param.toCharArray();
StringBuilder columnName = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
Character c = chars[i];
if (!".".equals(c.toString()) && !"[".equals(c.toString()) &&
!"]".equals(c.toString())) {
isString = true;
columnName.append(c.toString());
if (i == chars.length - 1) {
tokens.add(columnName.toString());
}
} else {
if (isString) {
tokens.add(columnName.toString());
columnName = new StringBuilder();
isString = false;
}
tokens.add(c.toString());
}
}
return tokens;
}
/**
* This method is used to embed syntaxes associated with UDT attribute notations to
* a queue of string tokens extracted from a UDT parameter.
* @param tokens Queue of string tokens
* @param syntaxQueue Syntax embedded tokens
* @param isIndex Flag to determine whether a particular string token is an inidex
* or a column name
*/
public static void getSyntaxEmbeddedQueue (Queue<String> tokens, Queue<String> syntaxQueue,
boolean isIndex) {
if (!tokens.isEmpty()) {
if ("[".equals(tokens.peek())) {
isIndex = true;
tokens.poll();
syntaxQueue.add("INEDX_START");
syntaxQueue.add(tokens.poll());
} else if ("]".equals(tokens.peek())) {
isIndex = false;
tokens.poll();
syntaxQueue.add("INDEX_END");
} else if (".".equals(tokens.peek())) {
tokens.poll();
syntaxQueue.add("DOT");
syntaxQueue.add("COLUMN");
syntaxQueue.add(tokens.poll());
} else {
if (isIndex) {
syntaxQueue.add("INDEX");
syntaxQueue.add(tokens.poll());
} else {
syntaxQueue.add("COLUMN");
syntaxQueue.add(tokens.poll());
}
}
getSyntaxEmbeddedQueue(tokens, syntaxQueue, isIndex);
}
}
}