statement ok
CREATE FUNCTION f1() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;

statement ok
CREATE VIEW v_col_fn_ids AS
SELECT
id,
json_array_elements(
  crdb_internal.pb_to_json(
    'cockroach.sql.sqlbase.Descriptor',
    descriptor,
    false
  )->'table'->'columns'
)->'id' as col_id,
json_array_elements(
  crdb_internal.pb_to_json(
    'cockroach.sql.sqlbase.Descriptor',
    descriptor,
    false
  )->'table'->'columns'
)->'defaultExpr' as default_expr,
json_array_elements(
  crdb_internal.pb_to_json(
    'cockroach.sql.sqlbase.Descriptor',
    descriptor,
    false
  )->'table'->'columns'
)->'usesFunctionIds' as uses_fn_ids
FROM system.descriptor

statement ok
CREATE FUNCTION get_col_fn_ids(table_id INT) RETURNS SETOF v_col_fn_ids
LANGUAGE SQL
AS $$
  SELECT *
  FROM v_col_fn_ids
  WHERE id = table_id
$$;

statement ok
CREATE VIEW v_fn_depended_on_by AS
SELECT
     id,
     jsonb_pretty(
       crdb_internal.pb_to_json(
         'cockroach.sql.sqlbase.Descriptor',
         descriptor,
         false
       )->'function'->'dependedOnBy'
     ) as depended_on_by
FROM system.descriptor

statement ok
CREATE FUNCTION get_fn_depended_on_by(function_id INT) RETURNS STRING
LANGUAGE SQL
AS $$
  SELECT depended_on_by
  FROM v_fn_depended_on_by
  WHERE id = function_id
$$;

# Make sure that column DEFAULT expression is properly serialized and
# deserialized.
statement ok
CREATE TABLE t1(
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  c INT,
  FAMILY fam_0 (a, b, c)
);

let $tbl_id
SELECT id FROM system.namespace WHERE name = 't1';

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(111,1,,)
(111,2,"""[FUNCTION 100106]()""",[106])
(111,3,,)

query T
SELECT create_statement FROM [SHOW CREATE TABLE t1];
----
CREATE TABLE public.t1 (
  a INT8 NOT NULL,
  b INT8 NULL DEFAULT public.f1(),
  c INT8 NULL,
  CONSTRAINT t1_pkey PRIMARY KEY (a ASC),
  FAMILY fam_0 (a, b, c)
)

# Make sure that back references are tracked properly.
let $fn_id
SELECT oid::int - 100000 FROM pg_catalog.pg_proc WHERE proname = 'f1';

query T
SELECT get_fn_depended_on_by($fn_id)
----
[
    {
        "columnIds": [
            2
        ],
        "id": 111
    }
]

# Make sure SET DEFAULT sets cross references properly.
statement ok
ALTER TABLE t1 ALTER COLUMN c SET DEFAULT f1();

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(111,1,,)
(111,2,"""[FUNCTION 100106]()""",[106])
(111,3,"""[FUNCTION 100106]()""",[106])

query T
SELECT create_statement FROM [SHOW CREATE TABLE t1];
----
CREATE TABLE public.t1 (
  a INT8 NOT NULL,
  b INT8 NULL DEFAULT public.f1(),
  c INT8 NULL DEFAULT public.f1(),
  CONSTRAINT t1_pkey PRIMARY KEY (a ASC),
  FAMILY fam_0 (a, b, c)
)

query T
SELECT get_fn_depended_on_by($fn_id)
----
[
    {
        "columnIds": [
            2,
            3
        ],
        "id": 111
    }
]

# Make sure cross references are properly removed with SET DEFAULT.
statement ok
ALTER TABLE t1 ALTER COLUMN c SET DEFAULT NULL;

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(111,1,,)
(111,2,"""[FUNCTION 100106]()""",[106])
(111,3,,)

query T
SELECT get_fn_depended_on_by($fn_id)
----
[
    {
        "columnIds": [
            2
        ],
        "id": 111
    }
]

statement ok
ALTER TABLE t1 ALTER COLUMN b SET DEFAULT NULL;

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(111,1,,)
(111,2,,)
(111,3,,)

query T
SELECT get_fn_depended_on_by($fn_id)
----
NULL

# Make sure cross references are properly removed with DROP COLUMN
statement ok
ALTER TABLE t1 ALTER COLUMN c SET DEFAULT f1();

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(111,1,,)
(111,2,,)
(111,3,"""[FUNCTION 100106]()""",[106])

query T
SELECT get_fn_depended_on_by($fn_id)
----
[
    {
        "columnIds": [
            3
        ],
        "id": 111
    }
]

statement ok
ALTER TABLE t1 DROP COLUMN c;

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(111,1,,)
(111,2,,)

query T
SELECT get_fn_depended_on_by($fn_id)
----
NULL

# Make sure that cross references are properly remove with DROP TABLE.
statement ok
ALTER TABLE t1 ALTER COLUMN b SET DEFAULT f1();

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(111,1,,)
(111,2,"""[FUNCTION 100106]()""",[106])

query T
SELECT get_fn_depended_on_by($fn_id)
----
[
    {
        "columnIds": [
            2
        ],
        "id": 111
    }
]

statement ok
DROP TABLE t1;

query T
SELECT get_fn_depended_on_by($fn_id)
----
NULL

# Make sure function used by multiple tables can handle cross-references
# properly.

statement ok
CREATE TABLE t1(
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  FAMILY fam_0 (a, b)
);
CREATE TABLE t2(
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  FAMILY fam_0 (a, b)
);

