1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use super::{id, indexes, relation_field, scalar_field};
use crate::introspection::{
    datamodel_calculator::DatamodelCalculatorContext, introspection_helpers as helpers, introspection_pair::ViewPair,
};
use datamodel_renderer::datamodel as renderer;
use schema_connector::ViewDefinition;

/// Render all view blocks to the PSL.
pub(super) fn render<'a>(
    ctx: &'a DatamodelCalculatorContext<'a>,
    rendered: &mut renderer::Datamodel<'a>,
) -> Vec<ViewDefinition> {
    let mut definitions = Vec::new();
    let mut views_with_idx: Vec<(Option<_>, renderer::View<'a>)> = Vec::with_capacity(ctx.sql_schema.views_count());

    for view in ctx.view_pairs() {
        if let Some(definition) = view.definition() {
            let schema = view
                .namespace()
                .map(ToString::to_string)
                .unwrap_or_else(|| ctx.search_path.to_string());

            definitions.push(ViewDefinition {
                schema,
                name: view.name().to_string(),
                definition: definition.to_string(),
            });
        }

        views_with_idx.push((view.previous_position(), render_view(view)));
    }

    views_with_idx.sort_by(|(a, _), (b, _)| helpers::compare_options_none_last(*a, *b));

    for (_, render) in views_with_idx.into_iter() {
        rendered.push_view(render);
    }

    definitions
}

/// Render a single view.
fn render_view(view: ViewPair<'_>) -> renderer::View<'_> {
    let mut rendered = renderer::View::new(view.name());

    if let Some(docs) = view.documentation() {
        rendered.documentation(docs);
    }

    if let Some(mapped_name) = view.mapped_name() {
        rendered.map(mapped_name);

        if view.uses_reserved_name() {
            let docs = format!(
                "This view has been renamed to '{}' during introspection, because the original name '{}' is reserved.",
                view.name(),
                mapped_name,
            );

            rendered.documentation(docs);
        }
    }

    if let Some(namespace) = view.namespace() {
        rendered.schema(namespace);
    }

    if view.ignored() {
        rendered.ignore();
    }

    if let Some(id) = view.id() {
        rendered.id(id::render(id));
    }

    if !view.has_usable_identifier() && !view.ignored_in_psl() {
        let docs = "The underlying view does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client.";
        rendered.documentation(docs);
    }

    if view.adds_a_description() {
        let docs = "This view or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments";
        rendered.documentation(docs);
    }

    for field in view.scalar_fields() {
        rendered.push_field(scalar_field::render(field));
    }

    for field in view.relation_fields() {
        rendered.push_field(relation_field::render(field));
    }

    for definition in view.indexes().map(indexes::render) {
        rendered.push_index(definition);
    }

    rendered
}