blob: 6b2a057909f86e0005b1d466710b48a6894bcabe [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
-- _DOCUMENTATION_INGESTION
CREATE OR REPLACE FUNCTION public._documentation_ingestion()
RETURNS text AS
$$
WITH ingestion_docs AS (
SELECT
proname || E'\n'
|| rpad('', character_length(proname), '-')
|| E'\n\n:code:`'
|| proname || '('
|| string_agg(a.argname || ' ' || typname , ', ')
|| E')`\n\n'
|| description
|| E'\n\n\nback to `Benchmark data model <benchmark-data-model>`_\n'
AS docs
FROM pg_catalog.pg_proc
JOIN pg_catalog.pg_namespace
ON nspname='public'
AND pg_namespace.oid = pronamespace
AND proname LIKE '%ingest%'
JOIN pg_catalog.pg_description
ON pg_description.objoid=pg_proc.oid,
LATERAL unnest(proargnames, proargtypes) AS a(argname, argtype)
JOIN pg_catalog.pg_type
ON pg_type.oid = a.argtype
GROUP BY proname, description
)
SELECT
string_agg(docs, E'\n\n') AS docs
FROM ingestion_docs;
$$
LANGUAGE sql STABLE;
-- _DOCUMENTATION_VIEW_DETAILS
CREATE OR REPLACE FUNCTION public._documentation_view_details(view_name citext)
RETURNS TABLE(
column_name name
, type_name name
, nullable text
, default_value text
, description text
) AS
$$
WITH view_columns AS (
SELECT
attname AS column_name
, attnum AS column_order
FROM pg_catalog.pg_attribute
WHERE attrelid=view_name::regclass
)
SELECT
t.column_name
, type_name
, coalesce(nullable, '')
, coalesce(default_value, '')
, coalesce(description, '')
FROM public.summarized_tables_view AS t
JOIN view_columns AS v ON v.column_name = t.column_name
WHERE t.table_name || '_view' = view_name OR t.column_name NOT LIKE '%_id'
ORDER BY column_order;
$$
LANGUAGE sql STABLE;
-- _DOCUMENTATION_VIEW_PIECES
CREATE OR REPLACE FUNCTION public._documentation_view_pieces(view_name citext)
RETURNS TABLE (rst_formatted text)
AS
$$
DECLARE
column_length integer;
type_length integer;
nullable_length integer;
default_length integer;
description_length integer;
sep text;
border text;
BEGIN
-- All of the hard-coded constants here are the string length of the table
-- column headers: 'Column', 'Type', 'Nullable', 'Default', 'Description'
SELECT greatest(6, max(character_length(column_name)))
FROM public._documentation_view_details(view_name) INTO column_length;
SELECT greatest(4, max(character_length(type_name)))
FROM public._documentation_view_details(view_name) INTO type_length;
SELECT greatest(8, max(character_length(nullable)))
FROM public._documentation_view_details(view_name) INTO nullable_length;
SELECT greatest(7, max(character_length(default_value)))
FROM public._documentation_view_details(view_name) INTO default_length;
SELECT greatest(11, max(character_length(description)))
FROM public._documentation_view_details(view_name) INTO description_length;
SELECT ' ' INTO sep;
SELECT
concat_ws(sep
, rpad('', column_length, '=')
, rpad('', type_length, '=')
, rpad('', nullable_length, '=')
, rpad('', default_length, '=')
, rpad('', description_length, '=')
)
INTO border;
RETURN QUERY
SELECT
border
UNION ALL
SELECT
concat_ws(sep
, rpad('Column', column_length, ' ')
, rpad('Type', type_length, ' ')
, rpad('Nullable', nullable_length, ' ')
, rpad('Default', default_length, ' ')
, rpad('Description', description_length, ' ')
)
UNION ALL
SELECT border
UNION ALL
SELECT
concat_ws(sep
, rpad(v.column_name, column_length, ' ')
, rpad(v.type_name, type_length, ' ')
, rpad(v.nullable, nullable_length, ' ')
, rpad(v.default_value, default_length, ' ')
, rpad(v.description, description_length, ' ')
)
FROM public._documentation_view_details(view_name) AS v
UNION ALL
SELECT border;
END
$$
LANGUAGE plpgsql STABLE;
-- DOCUMENTATION_FOR
CREATE OR REPLACE FUNCTION public.documentation_for(view_name citext)
RETURNS text AS
$$
DECLARE
view_description text;
view_table_markup text;
BEGIN
SELECT description FROM pg_catalog.pg_description
WHERE pg_description.objoid = view_name::regclass
INTO view_description;
SELECT
view_name || E'\n' || rpad('', length(view_name), '-') || E'\n\n' ||
view_description || E'\n\n' ||
string_agg(rst_formatted, E'\n')
INTO view_table_markup
FROM public._documentation_view_pieces(view_name);
RETURN view_table_markup;
END
$$
LANGUAGE plpgsql STABLE;
COMMENT ON FUNCTION public.documentation_for(citext)
IS E'Create an ".rst"-formatted table describing a specific view.\n'
'Example: SELECT public.documentation_for(''endpoint'');';
-- DOCUMENTATION
CREATE OR REPLACE FUNCTION public.documentation(dotfile_name text)
RETURNS TABLE (full_text text) AS
$$
WITH v AS (
SELECT
public.documentation_for(relname::citext)
|| E'\n\nback to `Benchmark data model <benchmark-data-model>`_\n'
AS view_documentation
FROM pg_catalog.pg_trigger
JOIN pg_catalog.pg_class ON pg_trigger.tgrelid = pg_class.oid
WHERE NOT tgisinternal
)
SELECT
E'\n.. _benchmark-data-model:\n\n'
'Benchmark data model\n'
'====================\n\n\n'
'.. graphviz:: '
|| dotfile_name
|| E'\n\n\n.. _benchmark-ingestion:\n\n'
'Benchmark ingestion helper functions\n'
'====================================\n\n'
|| public._documentation_ingestion()
|| E'\n\n\n.. _benchmark-views:\n\n'
'Benchmark views\n'
'===============\n\n\n'
|| string_agg(v.view_documentation, E'\n')
FROM v
GROUP BY True;
$$
LANGUAGE sql STABLE;
COMMENT ON FUNCTION public.documentation(text)
IS E'Create an ".rst"-formatted file that shows the columns in '
'every insertable view in the "public" schema.\n'
'The text argument is the name of the generated dotfile to be included.\n'
'Example: SELECT public.documentation(''data_model.dot'');';
-- _DOCUMENTATION_DOTFILE_NODE_FOR
CREATE OR REPLACE FUNCTION public._documentation_dotfile_node_for(tablename name)
RETURNS text AS
$$
DECLARE
result text;
BEGIN
WITH node AS (
SELECT
tablename::text AS lines
UNION ALL
SELECT
E'[label = \n'
' <<table border="0" cellborder="1" cellspacing="0" cellpadding="2">'
UNION ALL
-- table name
SELECT
' <tr><td border="0"><font point-size="14">'
|| tablename
|| '</font></td></tr>'
UNION ALL
-- primary keys
SELECT
' <tr><td port="' || column_name || '"><b>'
|| column_name
|| ' (pk)</b></td></tr>'
FROM public.summarized_tables_view
WHERE table_name = tablename
AND description LIKE '%primary key%'
UNION ALL
-- columns
SELECT
' <tr><td>'
|| column_name
|| CASE WHEN description LIKE '%unique' THEN ' (u)' ELSE '' END
|| CASE WHEN nullable <> 'not null' THEN ' (o)' ELSE '' END
|| '</td></tr>'
FROM public.summarized_tables_view
WHERE table_name = tablename
AND (description IS NULL OR description not like '%key%')
UNION ALL
-- foreign keys
SELECT
' <tr><td port="' || column_name || '">'
|| column_name
|| CASE WHEN description LIKE '%unique' THEN ' (u)' ELSE '' END
|| ' (fk) </td></tr>'
FROM public.summarized_tables_view
WHERE table_name = tablename
AND description LIKE '%foreign key%'
AND description NOT LIKE '%primary key%'
UNION ALL
SELECT
E' </table>>\n];'
)
SELECT
string_agg(lines, E'\n')
INTO result
FROM node;
RETURN result;
END
$$
LANGUAGE plpgsql STABLE;
-- _DOCUMENTATION_DOTFILE_EDGES
CREATE OR REPLACE FUNCTION public._documentation_dotfile_edges()
RETURNS text AS
$$
DECLARE
result text;
BEGIN
WITH relationship AS (
SELECT
conrelid AS fk_table_id
, confrelid AS pk_table_id
, unnest(conkey) AS fk_colnum
, unnest(confkey) AS pk_colnum
FROM pg_catalog.pg_constraint
WHERE confkey IS NOT NULL
AND connamespace='public'::regnamespace
), all_edges AS (
SELECT
fk_tbl.relname || ':' || fk_col.attname
|| ' -> '
|| pk_tbl.relname || ':' || pk_col.attname
|| ';' AS lines
FROM relationship
-- foreign key table + column
JOIN pg_catalog.pg_attribute AS fk_col
ON fk_col.attrelid = relationship.fk_table_id
AND fk_col.attnum = relationship.fk_colnum
JOIN pg_catalog.pg_class AS fk_tbl
ON fk_tbl.oid = relationship.fk_table_id
-- primary key table + column
JOIN pg_catalog.pg_attribute AS pk_col
ON pk_col.attrelid = relationship.pk_table_id
AND pk_col.attnum = relationship.pk_colnum
JOIN pg_catalog.pg_class AS pk_tbl
ON pk_tbl.oid = relationship.pk_table_id
)
SELECT
string_agg(lines, E'\n')
INTO result
FROM all_edges;
RETURN result;
END
$$
LANGUAGE plpgsql STABLE;
-- DOCUMENTATION_DOTFILE
CREATE OR REPLACE FUNCTION public.documentation_dotfile()
RETURNS text AS
$$
DECLARE
schemaname name := 'public';
result text;
BEGIN
WITH file_contents AS (
SELECT
E'digraph database {\n concentrate = true;\n'
' rankdir = LR;\n'
' ratio = ".75";\n'
' node [shape = none, fontsize="11", fontname="Helvetica"];\n'
' edge [fontsize="8", fontname="Helvetica"];'
AS lines
UNION ALL
SELECT
E'legend\n[fontsize = "14"\nlabel =\n'
'<<table border="0" cellpadding="0">\n'
' <tr><td align="left"><font point-size="16">Legend</font></td></tr>\n'
' <tr><td align="left">pk = primary key</td></tr>\n'
' <tr><td align="left">fk = foreign key</td></tr>\n'
' <tr><td align="left">u = unique*</td></tr>\n'
' <tr><td align="left">o = optional</td></tr>\n'
' <tr><td align="left">'
'* multiple uniques in the same table are a unique group</td></tr>\n'
'</table>>\n];'
UNION ALL
SELECT
string_agg(
public._documentation_dotfile_node_for(relname),
E'\n' -- Forcing the 'env' table to the end makes a better image
ORDER BY (CASE WHEN relname LIKE 'env%' THEN 'z' ELSE relname END)
)
FROM pg_catalog.pg_class
WHERE relkind='r' AND relnamespace = schemaname::regnamespace
UNION ALL
SELECT
public._documentation_dotfile_edges()
UNION ALL
SELECT
'}'
)
SELECT
string_agg(lines, E'\n') AS dotfile
INTO result
FROM file_contents;
RETURN result;
END
$$
LANGUAGE plpgsql STABLE;
COMMENT ON FUNCTION public.documentation_dotfile()
IS E'Create a Graphviz dotfile of the data model: '
'every table in the "public" schema.\n'
'Example: SELECT public.documentation_dotfile();';