class LogicalReplicationAnalyzer {

    private final Schemas schemas;
    private final LogicalReplicationService logicalReplicationService;
    private final NodeContext nodeCtx;

    public LogicalReplicationAnalyzer(Schemas schemas,
                                      LogicalReplicationService logicalReplicationService,
                                      NodeContext nodeCtx) {
        this.schemas = schemas;
        this.logicalReplicationService = logicalReplicationService;
        this.nodeCtx = nodeCtx;
    }

    public AnalyzedCreatePublication analyze(CreatePublication createPublication, CoordinatorTxnCtx txnCtx) {
        if (logicalReplicationService.publications().containsKey(createPublication.name())) {
            throw new PublicationAlreadyExistsException(createPublication.name());
        }
        var tables = Lists.map(
            createPublication.tables(),
            q -> {
                CoordinatorSessionSettings sessionSettings = txnCtx.sessionSettings();
                DocTableInfo tableInfo = schemas.findRelation(
                    q,
                    Operation.CREATE_PUBLICATION,
                    sessionSettings.sessionUser(),
                    sessionSettings.searchPath()
                );
                boolean softDeletes = IndexSettings.INDEX_SOFT_DELETES_SETTING.get(tableInfo.parameters());
                if (softDeletes == false) {
                    throw new UnsupportedOperationException(
                        String.format(
                            Locale.ENGLISH,
                            "Tables included in a publication must have the table setting " +
                            "'soft_deletes.enabled' set to `true`, current setting for table '%s': %b",
                            tableInfo.ident(),
                            softDeletes)
                    );
                }
                return tableInfo.ident();
            }
        );

        return new AnalyzedCreatePublication(createPublication.name(), createPublication.isForAllTables(), tables);
    }

    public AnalyzedDropPublication analyze(DropPublication dropPublication,
                                           CoordinatorSessionSettings sessionSettings) {
        var publication = logicalReplicationService.publications().get(dropPublication.name());
        if (dropPublication.ifExists() == false && publication == null) {
            throw new PublicationUnknownException(dropPublication.name());
        }
        if (publication != null
            && publication.owner().equals(sessionSettings.sessionUser().name()) == false
            && sessionSettings.sessionUser().isSuperUser() == false) {
            throw new UnauthorizedException("A publication can only be dropped by the owner or a superuser");
        }

        return new AnalyzedDropPublication(dropPublication.name(), dropPublication.ifExists());
    }

    public AnalyzedAlterPublication analyze(AlterPublication alterPublication,
                                            CoordinatorSessionSettings sessionSettings) {<FILL_FUNCTION_BODY>}

    public AnalyzedCreateSubscription analyze(CreateSubscription<Expression> createSubscription,
                                              ParamTypeHints paramTypeHints,
                                              CoordinatorTxnCtx txnCtx) {
        if (logicalReplicationService.subscriptions().containsKey(createSubscription.name())) {
            throw new SubscriptionAlreadyExistsException(createSubscription.name());
        }

        var expressionAnalyzer = new ExpressionAnalyzer(
            txnCtx, nodeCtx, paramTypeHints, FieldProvider.TO_LITERAL_VALIDATE_NAME, null);
        var exprCtx = new ExpressionAnalysisContext(txnCtx.sessionSettings());
        GenericProperties<Symbol> genericProperties = createSubscription.properties()
            .map(p -> expressionAnalyzer.convert(p, exprCtx));
        Symbol connectionInfo = expressionAnalyzer.convert(createSubscription.connectionInfo(), exprCtx);

        return new AnalyzedCreateSubscription(
            createSubscription.name(),
            connectionInfo,
            createSubscription.publications(),
            genericProperties
        );
    }

    public AnalyzedDropSubscription analyze(DropSubscription dropSubscription, CoordinatorSessionSettings sessionSettings) {
        var subscription = logicalReplicationService.subscriptions().get(dropSubscription.name());
        if (dropSubscription.ifExists() == false && subscription == null) {
            throw new SubscriptionUnknownException(dropSubscription.name());
        }
        if (subscription != null
            && subscription.owner().equals(sessionSettings.sessionUser().name()) == false
            && sessionSettings.sessionUser().isSuperUser() == false) {
            throw new UnauthorizedException("A subscription can only be dropped by the owner or a superuser");
        }
        return new AnalyzedDropSubscription(dropSubscription.name(), subscription, dropSubscription.ifExists());
    }

    public AnalyzedAlterSubscription analyze(AlterSubscription alterSubscription, CoordinatorSessionSettings sessionSettings) {
        var subscription = logicalReplicationService.subscriptions().get(alterSubscription.name());
        if (subscription == null) {
            throw new SubscriptionUnknownException(alterSubscription.name());
        }
        if (subscription.owner().equals(sessionSettings.sessionUser().name()) == false
            && sessionSettings.sessionUser().isSuperUser() == false) {
            throw new UnauthorizedException("A subscription can only be altered by the owner or a superuser");
        }

        return new AnalyzedAlterSubscription(
            alterSubscription.name(),
            alterSubscription.mode() == AlterSubscription.Mode.ENABLE
        );
    }
}

        var publication = logicalReplicationService.publications().get(alterPublication.name());
        if (publication == null) {
            throw new PublicationUnknownException(alterPublication.name());
        }
        if (publication.owner().equals(sessionSettings.sessionUser().name()) == false
            && sessionSettings.sessionUser().isSuperUser() == false) {
            throw new UnauthorizedException("A publication can only be altered by the owner or a superuser");
        }
        if (publication.isForAllTables()) {
            throw new InvalidArgumentException(
                "Publication '" + alterPublication.name() + "' is defined as FOR ALL TABLES," +
                " adding or dropping tables is not supported"
            );
        }
        var defaultSchema = sessionSettings.searchPath().currentSchema();
        var tables = Lists.map(
            alterPublication.tables(),
            q -> {
                var relation = RelationName.of(q, defaultSchema);
                if (schemas.tableExists(relation) == false) {
                    throw new RelationUnknown(relation);
                }
                return relation;
            }
        );
        return new AnalyzedAlterPublication(alterPublication.name(), alterPublication.operation(), tables);
    
<no_super_class>