protected function processReferences(SystemTableMetaModelLoaderCallContext $callcontext, DataSourceMetaData $datasource) { $foreignKeyConstraintCount = 0; $constraintsProperties = $this->loadReferenceConstraintsProperties($callcontext, $datasource); if (isset($constraintsProperties)) { $constraintsColumnsPropertyFormatter = new QueryKeyResultFormatter(array(self::CN_TABLE_OWNER, self::CN_OBJECT_NAME), FALSE); $constraintsColumnsProperties = $constraintsColumnsPropertyFormatter->formatRecords( $this->loadConstraintColumnsProperties( $callcontext, $datasource, array(self::CONSTRAINT_TYPE__PRIMARY_KEY, self::CONSTRAINT_TYPE__REFERENCE))); foreach ($constraintsProperties as $constraintProperties) { $tableOwner = $this->adjustOwnerName($constraintProperties[self::CN_TABLE_OWNER]); // ---------- preparing referring constraint properties $constraintName = $constraintProperties[self::CN_OBJECT_NAME]; $constraintAccessKey = ArrayHelper::prepareCompositeKey(array($tableOwner, $constraintName)); // for some reason we do not have access to column configuration for referring constraint if (!isset($constraintsColumnsProperties[$constraintAccessKey])) { continue; } $constraintColumnsProperties = $constraintsColumnsProperties[$constraintAccessKey]; // we do not support composite references yet if (count($constraintColumnsProperties) > 1) { continue; } // ----------- preparing referenced constraint properties $refConstraintName = $constraintProperties[self::CN_REFERENCED_OBJECT_NAME]; $refConstraintAccessKey = ArrayHelper::prepareCompositeKey(array($tableOwner, $refConstraintName)); // for some reason we do not have access to column configuration for referenced constraint if (!isset($constraintsColumnsProperties[$refConstraintAccessKey])) { continue; } $constraintColumnProperties = $constraintColumnsProperties[0]; $tableName = $this->adjustTableName($constraintColumnProperties[self::CN_TABLE_NAME]); $tableAccessKey = ArrayHelper::prepareCompositeKey(array($tableOwner, $tableName)); if (!isset($callcontext->datasets[$tableAccessKey])) { continue; } $refConstraintColumnProperties = $constraintsColumnsProperties[$refConstraintAccessKey][0]; $refTableName = $this->adjustTableName($refConstraintColumnProperties[self::CN_TABLE_NAME]); $refTableAccessKey = ArrayHelper::prepareCompositeKey(array($tableOwner, $refTableName)); if (!isset($callcontext->datasets[$refTableAccessKey])) { continue; } $columnName = $this->adjustColumnName($constraintColumnProperties[self::CN_COLUMN_NAME]); $refColumnName = $this->adjustColumnName($refConstraintColumnProperties[self::CN_COLUMN_NAME]); if ($tableName == $refTableName) { LogHelper::log_warn(t( "Self-reference is not supported yet: @tableName(@columnName)", array('@tableName' => $tableName, '@columnName' => $columnName))); continue; } $dataset = $callcontext->datasets[$tableAccessKey]; $column = $dataset->findColumn($columnName); if (!isset($column)) { continue; } $refDataset = $callcontext->datasets[$refTableAccessKey]; $refColumn = $refDataset->findColumn($refColumnName); if (!isset($refColumn)) { continue; } $logicalApplicationType = ReferencePathHelper::assembleReference($refDataset->name, $refColumnName); if (isset($column->type->logicalApplicationType)) { LogHelper::log_warn(t( "Multi-reference is not supported yet for '@columnName' column from '@tableName' table: [@referenceExisting, @referenceNew]", array( '@tableName' => $tableName, '@columnName' => $columnName, '@referenceExisting' => $column->type->logicalApplicationType, '@referenceNew' => $logicalApplicationType))); continue; } $column->type->logicalApplicationType = $logicalApplicationType; $foreignKeyConstraintCount++; } } LogHelper::log_info(t( 'Processed system meta data about @constraintCount foreign key constraint(s)', array('@constraintCount' => $foreignKeyConstraintCount))); }
public function insertOrUpdateOrDeleteDatasetRecords(DataControllerCallContext $callcontext, AbstractDatasetManipulationRequest $request) { $environment_metamodel = data_controller_get_environment_metamodel(); $metamodel = data_controller_get_metamodel(); $datasourceQueryHandler = DataSourceQueryFactory::getInstance()->getHandler($this->getDataSourceType()); $dataset = $metamodel->getDataset($request->datasetName); $datasource = $environment_metamodel->getDataSource($dataset->datasourceName); $recordMetaData = isset($request->recordsHolder->recordMetaData) ? $request->recordsHolder->recordMetaData : $dataset; $isRecordIndexed = $request->recordsHolder instanceof IndexedRecordsHolder; $keyColumnNames = $recordMetaData->getKeyColumnNames(); $nonkeyColumnNames = $recordMetaData->findNonKeyColumnNames(); // preparing a request to find existing records $queryRequest = new DatasetQueryRequest($request->datasetName); // loading only key columns from database $queryRequest->addColumns($keyColumnNames); // a request can be more efficient for single column key if (count($keyColumnNames) == 1) { list($keyColumnIndex, $keyColumnName) = each($keyColumnNames); $keyColumnIdentifier = $isRecordIndexed ? $keyColumnIndex : $keyColumnName; $keyValues = NULL; foreach ($request->recordsHolder->records as $record) { ArrayHelper::addUniqueValue($keyValues, $record->getColumnValue($keyColumnIdentifier, TRUE)); } $queryRequest->addQueryValue( 0, $keyColumnName, OperatorFactory::getInstance()->initiateHandler(EqualOperatorHandler::OPERATOR__NAME, array($keyValues))); } else { for ($i = 0, $count = count($request->recordsHolder->records); $i < $count; $i++) { $record = $request->recordsHolder->records[$i]; foreach ($keyColumnNames as $keyColumnIndex => $keyColumnName) { $keyColumnIdentifier = $isRecordIndexed ? $keyColumnIndex : $keyColumnName; $keyColumnValue = $record->getColumnValue($keyColumnIdentifier, TRUE); $queryRequest->addQueryValue( $i, $keyColumnName, OperatorFactory::getInstance()->initiateHandler(EqualOperatorHandler::OPERATOR__NAME, $keyColumnValue)); } } } // loading existing records ... if any $existingRecordFormatter = new QueryKeyResultFormatter($keyColumnNames); $existingRecords = $existingRecordFormatter->formatRecords($datasourceQueryHandler->queryDataset($callcontext, $queryRequest)); // sorting out records for insert, update and delete operations $keyedRecords = $insertedRecordKeys = $updatedRecordKeys = $deletedRecordKeys = NULL; foreach ($request->recordsHolder->records as $record) { $keyParts = NULL; foreach ($keyColumnNames as $keyColumnIndex => $keyColumnName) { $keyColumnIdentifier = $isRecordIndexed ? $keyColumnIndex : $keyColumnName; $keyParts[] = $record->getColumnValue($keyColumnIdentifier, TRUE); } $key = ArrayHelper::prepareCompositeKey($keyParts); $keyedRecords[$key] = $record; // checking if the record has to be deleted $isDeletable = TRUE; if (isset($nonkeyColumnNames)) { foreach ($nonkeyColumnNames as $columnIndex => $columnName) { $columnIdentifier = $isRecordIndexed ? $columnIndex : $columnName; if ($record->getColumnValue($columnIdentifier) != NULL) { $isDeletable = FALSE; break; } } } else { // the dataset has NO non-key columns. We should not delete these records $isDeletable = FALSE; } if ($isDeletable) { unset($insertedRecordKeys[$key]); unset($updatedRecordKeys[$key]); // the record physically present in database and needs to be deleted if (isset($existingRecords[$key])) { unset($existingRecords[$key]); $deletedRecordKeys[$key] = TRUE; } } elseif (isset($insertedRecordKeys[$key])) { // the key has been already used to insert a record within this batch. This record needs to be part of update operation $updatedRecordKeys[$key] = TRUE; } elseif (isset($existingRecords[$key])) { $updatedRecordKeys[$key] = TRUE; } else { $insertedRecordKeys[$key] = TRUE; } } $sqls = NULL; // deleting existing records $deletedRecordCount = 0; if (isset($deletedRecordKeys)) { $deleteRecordHolder = $this->prepareRecordHolder($request, $keyedRecords, $deletedRecordKeys); // preparing request $deleteRequest = new DatasetDeleteRequest($request->datasetName, $deleteRecordHolder); // preparing statements to delete records from the database ArrayHelper::appendValue($sqls, $this->prepareDeleteDatasetRecordStatements($callcontext, $deleteRequest)); $deletedRecordCount = count($deleteRecordHolder->records); } // inserting new records $insertedRecordCount = 0; if (isset($insertedRecordKeys)) { $insertRecordHolder = $this->prepareRecordHolder($request, $keyedRecords, $insertedRecordKeys); // preparing request $insertRequest = new DatasetInsertRequest($request->datasetName, $insertRecordHolder); // preparing statements to insert records into the database ArrayHelper::appendValue($sqls, $this->prepareInsertDatasetRecordStatements($callcontext, $insertRequest)); $insertedRecordCount = count($insertRecordHolder->records); } // updating existing records $updatedRecordCount = 0; if (isset($updatedRecordKeys)) { $updateRecordHolder = $this->prepareRecordHolder($request, $keyedRecords, $updatedRecordKeys); // preparing request $updateRequest = new DatasetUpdateRequest($request->datasetName, $updateRecordHolder); // preparing statements to update records in the database ArrayHelper::appendValue($sqls, $this->prepareUpdateDatasetRecordStatements($callcontext, $updateRequest)); $updatedRecordCount = count($updateRecordHolder->records); } $affectedRecordCount = isset($sqls) ? $this->executeManipulationStatementBatch($datasource, $sqls) : 0; if (($insertedRecordCount + $updatedRecordCount + $deletedRecordCount) < $affectedRecordCount) { throw new IllegalStateException(t('Number of affected records is greater than expected number of inserted, updated and deleted records')); } return array($insertedRecordCount, $updatedRecordCount, $deletedRecordCount); }