All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.torodb.backend.AbstractReadInterface Maven / Gradle / Ivy

There is a newer version: 0.50.3
Show newest version
/*
 * ToroDB
 * Copyright © 2014 8Kdata Technology (www.8kdata.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see .
 */

package com.torodb.backend;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.torodb.backend.ErrorHandler.Context;
import com.torodb.backend.d2r.ResultSetDocPartResult;
import com.torodb.backend.tables.MetaDocPartTable.DocPartTableFields;
import com.torodb.core.TableRef;
import com.torodb.core.TableRefFactory;
import com.torodb.core.cursors.Cursor;
import com.torodb.core.cursors.EmptyCursor;
import com.torodb.core.cursors.IteratorCursor;
import com.torodb.core.d2r.DocPartResult;
import com.torodb.core.transaction.metainf.MetaCollection;
import com.torodb.core.transaction.metainf.MetaDatabase;
import com.torodb.core.transaction.metainf.MetaDocPart;
import com.torodb.core.transaction.metainf.MetaField;
import com.torodb.kvdocument.values.KvValue;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.jooq.DSLContext;
import org.jooq.lambda.Seq;
import org.jooq.lambda.Unchecked;
import org.jooq.lambda.tuple.Tuple2;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Nonnull;
import javax.inject.Provider;
import javax.inject.Singleton;

/**
 *
 */
@Singleton
@SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
public abstract class AbstractReadInterface implements ReadInterface {

  private final MetaDataReadInterface metaDataReadInterface;
  private final DataTypeProvider dataTypeProvider;
  private final ErrorHandler errorHandler;
  private final SqlHelper sqlHelper;
  private final TableRefFactory tableRefFactory;

  public AbstractReadInterface(MetaDataReadInterface metaDataReadInterface,
      DataTypeProvider dataTypeProvider,
      ErrorHandler errorHandler, SqlHelper sqlHelper, TableRefFactory tableRefFactory) {
    this.metaDataReadInterface = metaDataReadInterface;
    this.dataTypeProvider = dataTypeProvider;
    this.errorHandler = errorHandler;
    this.sqlHelper = sqlHelper;
    this.tableRefFactory = tableRefFactory;
  }

