private void setRelationalUpdateValues(
      List<ParsedPropertyReplacement> replacementProps,
      FeatureTypeMapping ftMapping,
      PreparedStatement stmt,
      IdFilter filter,
      FIDMapping fidMapping)
      throws SQLException {
    int i = 1;

    for (ParsedPropertyReplacement replacement : replacementProps) {
      Property replacementProp = replacement.getNewValue();
      QName propName = replacementProp.getType().getName();
      Mapping mapping = ftMapping.getMapping(propName);
      if (mapping != null) {
        if (mapping.getJoinedTable() != null && !mapping.getJoinedTable().isEmpty()) {
          continue;
        }

        Object value = replacementProp.getValue();
        if (value != null) {
          ParticleConverter<TypedObjectNode> converter =
              (ParticleConverter<TypedObjectNode>) fs.getConverter(mapping);
          if (mapping instanceof PrimitiveMapping) {
            MappingExpression me = ((PrimitiveMapping) mapping).getMapping();
            if (!(me instanceof DBField)) {
              continue;
            }
            converter.setParticle(stmt, (PrimitiveValue) value, i++);
          } else if (mapping instanceof GeometryMapping) {
            MappingExpression me = ((GeometryMapping) mapping).getMapping();
            if (!(me instanceof DBField)) {
              continue;
            }
            converter.setParticle(stmt, (Geometry) value, i++);
          }
        } else {
          stmt.setObject(i++, null);
        }
      }
    }

    for (String id : filter.getMatchingIds()) {
      IdAnalysis analysis = schema.analyzeId(id);
      int j = i;
      for (String fidKernel : analysis.getIdKernels()) {
        PrimitiveValue value =
            new PrimitiveValue(fidKernel, new PrimitiveType(fidMapping.getColumnType()));
        Object sqlValue = SQLValueMangler.internalToSQL(value);
        stmt.setObject(j++, sqlValue);
      }
      stmt.addBatch();
    }
  }
 /**
  * Inserts the given feature into BLOB table and returns the generated primary key.
  *
  * @param stmt
  * @param feature
  * @return primary key of the feature
  * @throws SQLException
  * @throws FeatureStoreException
  */
 private int insertFeatureBlob(PreparedStatement stmt, Feature feature)
     throws SQLException, FeatureStoreException {
   LOG.debug("Inserting feature with id '" + feature.getId() + "' (BLOB)");
   if (fs.getSchema().getFeatureType(feature.getName()) == null) {
     throw new FeatureStoreException(
         "Cannot insert feature '"
             + feature.getName()
             + "': feature type is not served by this feature store.");
   }
   ICRS crs = blobMapping.getCRS();
   stmt.setString(1, feature.getId());
   stmt.setShort(2, fs.getFtId(feature.getName()));
   byte[] bytes = encodeFeatureBlob(feature, crs);
   stmt.setBytes(3, bytes);
   LOG.debug("Feature blob size: " + bytes.length);
   Geometry bboxGeom = getFeatureEnvelopeAsGeometry(feature);
   blobGeomConverter.setParticle(stmt, bboxGeom, 4);
   stmt.execute();
   int internalId = -1;
   // ResultSet rs = null;
   // try {
   // // TODO only supported for PostgreSQL >= 8.2
   // rs = stmt.getGeneratedKeys();
   // rs.next();
   // internalId = rs.getInt( 1 );
   // } finally {
   // if ( rs != null ) {
   // rs.close();
   // }
   // }
   return internalId;
 }
 private void updateFeatureBlob(final PreparedStatement stmt, final Feature feature)
     throws SQLException {
   LOG.debug("Updating feature with id '" + feature.getId() + "' (BLOB)");
   final ICRS crs = blobMapping.getCRS();
   final byte[] bytes = encodeFeatureBlob(feature, crs);
   stmt.setBytes(1, bytes);
   LOG.debug("Feature blob size: " + bytes.length);
   final Geometry bboxGeom = getFeatureEnvelopeAsGeometry(feature);
   blobGeomConverter.setParticle(stmt, bboxGeom, 2);
   stmt.setString(3, feature.getId());
   stmt.execute();
 }
  private String createRelationalUpdateStatement(
      FeatureTypeMapping ftMapping,
      FIDMapping fidMapping,
      List<ParsedPropertyReplacement> replacementProps,
      List<ResourceId> list)
      throws FilterEvaluationException, FeatureStoreException, SQLException {
    StringBuffer sql = new StringBuffer("UPDATE ");
    sql.append(ftMapping.getFtTable());
    sql.append(" SET ");
    boolean first = true;
    for (ParsedPropertyReplacement replacement : replacementProps) {
      Property replacementProp = replacement.getNewValue();
      QName propName = replacementProp.getType().getName();
      Mapping mapping = ftMapping.getMapping(propName);
      if (mapping != null) {
        if (mapping.getJoinedTable() != null && !mapping.getJoinedTable().isEmpty()) {
          addRelationallyMappedMultiProperty(replacement, mapping, ftMapping, list);
          continue;
        }
        String column = null;
        ParticleConverter<TypedObjectNode> converter =
            (ParticleConverter<TypedObjectNode>) fs.getConverter(mapping);
        if (mapping instanceof PrimitiveMapping) {
          MappingExpression me = ((PrimitiveMapping) mapping).getMapping();
          if (!(me instanceof DBField)) {
            continue;
          }
          column = ((DBField) me).getColumn();
          if (!first) {
            sql.append(",");
          } else {
            first = false;
          }
          sql.append(column);
          sql.append("=");

          // TODO communicate value for non-prepared statement converters
          sql.append(converter.getSetSnippet(null));
        } else if (mapping instanceof GeometryMapping) {
          MappingExpression me = ((GeometryMapping) mapping).getMapping();
          if (!(me instanceof DBField)) {
            continue;
          }
          column = ((DBField) me).getColumn();
          if (!first) {
            sql.append(",");
          } else {
            first = false;
          }
          sql.append(column);
          sql.append("=");
          // TODO communicate value for non-prepared statement converters
          sql.append(converter.getSetSnippet(null));
        } else {
          LOG.warn(
              "Updating of " + mapping.getClass() + " is currently not implemented. Omitting.");
          continue;
        }
      } else {
        LOG.warn("No mapping for update property '" + propName + "'. Omitting.");
      }
    }

    // only property changes in multi properties?
    if (first) {
      return null;
    }

    sql.append(" WHERE ");
    sql.append(fidMapping.getColumns().get(0).first);
    sql.append("=?");
    for (int i = 1; i < fidMapping.getColumns().size(); i++) {
      sql.append(" AND ");
      sql.append(fidMapping.getColumns().get(i));
      sql.append("=?");
    }
    return sql.toString();
  }
  @Override
  public List<String> performInsert(FeatureCollection fc, IDGenMode mode)
      throws FeatureStoreException {

    LOG.debug("performInsert()");

    Set<Geometry> geometries = new LinkedHashSet<Geometry>();
    Set<Feature> features = new LinkedHashSet<Feature>();
    Set<String> fids = new LinkedHashSet<String>();
    Set<String> gids = new LinkedHashSet<String>();
    for (Feature member : fc) {
      findFeaturesAndGeometries(member, geometries, features, fids, gids);
    }

    LOG.debug(features.size() + " features / " + geometries.size() + " geometries");

    for (FeatureInspector inspector : inspectors) {
      for (Feature f : features) {
        // TODO cope with inspectors that return a different instance
        inspector.inspect(f);
      }
    }

    long begin = System.currentTimeMillis();

    String fid = null;
    try {
      PreparedStatement blobInsertStmt = null;
      if (blobMapping != null) {
        switch (mode) {
          case GENERATE_NEW:
            {
              // TODO don't change incoming features / geometries
              for (Feature feature : features) {
                String newFid = "FEATURE_" + generateNewId();
                String oldFid = feature.getId();
                if (oldFid != null) {
                  fids.remove(oldFid);
                }
                fids.add(newFid);
                feature.setId(newFid);
              }
              for (Geometry geometry : geometries) {
                String newGid = "GEOMETRY_" + generateNewId();
                String oldGid = geometry.getId();
                if (oldGid != null) {
                  gids.remove(oldGid);
                }
                gids.add(newGid);
                geometry.setId(newGid);
              }
              break;
            }
          case REPLACE_DUPLICATE:
            {
              throw new FeatureStoreException("REPLACE_DUPLICATE is not available yet.");
            }
          case USE_EXISTING:
            {
              // TODO don't change incoming features / geometries
              for (Feature feature : features) {
                if (feature.getId() == null) {
                  String newFid = "FEATURE_" + generateNewId();
                  feature.setId(newFid);
                  fids.add(newFid);
                }
              }

              for (Geometry geometry : geometries) {
                if (geometry.getId() == null) {
                  String newGid = "GEOMETRY_" + generateNewId();
                  geometry.setId(newGid);
                  gids.add(newGid);
                }
              }
              break;
            }
        }
        StringBuilder sql = new StringBuilder("INSERT INTO ");
        sql.append(blobMapping.getTable());
        sql.append(" (");
        sql.append(blobMapping.getGMLIdColumn());
        sql.append(",");
        sql.append(blobMapping.getTypeColumn());
        sql.append(",");
        sql.append(blobMapping.getDataColumn());
        sql.append(",");
        sql.append(blobMapping.getBBoxColumn());
        sql.append(") VALUES(?,?,?,");
        sql.append(blobGeomConverter.getSetSnippet(null));
        sql.append(")");
        LOG.debug("Inserting: {}", sql);
        blobInsertStmt = conn.prepareStatement(sql.toString());
        for (Feature feature : features) {
          fid = feature.getId();
          if (blobInsertStmt != null) {
            insertFeatureBlob(blobInsertStmt, feature);
          }
          FeatureTypeMapping ftMapping = fs.getMapping(feature.getName());
          if (ftMapping != null) {
            throw new UnsupportedOperationException();
          }
          ICRS storageSrs = blobMapping.getCRS();
          bboxTracker.insert(feature, storageSrs);
        }
        if (blobInsertStmt != null) {
          blobInsertStmt.close();
        }
      } else {
        // pure relational mode
        List<FeatureRow> idAssignments = new ArrayList<FeatureRow>();
        InsertRowManager insertManager = new InsertRowManager(fs, conn, mode);
        for (Feature feature : features) {
          FeatureTypeMapping ftMapping = fs.getMapping(feature.getName());
          if (ftMapping == null) {
            throw new FeatureStoreException(
                "Cannot insert feature of type '"
                    + feature.getName()
                    + "'. No mapping defined and BLOB mode is off.");
          }
          idAssignments.add(insertManager.insertFeature(feature, ftMapping));
          Pair<TableName, GeometryMapping> mapping = ftMapping.getDefaultGeometryMapping();
          if (mapping != null) {
            ICRS storageSrs = mapping.second.getCRS();
            bboxTracker.insert(feature, storageSrs);
          }
        }
        if (insertManager.getDelayedRows() != 0) {
          String msg =
              "After insertion, "
                  + insertManager.getDelayedRows()
                  + " delayed rows left uninserted. Probably a cyclic key constraint blocks insertion.";
          throw new RuntimeException(msg);
        }
        // TODO why is this necessary?
        fids.clear();
        for (FeatureRow assignment : idAssignments) {
          fids.add(assignment.getNewId());
        }
      }
    } catch (Throwable t) {
      String msg = "Error inserting feature: " + t.getMessage();
      LOG.error(msg);
      LOG.trace("Stack trace:", t);
      throw new FeatureStoreException(msg, t);
    }

    long elapsed = System.currentTimeMillis() - begin;
    LOG.debug("Insertion of " + features.size() + " features: " + elapsed + " [ms]");
    return new ArrayList<String>(fids);
  }