query T
SELECT get_fn_depended_on_by($fn_id)
----
[
    {
        "columnIds": [
            2
        ],
        "id": 112
    },
    {
        "columnIds": [
            2
        ],
        "id": 113
    }
]

statement ok
DROP TABLE t1;
DROp TABLE t2;

query T
SELECT get_fn_depended_on_by($fn_id)
----
NULL

# Make sure table uses multiple functions can handle cross-references properly.
statement ok
CREATE FUNCTION f2() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;

let $fn_id_2
SELECT oid::int - 100000 FROM pg_catalog.pg_proc WHERE proname = 'f2';

statement ok
CREATE TABLE t1(
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  c INT DEFAULT f2(),
  FAMILY fam_0 (a, b, c)
);

query T
SELECT get_fn_depended_on_by($fn_id)
----
[
    {
        "columnIds": [
            2
        ],
        "id": 115
    }
]

query T
SELECT get_fn_depended_on_by($fn_id_2)
----
[
    {
        "columnIds": [
            3
        ],
        "id": 115
    }
]

statement ok
DROP TABLE t1;

query T
SELECT get_fn_depended_on_by($fn_id)
----
NULL

query T
SELECT get_fn_depended_on_by($fn_id_2)
----
NULL

# Make sure function cannot be dropped if used in constraints
statement ok
CREATE TABLE t1(
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  FAMILY fam_0 (a, b)
);
CREATE TABLE t2(
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  FAMILY fam_0 (a, b)
);

statement error pgcode 2BP01 cannot drop function "f1" because other objects \(\[test.public.t1, test.public.t2\]\) still depend on it
DROP FUNCTION f1;

statement ok
ALTER TABLE t1 ALTER COLUMN b SET DEFAULT NULL;
ALTER TABLE t2 ALTER COLUMN b SET DEFAULT NULL;

statement ok
DROP FUNCTION f1;
DROP TABLE t1;
DROP TABLE t2;

# Make sure that CREATE FUNCTION and CREATE TABLE works in one txn.
statement ok
BEGIN;
CREATE FUNCTION f1() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;
CREATE TABLE t1(
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  FAMILY fam_0 (a, b)
);
END;

let $tbl_id
SELECT id FROM system.namespace WHERE name = 't1';

let $fn_id
SELECT oid::int - 100000 FROM pg_catalog.pg_proc WHERE proname = 'f1';

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(119,1,,)
(119,2,"""[FUNCTION 100118]()""",[118])

query T
SELECT get_fn_depended_on_by($fn_id);
----
[
    {
        "columnIds": [
            2
        ],
        "id": 119
    }
]

statement ok
BEGIN;
DROP TABLE t1;
DROP FUNCTION f1;
END;

# Make sure that CREATE FUNCTION and SET DEFAULT works in one txn.
statement ok
CREATE TABLE t1 (
  a INT PRIMARY KEY,
  b INT,
  FAMILY fam_0 (a, b)
);

statement ok
CREATE FUNCTION f1() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;
ALTER TABLE t1 ALTER COLUMN b SET DEFAULT f1();

let $tbl_id
SELECT id FROM system.namespace WHERE name = 't1';

let $fn_id
SELECT oid::int - 100000 FROM pg_catalog.pg_proc WHERE proname = 'f1';

query T rowsort
SELECT get_col_fn_ids($tbl_id);
----
(120,1,,)
(120,2,"""[FUNCTION 100121]()""",[121])

query T
SELECT get_fn_depended_on_by($fn_id);
----
[
    {
        "columnIds": [
            2
        ],
        "id": 120
    }
]

skipif config local-legacy-schema-changer
statement ok
SET use_declarative_schema_changer = 'unsafe_always';

statement ok
BEGIN;
ALTER TABLE t1 DROP COLUMN b;
DROP FUNCTION f1;
END;

skipif config local-legacy-schema-changer
statement ok
SET use_declarative_schema_changer = 'on';

# Make sure column DEFAULT works properly with insert.
statement ok
DROP TABLE t1;

statement ok
CREATE FUNCTION f1() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$;
CREATE TABLE t1 (
  a INT PRIMARY KEY,
  b INT DEFAULT f1(),
  FAMILY fam_0 (a, b)
);

statement ok
INSERT INTO t1 VALUES (1), (2)

query II
SELECT * FROM t1 ORDER BY a;
----
1  1
2  1

# Make sure that renaming a function is fine.
statement ok
ALTER FUNCTION f1() RENAME TO f1_new;

query T
SELECT create_statement FROM [SHOW CREATE TABLE t1]
----
CREATE TABLE public.t1 (
  a INT8 NOT NULL,
  b INT8 NULL DEFAULT public.f1_new(),
  CONSTRAINT t1_pkey PRIMARY KEY (a ASC),
  FAMILY fam_0 (a, b)
)

statement ok
INSERT INTO t1 VALUES (3)

query II
SELECT * FROM t1 ORDER BY a;
----
1  1
2  1
3  1

# Make sure dependency circle is not allowed.
statement ok
CREATE TABLE t_circle(a INT PRIMARY KEY, b INT);
CREATE FUNCTION f_circle() RETURNS INT LANGUAGE SQL AS $$ SELECT a FROM t_circle $$;

statement error pgcode 42P13 pq: .*cannot add dependency from descriptor \d+ to function f_circle \(\d+\) because there will be a dependency cycle.*
ALTER TABLE t_circle ALTER COLUMN b SET DEFAULT f_circle();