  @Override
  @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"},
      justification =
      "ResultSet is wrapped in a Cursor. It's iterated and closed in caller code")
  public Cursor getCollectionDidsWithFieldEqualsTo(DSLContext dsl,
      MetaDatabase metaDatabase,
      MetaCollection metaCol, MetaDocPart metaDocPart, MetaField metaField, KvValue value)
      throws SQLException {
    assert metaDatabase.getMetaCollectionByIdentifier(metaCol.getIdentifier()) != null;
    assert metaCol.getMetaDocPartByIdentifier(metaDocPart.getIdentifier()) != null;
    assert metaDocPart.getMetaFieldByIdentifier(metaField.getIdentifier()) != null;

    String statement = getReadCollectionDidsWithFieldEqualsToStatement(metaDatabase.getIdentifier(),
        metaDocPart.getIdentifier(), metaField.getIdentifier());
    Connection connection = dsl.configuration().connectionProvider().acquire();
    try {
      PreparedStatement preparedStatement = connection.prepareStatement(statement);
      sqlHelper.setPreparedStatementValue(preparedStatement, 1, metaField.getType(), value);
      return new DefaultDidCursor(errorHandler, preparedStatement.executeQuery());
    } finally {
      dsl.configuration().connectionProvider().release(connection);
    }
  }

  protected abstract String getReadCollectionDidsWithFieldEqualsToStatement(String schemaName,
      String rootTableName,
      String columnName);

  @Override
  @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"},
      justification =
      "ResultSet is wrapped in a Cursor. It's iterated and closed in caller code")
  public Cursor getCollectionDidsWithFieldsIn(DSLContext dsl, MetaDatabase metaDatabase,
      MetaCollection metaCol, MetaDocPart metaDocPart,
      Multimap> valuesMultimap)
      throws SQLException {
    assert metaDatabase.getMetaCollectionByIdentifier(metaCol.getIdentifier()) != null;
    assert metaCol.getMetaDocPartByIdentifier(metaDocPart.getIdentifier()) != null;
    assert valuesMultimap.keySet().stream().allMatch(metafield -> metaDocPart
        .getMetaFieldByIdentifier(metafield.getIdentifier()) != null);

    if (valuesMultimap.size() > 500) {
      @SuppressWarnings("checkstyle:LineLength")
      Stream>, Long>>>> valuesEntriesBatchStream =
          Seq.seq(valuesMultimap.entries().stream())
              .zipWithIndex()
              .groupBy(t -> t.v2 / 500)
              .entrySet()
              .stream();
      Stream>>> valuesEntryBatchStreamOfStream =
          valuesEntriesBatchStream
              .map(e -> e.getValue()
                  .stream()
                  .map(se -> se.v1));
      Stream>> valuesMultimapBatchStream =
          valuesEntryBatchStreamOfStream
              .map(e -> toValuesMultimap(e));
      Stream> didCursorStream =
          valuesMultimapBatchStream
              .map(Unchecked.function(valuesMultimapBatch ->
                  getCollectionDidsWithFieldsInBatch(
                      dsl,
                      metaDatabase,
                      metaCol,
                      metaDocPart,
                      valuesMultimapBatch)));
      Stream didStream = didCursorStream
          .flatMap(cursor -> cursor.getRemaining().stream());

      return new IteratorCursor<>(didStream.iterator());
    }

    return getCollectionDidsWithFieldsInBatch(dsl, metaDatabase, metaCol, metaDocPart,
        valuesMultimap);
  }

  private Multimap> toValuesMultimap(
      Stream>> valueEntryStream) {
    Multimap> valuesMultimap = ArrayListMultimap.create();

    valueEntryStream.forEach(e -> valuesMultimap.put(e.getKey(), e.getValue()));

    return valuesMultimap;
  }

  @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"},
      justification =
      "ResultSet is wrapped in a Cursor. It's iterated and closed in caller code")
  private Cursor getCollectionDidsWithFieldsInBatch(DSLContext dsl,
      MetaDatabase metaDatabase,
      MetaCollection metaCol, MetaDocPart metaDocPart,
      Multimap> valuesMultimap)
      throws SQLException {
    @SuppressWarnings("checkstyle:LineLength")
    Provider>>>> valuesMultimapSortedStreamProvider =
        () -> valuesMultimap.asMap().entrySet().stream()
            .sorted((e1, e2) -> e1.getKey().getIdentifier().compareTo(e2.getKey().getIdentifier()));
    String statement = getReadCollectionDidsWithFieldInStatement(metaDatabase.getIdentifier(),
        metaDocPart.getIdentifier(), valuesMultimapSortedStreamProvider.get()
        .map(e -> new Tuple2(e.getKey().getIdentifier(), e.getValue().size())));
    Connection connection = dsl.configuration().connectionProvider().acquire();
    try {
      PreparedStatement preparedStatement = connection.prepareStatement(statement);
      int parameterIndex = 1;
      Iterator>>> valuesMultimapSortedIterator =
          valuesMultimapSortedStreamProvider.get().iterator();
      while (valuesMultimapSortedIterator.hasNext()) {
        Map.Entry>> valuesMultimapEntry =
            valuesMultimapSortedIterator.next();
        for (KvValue value : valuesMultimapEntry.getValue()) {
          sqlHelper.setPreparedStatementValue(preparedStatement, parameterIndex, valuesMultimapEntry
              .getKey().getType(), value);
          parameterIndex++;
        }
      }
      return new DefaultDidCursor(errorHandler, preparedStatement.executeQuery());
    } finally {
      dsl.configuration().connectionProvider().release(connection);
    }
  }

  protected abstract String getReadCollectionDidsWithFieldInStatement(String schemaName,
      String rootTableName,
      Stream> valuesCountList);

  @Override
  @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"},
      justification = "ResultSet is wrapped in a Cursor>>. It's "
          + "iterated and closed in caller code")
  public Cursor>> getCollectionDidsAndProjectionWithFieldsIn(
      DSLContext dsl, MetaDatabase metaDatabase,
      MetaCollection metaCol, MetaDocPart metaDocPart,
      Multimap> valuesMultimap)
      throws SQLException {
    assert metaDatabase.getMetaCollectionByIdentifier(metaCol.getIdentifier()) != null;
    assert metaCol.getMetaDocPartByIdentifier(metaDocPart.getIdentifier()) != null;
    assert valuesMultimap.keySet().stream().allMatch(metafield -> metaDocPart
        .getMetaFieldByIdentifier(metafield.getIdentifier()) != null);

    Stream>>> valuesBatchStream =
        valuesMultimap.asMap().entrySet().stream()
            .map(e -> new Tuple2>>(e.getKey(), e.getValue()));
    if (valuesMultimap.asMap().entrySet().stream().anyMatch(e -> e.getValue().size() > 500)) {
      valuesBatchStream = valuesBatchStream
          .flatMap(e -> Seq.seq(e.v2.stream())
              .zipWithIndex()
              .groupBy(t -> t.v2 / 500)
              .entrySet()
              .stream()
              .map(se -> toValuesMap(e.v1, se)));
    }
    Stream>>> didProjectionCursorStream =
        valuesBatchStream
            .map(Unchecked.function(mapBatch ->
                getCollectionDidsAndProjectionWithFieldsInBatch(
                    dsl,
                    metaDatabase,
                    metaCol,
                    metaDocPart,
                    mapBatch.v1,
                    mapBatch.v2)));
    Stream>> didProjectionStream =
        didProjectionCursorStream
            .flatMap(cursor -> cursor.getRemaining().stream());

    return new IteratorCursor<>(didProjectionStream.iterator());
  }

  @SuppressWarnings("rawtypes")
  private Tuple2>> toValuesMap(MetaField metaField,
      Entry, Long>>> groupedValuesMap) {
    List collect = groupedValuesMap.getValue().stream()
        .map(e -> (KvValue) e.v1)
        .collect(Collectors.toList());

    return new Tuple2>>(metaField, collect.stream()
        .map(e -> (KvValue) e)
        .collect(Collectors.toList()));
  }

  @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"},
      justification = "ResultSet is wrapped in a Cursor>>. "
          + "It's iterated and closed in caller code")
  private Cursor>> getCollectionDidsAndProjectionWithFieldsInBatch(
      DSLContext dsl, MetaDatabase metaDatabase,
      MetaCollection metaCol, MetaDocPart metaDocPart, MetaField metaField,
      Collection> values)
      throws SQLException {
    String statement = getReadCollectionDidsAndProjectionWithFieldInStatement(metaDatabase
        .getIdentifier(),
        metaDocPart.getIdentifier(), metaField.getIdentifier(), values.size());
    Connection connection = dsl.configuration().connectionProvider().acquire();
    try {
      PreparedStatement preparedStatement = connection.prepareStatement(statement);
      int parameterIndex = 1;
      for (KvValue value : values) {
        sqlHelper.setPreparedStatementValue(preparedStatement, parameterIndex, metaField.getType(),
            value);
        parameterIndex++;
      }
      return new AbstractCursor>>(errorHandler, preparedStatement
          .executeQuery()) {
        @Override
        protected Tuple2> read(ResultSet resultSet) throws SQLException {
          return new Tuple2<>(
              resultSet.getInt(1),
              sqlHelper.getResultSetKvValue(
                metaField.getType(),
                dataTypeProvider.getDataType(metaField.getType()), resultSet, 2
              )
          );
        }
      };
    } finally {
      dsl.configuration().connectionProvider().release(connection);
    }
  }

  protected abstract String getReadCollectionDidsAndProjectionWithFieldInStatement(
      String schemaName,
      String rootTableName,
      String columnName, int valuesCount);

  @Override
  @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"},
      justification =
      "ResultSet is wrapped in a Cursor. It's iterated and closed in caller code")
  public Cursor getAllCollectionDids(DSLContext dsl, MetaDatabase metaDatabase,
      MetaCollection metaCollection)
      throws SQLException {

    MetaDocPart rootDocPart = metaCollection.getMetaDocPartByTableRef(tableRefFactory.createRoot());
    if (rootDocPart == null) {
      return new EmptyCursor<>();
    }

    String statement = getReadAllCollectionDidsStatement(metaDatabase.getIdentifier(), rootDocPart
        .getIdentifier());
    Connection connection = dsl.configuration().connectionProvider().acquire();
    try {
      PreparedStatement preparedStatement = connection.prepareStatement(statement);
      return new DefaultDidCursor(errorHandler, preparedStatement.executeQuery());
    } finally {
      dsl.configuration().connectionProvider().release(connection);
    }
  }

  protected abstract String getReadAllCollectionDidsStatement(String schemaName,
      String rootTableName);

  @Override
  public long countAll(
      @Nonnull DSLContext dsl,
      @Nonnull MetaDatabase database,
      @Nonnull MetaCollection collection
  ) {
    MetaDocPart rootDocPart = collection.getMetaDocPartByTableRef(tableRefFactory.createRoot());
    if (rootDocPart == null) {
      return 0;
    }
    String statement = getReadCountAllStatement(database.getIdentifier(), rootDocPart
        .getIdentifier());
    return sqlHelper.executeStatementWithResult(dsl, statement, Context.FETCH)
        .get(0).into(Long.class);
  }

  protected abstract String getReadCountAllStatement(String schema, String rootTableName);

  @Nonnull
  @Override
  public List getCollectionResultSets(@Nonnull DSLContext dsl,
      @Nonnull MetaDatabase metaDatabase, @Nonnull MetaCollection metaCollection,
      @Nonnull Cursor didCursor, int maxSize) throws SQLException {
    Collection dids = didCursor.getNextBatch(maxSize);
    return getCollectionResultSets(dsl, metaDatabase, metaCollection, dids);
  }

  @Override
  @SuppressFBWarnings(value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"},
      justification =
      "ResultSet is wrapped in a DocPartResult. It's iterated and closed in caller code")
  public List getCollectionResultSets(DSLContext dsl, MetaDatabase metaDatabase,
      MetaCollection metaCollection, Collection dids) throws SQLException {
    ArrayList result = new ArrayList<>();
    Connection connection = dsl.configuration().connectionProvider().acquire();
    try {
      Iterator metaDocPartIterator = metaCollection
          .streamContainedMetaDocParts()
          .sorted(TableRefComparator.MetaDocPart.DESC)
          .iterator();
      while (metaDocPartIterator.hasNext()) {
        MetaDocPart metaDocPart = metaDocPartIterator.next();
        String statament = getDocPartStatament(metaDatabase, metaDocPart, dids);

        PreparedStatement preparedStatement = connection.prepareStatement(statament);
        result.add(new ResultSetDocPartResult(metaDataReadInterface, dataTypeProvider, errorHandler,
            metaDocPart, preparedStatement.executeQuery(), sqlHelper));
      }
    } finally {
      dsl.configuration().connectionProvider().release(connection);
    }
    return result;
  }

  protected abstract String getDocPartStatament(MetaDatabase metaDatabase, MetaDocPart metaDocPart,
      Collection dids);

  @Override
  public int getLastRowIdUsed(DSLContext dsl, MetaDatabase metaDatabase,
      MetaCollection metaCollection, MetaDocPart metaDocPart) {

    String statement = getLastRowIdUsedStatement(metaDatabase, metaDocPart);

    Connection connection = dsl.configuration().connectionProvider().acquire();
    try (PreparedStatement preparedStatement = connection.prepareStatement(statement)) {
      try (ResultSet rs = preparedStatement.executeQuery()) {
        rs.next();
        int maxId = rs.getInt(1);
        if (rs.wasNull()) {
          return -1;
        }
        return maxId;
      }
    } catch (SQLException ex) {
      throw errorHandler.handleException(Context.FETCH, ex);
    } finally {
      dsl.configuration().connectionProvider().release(connection);
    }
  }

  protected abstract String getLastRowIdUsedStatement(MetaDatabase metaDatabase,
      MetaDocPart metaDocPart);

  protected String getPrimaryKeyColumnIdentifier(TableRef tableRef) {
    if (tableRef.isRoot()) {
      return DocPartTableFields.DID.fieldName;
    }
    return DocPartTableFields.RID.fieldName;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy