protected CreateResult doCreateView()

in asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java [2908:3099]


    protected CreateResult doCreateView(MetadataProvider metadataProvider, CreateViewStatement cvs, String databaseName,
            DataverseName dataverseName, String viewName, String itemTypeDatabaseName,
            DataverseName itemTypeDataverseName, String itemTypeName, IStatementRewriter stmtRewriter,
            IRequestParameters requestParameters, Creator creator) throws Exception {
        SourceLocation sourceLoc = cvs.getSourceLocation();
        MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
        metadataProvider.setMetadataTxnContext(mdTxnCtx);
        try {
            Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, databaseName, dataverseName);
            if (dv == null) {
                throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc,
                        MetadataUtil.dataverseName(databaseName, dataverseName, metadataProvider.isUsingDatabase()));
            }
            Namespace ns = new Namespace(dv.getDatabaseName(), dv.getDataverseName());
            Dataset existingDataset =
                    MetadataManager.INSTANCE.getDataset(mdTxnCtx, databaseName, dataverseName, viewName);
            if (existingDataset != null) {
                if (DatasetUtil.isNotView(existingDataset)) {
                    throw new CompilationException(ErrorCode.DATASET_EXISTS, sourceLoc,
                            existingDataset.getDatasetName(), existingDataset.getDataverseName());
                }
                if (cvs.getIfNotExists()) {
                    MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
                    return CreateResult.NOOP;
                } else if (!cvs.getReplaceIfExists()) {
                    throw new CompilationException(ErrorCode.VIEW_EXISTS, sourceLoc,
                            existingDataset.getDatasetFullyQualifiedName());
                }
            }

            DatasetFullyQualifiedName viewQualifiedName =
                    new DatasetFullyQualifiedName(databaseName, dataverseName, viewName);

            Datatype itemTypeEntity = null;
            boolean itemTypeIsInline = false;
            CreateViewStatement.KeyDecl primaryKeyDecl = cvs.getPrimaryKeyDecl();
            List<String> primaryKeyFields = null;
            List<CreateViewStatement.ForeignKeyDecl> foreignKeyDecls = cvs.getForeignKeyDecls();
            List<ViewDetails.ForeignKey> foreignKeys = null;
            String datetimeFormat = null, dateFormat = null, timeFormat = null;
            if (cvs.hasItemType()) {
                Pair<Datatype, Boolean> itemTypePair = fetchDatasetItemType(mdTxnCtx, DatasetType.VIEW,
                        DatasetConfig.DatasetFormat.ROW, null, itemTypeDatabaseName, itemTypeDataverseName,
                        itemTypeName, cvs.getItemType(), false, metadataProvider, sourceLoc);
                itemTypeEntity = itemTypePair.first;
                itemTypeIsInline = itemTypePair.second;
                ARecordType itemType = (ARecordType) itemTypeEntity.getDatatype();
                if (primaryKeyDecl != null) {
                    primaryKeyFields = ValidateUtil.validateViewKeyFields(primaryKeyDecl, itemType, false, sourceLoc);
                }
                if (foreignKeyDecls != null) {
                    foreignKeys = new ArrayList<>(foreignKeyDecls.size());
                    for (CreateViewStatement.ForeignKeyDecl foreignKeyDecl : foreignKeyDecls) {
                        List<String> foreignKeyFields =
                                ValidateUtil.validateViewKeyFields(foreignKeyDecl, itemType, true, sourceLoc);
                        DataverseName refDataverseName = foreignKeyDecl.getReferencedDataverseName();
                        String refDatabaseName = foreignKeyDecl.getReferencedDatabaseName();
                        if (refDataverseName == null) {
                            refDataverseName = dataverseName;
                            refDatabaseName = databaseName;
                        } else {
                            Dataverse refDataverse =
                                    MetadataManager.INSTANCE.getDataverse(mdTxnCtx, refDatabaseName, refDataverseName);
                            if (refDataverse == null) {
                                throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc,
                                        MetadataUtil.dataverseName(refDatabaseName, refDataverseName,
                                                metadataProvider.isUsingDatabase()));
                            }
                        }
                        String refDatasetName = foreignKeyDecl.getReferencedDatasetName().getValue();
                        boolean isSelfRef = refDatabaseName.equals(databaseName)
                                && refDataverseName.equals(dataverseName) && refDatasetName.equals(viewName);
                        DatasetType refDatasetType;
                        DatasetFullyQualifiedName refQualifiedName;
                        List<String> refPrimaryKeyFields;
                        if (isSelfRef) {
                            refDatasetType = DatasetType.VIEW;
                            refQualifiedName = viewQualifiedName;
                            refPrimaryKeyFields = primaryKeyFields;
                        } else {
                            // findDataset() will acquire lock on referenced dataset (view)
                            Dataset refDataset = metadataProvider.findDataset(refDatabaseName, refDataverseName,
                                    refDatasetName, true);
                            if (refDataset == null || DatasetUtil.isNotView(refDataset)) {
                                throw new CompilationException(ErrorCode.UNKNOWN_VIEW, sourceLoc,
                                        DatasetUtil.getFullyQualifiedDisplayName(refDataverseName, refDatasetName));
                            }
                            ViewDetails refViewDetails = (ViewDetails) refDataset.getDatasetDetails();
                            refDatasetType = refDataset.getDatasetType();
                            refQualifiedName =
                                    new DatasetFullyQualifiedName(refDatabaseName, refDataverseName, refDatasetName);
                            refPrimaryKeyFields = refViewDetails.getPrimaryKeyFields();
                        }

                        if (refPrimaryKeyFields == null) {
                            throw new CompilationException(ErrorCode.INVALID_FOREIGN_KEY_DEFINITION_REF_PK_NOT_FOUND,
                                    sourceLoc, DatasetUtil.getDatasetTypeDisplayName(refDatasetType),
                                    DatasetUtil.getFullyQualifiedDisplayName(refDataverseName, refDatasetName));
                        } else if (refPrimaryKeyFields.size() != foreignKeyFields.size()) {
                            throw new CompilationException(ErrorCode.INVALID_FOREIGN_KEY_DEFINITION_REF_PK_MISMATCH,
                                    sourceLoc, DatasetUtil.getDatasetTypeDisplayName(refDatasetType),
                                    DatasetUtil.getFullyQualifiedDisplayName(refDataverseName, refDatasetName));
                        } else if (isSelfRef
                                && !OperatorPropertiesUtil.disjoint(refPrimaryKeyFields, foreignKeyFields)) {
                            throw new CompilationException(ErrorCode.INVALID_FOREIGN_KEY_DEFINITION, sourceLoc);
                        }

                        foreignKeys.add(new ViewDetails.ForeignKey(foreignKeyFields, refQualifiedName));
                    }
                }

                Map<String, String> viewConfig =
                        TypeUtil.validateConfiguration(cvs.getViewConfiguration(), cvs.getSourceLocation());
                datetimeFormat = TypeUtil.getDatetimeFormat(viewConfig);
                dateFormat = TypeUtil.getDateFormat(viewConfig);
                timeFormat = TypeUtil.getTimeFormat(viewConfig);

            } else {
                if (primaryKeyDecl != null) {
                    throw new CompilationException(ErrorCode.INVALID_PRIMARY_KEY_DEFINITION, cvs.getSourceLocation());
                }
                if (foreignKeyDecls != null) {
                    throw new CompilationException(ErrorCode.INVALID_FOREIGN_KEY_DEFINITION, cvs.getSourceLocation());
                }
                if (cvs.getViewConfiguration() != null) {
                    throw new CompilationException(ErrorCode.ILLEGAL_SET_PARAMETER, cvs.getSourceLocation(),
                            cvs.getViewConfiguration().keySet().iterator().next());
                }
            }

            if (existingDataset != null) {
                ViewDetails existingViewDetails = (ViewDetails) existingDataset.getDatasetDetails();
                List<String> existingPrimaryKeyFields = existingViewDetails.getPrimaryKeyFields();
                // For now don't allow view replacement if existing view has primary keys and they are different
                // from the new view's primary keys, because there could be another view that references
                // these primary keys via its foreign keys declaration.
                // In the future we should relax this check: scan datasets metadata and allow replacement in this case
                // if there's no view that references this view
                boolean allowToReplace =
                        existingPrimaryKeyFields == null || existingPrimaryKeyFields.equals(primaryKeyFields);
                if (!allowToReplace) {
                    throw new CompilationException(ErrorCode.CANNOT_CHANGE_PRIMARY_KEY, cvs.getSourceLocation(),
                            DatasetUtil.getDatasetTypeDisplayName(existingDataset.getDatasetType()),
                            DatasetUtil.getFullyQualifiedDisplayName(existingDataset));
                }
            }

            // Check whether the view is usable:
            // create a view declaration for this function,
            // and a query body that queries this view:
            ViewDecl viewDecl = new ViewDecl(viewQualifiedName, cvs.getViewBodyExpression());
            viewDecl.setSourceLocation(sourceLoc);
            IQueryRewriter queryRewriter = rewriterFactory.createQueryRewriter();
            Query wrappedQuery =
                    queryRewriter.createViewAccessorQuery(viewDecl, metadataProvider.getNamespaceResolver());
            metadataProvider.setDefaultNamespace(ns);

            LangRewritingContext langRewritingContext = createLangRewritingContext(metadataProvider, declaredFunctions,
                    Collections.singletonList(viewDecl), null, warningCollector, wrappedQuery.getVarCounter());
            apiFramework.reWriteQuery(langRewritingContext, wrappedQuery, sessionOutput, false, false,
                    Collections.emptyList());

            List<List<DependencyFullyQualifiedName>> dependencies =
                    ViewUtil.getViewDependencies(metadataProvider, viewDecl, foreignKeys, queryRewriter);
            appCtx.getReceptionist().ensureAuthorized(requestParameters, metadataProvider);

            ViewDetails viewDetails = new ViewDetails(cvs.getViewBody(), dependencies, cvs.getDefaultNull(),
                    primaryKeyFields, foreignKeys, datetimeFormat, dateFormat, timeFormat);

            Dataset view = new Dataset(databaseName, dataverseName, viewName, itemTypeDatabaseName,
                    itemTypeDataverseName, itemTypeName, MetadataConstants.METADATA_NODEGROUP_NAME, "",
                    Collections.emptyMap(), viewDetails, Collections.emptyMap(), DatasetType.VIEW, 0,
                    MetadataUtil.PENDING_NO_OP, creator);
            if (existingDataset == null) {
                if (itemTypeIsInline) {
                    MetadataManager.INSTANCE.addDatatype(mdTxnCtx, itemTypeEntity);
                }
                MetadataManager.INSTANCE.addDataset(mdTxnCtx, view);
            } else {
                if (itemTypeIsInline) {
                    MetadataManager.INSTANCE.updateDatatype(mdTxnCtx, itemTypeEntity);
                }
                MetadataManager.INSTANCE.updateDataset(mdTxnCtx, view);
            }
            beforeTxnCommit(metadataProvider, creator, EntityDetails.newView(databaseName, dataverseName, viewName));
            MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
            return existingDataset != null ? CreateResult.REPLACED : CreateResult.CREATED;
        } catch (Exception e) {
            abort(e, e, mdTxnCtx);
            throw e;
        }
    }