/**
  * Method to return the SQLText for an UPDATE TABLE statement. Returns the SQLText for <code>
  * UPDATE T1 SET x1 = val1, x2 = val2 FROM MYTBL T1</code>. Override if the datastore doesn't
  * support that standard syntax.
  *
  * @param tbl The primary table
  * @param setSQL The SQLText for the SET component
  * @return SQLText for the update statement
  */
 public SQLText getUpdateTableStatement(SQLTable tbl, SQLText setSQL) {
   SQLText sql = new SQLText("UPDATE ").append(tbl.getAlias().toString()); // "UPDATE T1"
   sql.append(" ").append(setSQL); // " SET x1 = val1, x2 = val2"
   sql.append(" FROM ").append(tbl.toString()); // " FROM MYTBL T1"
   return sql;
 }
  /**
   * Method to return an expression for Map.containsValue using INNER JOIN to the element. This is
   * only for use when there are no "!containsValue" and no "OR" operations. Creates SQL by adding
   * INNER JOIN to the join table (where it exists), and also to the value table adding an AND
   * condition on the value (with value of the valueExpr). Returns a BooleanExpression "TRUE" (since
   * the INNER JOIN will guarantee if the value is contained of not).
   *
   * @param mapExpr Map expression
   * @param valExpr Expression for the value
   * @return Contains expression
   */
  protected SQLExpression containsAsInnerJoin(MapExpression mapExpr, SQLExpression valExpr) {
    boolean valIsUnbound = (valExpr instanceof UnboundExpression);
    String varName = null;
    String valAlias = null;
    if (valIsUnbound) {
      varName = ((UnboundExpression) valExpr).getVariableName();
      NucleusLogger.QUERY.debug(
          "map.containsValue("
              + valExpr
              + ") binding unbound variable "
              + varName
              + " using INNER JOIN");
      // TODO What if the variable is declared as a subtype, handle this see
      // CollectionContainsMethod
    } else if (!stmt.getQueryGenerator().hasExplicitJoins()) {
      JoinType joinType = stmt.getJoinTypeForTable(valExpr.getSQLTable());
      if (joinType == JoinType.CROSS_JOIN) {
        // Value is currently joined via CROSS JOIN, so remove it (and use INNER JOIN below)
        valAlias = stmt.removeCrossJoin(valExpr.getSQLTable());
        valIsUnbound = true;
        NucleusLogger.QUERY.debug(
            "map.containsValue("
                + valExpr
                + ") was previously bound as CROSS JOIN but changing to INNER JOIN");
      }

      // TODO If owner is joined via CROSS JOIN and value is already present then remove CROSS JOIN
      // and join via INNER JOIN
    }

    RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
    MetaDataManager mmgr = storeMgr.getMetaDataManager();
    AbstractMemberMetaData mmd = mapExpr.getJavaTypeMapping().getMemberMetaData();
    AbstractClassMetaData valCmd = mmd.getMap().getValueClassMetaData(clr, mmgr);
    if (mmd.getMap().getMapType() == MapType.MAP_TYPE_JOIN) {
      // Map formed in join table - add join to join table, then to value table (if present)
      MapTable mapTbl = (MapTable) storeMgr.getTable(mmd);
      SQLTable joinSqlTbl =
          stmt.innerJoin(
              mapExpr.getSQLTable(),
              mapExpr.getSQLTable().getTable().getIdMapping(),
              mapTbl,
              null,
              mapTbl.getOwnerMapping(),
              null,
              null);
      if (valCmd != null) {
        if (valIsUnbound) {
          DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr);
          SQLTable valSqlTbl =
              stmt.innerJoin(
                  joinSqlTbl,
                  mapTbl.getValueMapping(),
                  valTbl,
                  valAlias,
                  valTbl.getIdMapping(),
                  null,
                  null);

          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, valSqlTbl, valSqlTbl.getTable().getIdMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping());
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      } else {
        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, joinSqlTbl, mapTbl.getValueMapping());
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      }
    } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_KEY_IN_VALUE) {
      // Map formed in value table - add join to value table
      DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr);
      JavaTypeMapping ownerMapping = null;
      if (mmd.getMappedBy() != null) {
        ownerMapping = valTbl.getMemberMapping(valCmd.getMetaDataForMember(mmd.getMappedBy()));
      } else {
        ownerMapping = valTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
      }
      SQLTable valSqlTbl =
          stmt.innerJoin(
              mapExpr.getSQLTable(),
              mapExpr.getSQLTable().getTable().getIdMapping(),
              valTbl,
              valAlias,
              ownerMapping,
              null,
              null);

      if (valIsUnbound) {
        // Bind the variable in the QueryGenerator
        valExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
        stmt.getQueryGenerator()
            .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
      } else {
        // Add restrict to value
        SQLExpression valIdExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
        stmt.whereAnd(valIdExpr.eq(valExpr), true);
      }
    } else if (mmd.getMap().getMapType() == MapType.MAP_TYPE_VALUE_IN_KEY) {
      // Map formed in key table - add join to key table then to value table
      AbstractClassMetaData keyCmd = mmd.getMap().getKeyClassMetaData(clr, mmgr);
      DatastoreClass keyTbl = storeMgr.getDatastoreClass(keyCmd.getFullClassName(), clr);
      AbstractMemberMetaData keyValMmd =
          keyCmd.getMetaDataForMember(mmd.getValueMetaData().getMappedBy());
      JavaTypeMapping ownerMapping = null;
      if (mmd.getMappedBy() != null) {
        ownerMapping = keyTbl.getMemberMapping(keyCmd.getMetaDataForMember(mmd.getMappedBy()));
      } else {
        ownerMapping = keyTbl.getExternalMapping(mmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
      }
      SQLTable keySqlTbl =
          stmt.innerJoin(
              mapExpr.getSQLTable(),
              mapExpr.getSQLTable().getTable().getIdMapping(),
              keyTbl,
              null,
              ownerMapping,
              null,
              null);

      if (valCmd != null) {
        DatastoreClass valTbl = storeMgr.getDatastoreClass(valCmd.getFullClassName(), clr);
        SQLTable valSqlTbl =
            stmt.innerJoin(
                keySqlTbl,
                keyTbl.getMemberMapping(keyValMmd),
                valTbl,
                valAlias,
                valTbl.getIdMapping(),
                null,
                null);

        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
          stmt.getQueryGenerator()
              .bindVariable(varName, valCmd, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, valSqlTbl, valTbl.getIdMapping());
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      } else {
        if (valIsUnbound) {
          // Bind the variable in the QueryGenerator
          valExpr = exprFactory.newExpression(stmt, keySqlTbl, keyTbl.getMemberMapping(keyValMmd));
          stmt.getQueryGenerator()
              .bindVariable(varName, null, valExpr.getSQLTable(), valExpr.getJavaTypeMapping());
        } else {
          // Add restrict to value
          SQLExpression valIdExpr =
              exprFactory.newExpression(stmt, keySqlTbl, keyTbl.getMemberMapping(keyValMmd));
          stmt.whereAnd(valIdExpr.eq(valExpr), true);
        }
      }
    }

    JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
    return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
  }
 /**
  * Method to return the basic SQL for a DELETE TABLE statement. Returns the String as <code>
  * DELETE MYTABLE FROM MYTABLE t1</code>.
  *
  * @param tbl The SQLTable to delete
  * @return The delete table string
  */
 public String getDeleteTableStatement(SQLTable tbl) {
   return "DELETE " + tbl.getAlias() + " FROM " + tbl.toString();
 }