private static void buildEntityTypes(
      MetadataStore metadataStore, List<EdmSchema.Builder> edmSchemas) {
    for (Schema schema : metadataStore.getSchemaList()) {

      List<EdmEntitySet.Builder> entitySets = new ArrayList<EdmEntitySet.Builder>();
      List<EdmEntityType.Builder> entityTypes = new ArrayList<EdmEntityType.Builder>();

      for (Table table : schema.getTables().values()) {

        KeyRecord primaryKey = table.getPrimaryKey();
        List<KeyRecord> uniques = table.getUniqueKeys();
        if (primaryKey == null && uniques.isEmpty()) {
          LogManager.logDetail(
              LogConstants.CTX_ODATA,
              ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16002, table.getFullName()));
          continue;
        }

        EdmEntityType.Builder entityType =
            EdmEntityType.newBuilder().setName(table.getName()).setNamespace(schema.getName());

        // adding key
        if (primaryKey != null) {
          for (Column c : primaryKey.getColumns()) {
            entityType.addKeys(c.getName());
          }
        } else {
          for (Column c : uniques.get(0).getColumns()) {
            entityType.addKeys(c.getName());
          }
        }

        // adding properties
        for (Column c : table.getColumns()) {
          EdmProperty.Builder property =
              EdmProperty.newBuilder(c.getName())
                  .setType(ODataTypeManager.odataType(c.getDatatype().getRuntimeTypeName()))
                  .setNullable(c.getNullType() == NullType.Nullable);
          if (c.getDatatype()
              .getRuntimeTypeName()
              .equals(DataTypeManager.DefaultDataTypes.STRING)) {
            property.setFixedLength(c.isFixedLength()).setMaxLength(c.getLength()).setUnicode(true);
          }
          entityType.addProperties(property);
        }

        // entity set one for one entity type
        EdmEntitySet.Builder entitySet =
            EdmEntitySet.newBuilder().setName(table.getName()).setEntityType(entityType);

        entityType.setNamespace(schema.getName());
        entitySets.add(entitySet);

        // add enitity types for entity schema
        entityTypes.add(entityType);
      }

      // entity container is holder entity sets, association sets, function imports
      EdmEntityContainer.Builder entityContainer =
          EdmEntityContainer.newBuilder()
              .setName(schema.getName())
              .setIsDefault(false)
              .addEntitySets(entitySets);

      // build entity schema
      EdmSchema.Builder modelSchema =
          EdmSchema.newBuilder()
              .setNamespace(schema.getName())
              .addEntityTypes(entityTypes)
              .addEntityContainers(entityContainer);

      edmSchemas.add(modelSchema);
    }
  }
  @Override
  public EntityList executeSQL(
      Query query,
      List<SQLParam> parameters,
      EdmEntitySet entitySet,
      LinkedHashMap<String, Boolean> projectedColumns,
      QueryInfo queryInfo) {
    ConnectionImpl connection = null;
    try {
      boolean cache = queryInfo != null && this.batchSize > 0;
      if (cache) {
        CacheHint hint = new CacheHint();
        hint.setTtl(this.cacheTime);
        hint.setScope(CacheDirective.Scope.USER);
        hint.setMinRows(Long.valueOf(this.batchSize));
        query.setCacheHint(hint);
      }

      boolean getCount = false;
      if (queryInfo != null) {
        getCount = queryInfo.inlineCount == InlineCount.ALLPAGES;
        if (!getCount && (queryInfo.top != null || queryInfo.skip != null)) {
          if (queryInfo.top != null && queryInfo.skip != null) {
            query.setLimit(new Limit(new Constant(queryInfo.skip), new Constant(queryInfo.top)));
          } else if (queryInfo.top != null) {
            query.setLimit(new Limit(new Constant(0), new Constant(queryInfo.top)));
          }
        }
      }

      connection = getConnection();
      String sessionId = connection.getServerConnection().getLogonResult().getSessionID();

      String skipToken = null;
      if (queryInfo != null && queryInfo.skipToken != null) {
        skipToken = queryInfo.skipToken;
        if (cache) {
          int idx = queryInfo.skipToken.indexOf(DELIMITER);
          sessionId = queryInfo.skipToken.substring(0, idx);
          skipToken = queryInfo.skipToken.substring(idx + 2);
        }
      }
      String sql = query.toString();
      if (cache) {
        sql += " /* " + sessionId + " */"; // $NON-NLS-1$ //$NON-NLS-2$
      }
      LogManager.logDetail(LogConstants.CTX_ODATA, "Teiid-Query:", sql); // $NON-NLS-1$

      final PreparedStatement stmt =
          connection.prepareStatement(
              sql,
              cache ? ResultSet.TYPE_SCROLL_INSENSITIVE : ResultSet.TYPE_FORWARD_ONLY,
              ResultSet.CONCUR_READ_ONLY);
      if (parameters != null && !parameters.isEmpty()) {
        for (int i = 0; i < parameters.size(); i++) {
          stmt.setObject(i + 1, parameters.get(i).value, parameters.get(i).sqlType);
        }
      }

      final ResultSet rs = stmt.executeQuery();

      if (projectedColumns == null) {
        projectedColumns = new LinkedHashMap<String, Boolean>();
        for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
          projectedColumns.put(rs.getMetaData().getColumnLabel(i + 1), Boolean.TRUE);
        }
      }

      EntityList result = new EntityList(invalidCharacterReplacement);

      HashMap<String, EdmProperty> propertyTypes = new HashMap<String, EdmProperty>();

      EdmEntityType entityType = entitySet.getType();
      Iterator<EdmProperty> propIter = entityType.getProperties().iterator();
      while (propIter.hasNext()) {
        EdmProperty prop = propIter.next();
        propertyTypes.put(prop.getName(), prop);
      }

      // skip to the initial position
      int count = 0;
      int skipSize = 0;
      // skip based upon the skip value
      if (getCount && queryInfo.skip != null) {
        skipSize = queryInfo.skip;
      }
      // skip based upon the skipToken
      if (skipToken != null) {
        skipSize += Integer.parseInt(skipToken);
      }
      if (skipSize > 0) {
        count += skip(cache, rs, skipSize);
      }

      // determine the number of records to return
      int size = batchSize;
      int top = Integer.MAX_VALUE;
      if (getCount && queryInfo.top != null) {
        top = queryInfo.top;
        size = top;
        if (batchSize > 0) {
          size = Math.min(batchSize, size);
        }
      } else if (size < 1) {
        size = Integer.MAX_VALUE;
      }

      // build the results
      for (int i = 0; i < size; i++) {
        if (!rs.next()) {
          break;
        }
        count++;
        result.addEntity(rs, propertyTypes, projectedColumns, entitySet);
      }

      // set the count
      if (getCount) {
        if (!cache) {
          while (rs.next()) {
            count++;
          }
        } else {
          rs.last();
          count = rs.getRow();
        }
      }
      result.setCount(count);

      // set the skipToken if needed
      if (cache && result.size() == this.batchSize) {
        int end = skipSize + result.size();
        if (getCount) {
          if (end < Math.min(top, count)) {
            result.setNextToken(nextToken(cache, sessionId, end));
          }
        } else if (rs.next()) {
          result.setNextToken(nextToken(cache, sessionId, end));
          // will force the entry to cache or is effectively a no-op when already cached
          rs.last();
        }
      }
      return result;
    } catch (Exception e) {
      throw new ServerErrorException(e.getMessage(), e);
    } finally {
      if (connection != null) {
        try {
          connection.close();
        } catch (SQLException e) {
        }
      }
    }
  }