1 | #include "duckdb/catalog/default/default_functions.hpp" |
2 | #include "duckdb/parser/parser.hpp" |
3 | #include "duckdb/parser/parsed_data/create_macro_info.hpp" |
4 | #include "duckdb/parser/expression/columnref_expression.hpp" |
5 | #include "duckdb/catalog/catalog_entry/scalar_macro_catalog_entry.hpp" |
6 | #include "duckdb/function/table_macro_function.hpp" |
7 | |
8 | #include "duckdb/function/scalar_macro_function.hpp" |
9 | |
10 | namespace duckdb { |
11 | |
12 | static DefaultMacro internal_macros[] = { |
13 | {DEFAULT_SCHEMA, .name: "current_role" , .parameters: {nullptr}, .macro: "'duckdb'" }, // user name of current execution context |
14 | {DEFAULT_SCHEMA, .name: "current_user" , .parameters: {nullptr}, .macro: "'duckdb'" }, // user name of current execution context |
15 | {DEFAULT_SCHEMA, .name: "current_catalog" , .parameters: {nullptr}, .macro: "current_database()" }, // name of current database (called "catalog" in the SQL standard) |
16 | {DEFAULT_SCHEMA, .name: "user" , .parameters: {nullptr}, .macro: "current_user" }, // equivalent to current_user |
17 | {DEFAULT_SCHEMA, .name: "session_user" , .parameters: {nullptr}, .macro: "'duckdb'" }, // session user name |
18 | {.schema: "pg_catalog" , .name: "inet_client_addr" , .parameters: {nullptr}, .macro: "NULL" }, // address of the remote connection |
19 | {.schema: "pg_catalog" , .name: "inet_client_port" , .parameters: {nullptr}, .macro: "NULL" }, // port of the remote connection |
20 | {.schema: "pg_catalog" , .name: "inet_server_addr" , .parameters: {nullptr}, .macro: "NULL" }, // address of the local connection |
21 | {.schema: "pg_catalog" , .name: "inet_server_port" , .parameters: {nullptr}, .macro: "NULL" }, // port of the local connection |
22 | {.schema: "pg_catalog" , .name: "pg_my_temp_schema" , .parameters: {nullptr}, .macro: "0" }, // OID of session's temporary schema, or 0 if none |
23 | {.schema: "pg_catalog" , .name: "pg_is_other_temp_schema" , .parameters: {"schema_id" , nullptr}, .macro: "false" }, // is schema another session's temporary schema? |
24 | |
25 | {.schema: "pg_catalog" , .name: "pg_conf_load_time" , .parameters: {nullptr}, .macro: "current_timestamp" }, // configuration load time |
26 | {.schema: "pg_catalog" , .name: "pg_postmaster_start_time" , .parameters: {nullptr}, .macro: "current_timestamp" }, // server start time |
27 | |
28 | {.schema: "pg_catalog" , .name: "pg_typeof" , .parameters: {"expression" , nullptr}, .macro: "lower(typeof(expression))" }, // get the data type of any value |
29 | |
30 | // privilege functions |
31 | // {"has_any_column_privilege", {"user", "table", "privilege", nullptr}, "true"}, //boolean //does user have privilege for any column of table |
32 | {.schema: "pg_catalog" , .name: "has_any_column_privilege" , .parameters: {"table" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for any column of table |
33 | // {"has_column_privilege", {"user", "table", "column", "privilege", nullptr}, "true"}, //boolean //does user have privilege for column |
34 | {.schema: "pg_catalog" , .name: "has_column_privilege" , .parameters: {"table" , "column" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for column |
35 | // {"has_database_privilege", {"user", "database", "privilege", nullptr}, "true"}, //boolean //does user have privilege for database |
36 | {.schema: "pg_catalog" , .name: "has_database_privilege" , .parameters: {"database" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for database |
37 | // {"has_foreign_data_wrapper_privilege", {"user", "fdw", "privilege", nullptr}, "true"}, //boolean //does user have privilege for foreign-data wrapper |
38 | {.schema: "pg_catalog" , .name: "has_foreign_data_wrapper_privilege" , .parameters: {"fdw" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for foreign-data wrapper |
39 | // {"has_function_privilege", {"user", "function", "privilege", nullptr}, "true"}, //boolean //does user have privilege for function |
40 | {.schema: "pg_catalog" , .name: "has_function_privilege" , .parameters: {"function" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for function |
41 | // {"has_language_privilege", {"user", "language", "privilege", nullptr}, "true"}, //boolean //does user have privilege for language |
42 | {.schema: "pg_catalog" , .name: "has_language_privilege" , .parameters: {"language" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for language |
43 | // {"has_schema_privilege", {"user", "schema, privilege", nullptr}, "true"}, //boolean //does user have privilege for schema |
44 | {.schema: "pg_catalog" , .name: "has_schema_privilege" , .parameters: {"schema" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for schema |
45 | // {"has_sequence_privilege", {"user", "sequence", "privilege", nullptr}, "true"}, //boolean //does user have privilege for sequence |
46 | {.schema: "pg_catalog" , .name: "has_sequence_privilege" , .parameters: {"sequence" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for sequence |
47 | // {"has_server_privilege", {"user", "server", "privilege", nullptr}, "true"}, //boolean //does user have privilege for foreign server |
48 | {.schema: "pg_catalog" , .name: "has_server_privilege" , .parameters: {"server" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for foreign server |
49 | // {"has_table_privilege", {"user", "table", "privilege", nullptr}, "true"}, //boolean //does user have privilege for table |
50 | {.schema: "pg_catalog" , .name: "has_table_privilege" , .parameters: {"table" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for table |
51 | // {"has_tablespace_privilege", {"user", "tablespace", "privilege", nullptr}, "true"}, //boolean //does user have privilege for tablespace |
52 | {.schema: "pg_catalog" , .name: "has_tablespace_privilege" , .parameters: {"tablespace" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for tablespace |
53 | |
54 | // various postgres system functions |
55 | {.schema: "pg_catalog" , .name: "pg_get_viewdef" , .parameters: {"oid" , nullptr}, .macro: "(select sql from duckdb_views() v where v.view_oid=oid)" }, |
56 | {.schema: "pg_catalog" , .name: "pg_get_constraintdef" , .parameters: {"constraint_oid" , "pretty_bool" , nullptr}, .macro: "(select constraint_text from duckdb_constraints() d_constraint where d_constraint.table_oid=constraint_oid//1000000 and d_constraint.constraint_index=constraint_oid%1000000)" }, |
57 | {.schema: "pg_catalog" , .name: "pg_get_expr" , .parameters: {"pg_node_tree" , "relation_oid" , nullptr}, .macro: "pg_node_tree" }, |
58 | {.schema: "pg_catalog" , .name: "format_pg_type" , .parameters: {"type_name" , nullptr}, .macro: "case when logical_type='FLOAT' then 'real' when logical_type='DOUBLE' then 'double precision' when logical_type='DECIMAL' then 'numeric' when logical_type='ENUM' then lower(type_name) when logical_type='VARCHAR' then 'character varying' when logical_type='BLOB' then 'bytea' when logical_type='TIMESTAMP' then 'timestamp without time zone' when logical_type='TIME' then 'time without time zone' else lower(logical_type) end" }, |
59 | {.schema: "pg_catalog" , .name: "format_type" , .parameters: {"type_oid" , "typemod" , nullptr}, .macro: "(select format_pg_type(type_name) from duckdb_types() t where t.type_oid=type_oid) || case when typemod>0 then concat('(', typemod//1000, ',', typemod%1000, ')') else '' end" }, |
60 | |
61 | {.schema: "pg_catalog" , .name: "pg_has_role" , .parameters: {"user" , "role" , "privilege" , nullptr}, .macro: "true" }, //boolean //does user have privilege for role |
62 | {.schema: "pg_catalog" , .name: "pg_has_role" , .parameters: {"role" , "privilege" , nullptr}, .macro: "true" }, //boolean //does current user have privilege for role |
63 | |
64 | {.schema: "pg_catalog" , .name: "col_description" , .parameters: {"table_oid" , "column_number" , nullptr}, .macro: "NULL" }, // get comment for a table column |
65 | {.schema: "pg_catalog" , .name: "obj_description" , .parameters: {"object_oid" , "catalog_name" , nullptr}, .macro: "NULL" }, // get comment for a database object |
66 | {.schema: "pg_catalog" , .name: "shobj_description" , .parameters: {"object_oid" , "catalog_name" , nullptr}, .macro: "NULL" }, // get comment for a shared database object |
67 | |
68 | // visibility functions |
69 | {.schema: "pg_catalog" , .name: "pg_collation_is_visible" , .parameters: {"collation_oid" , nullptr}, .macro: "true" }, |
70 | {.schema: "pg_catalog" , .name: "pg_conversion_is_visible" , .parameters: {"conversion_oid" , nullptr}, .macro: "true" }, |
71 | {.schema: "pg_catalog" , .name: "pg_function_is_visible" , .parameters: {"function_oid" , nullptr}, .macro: "true" }, |
72 | {.schema: "pg_catalog" , .name: "pg_opclass_is_visible" , .parameters: {"opclass_oid" , nullptr}, .macro: "true" }, |
73 | {.schema: "pg_catalog" , .name: "pg_operator_is_visible" , .parameters: {"operator_oid" , nullptr}, .macro: "true" }, |
74 | {.schema: "pg_catalog" , .name: "pg_opfamily_is_visible" , .parameters: {"opclass_oid" , nullptr}, .macro: "true" }, |
75 | {.schema: "pg_catalog" , .name: "pg_table_is_visible" , .parameters: {"table_oid" , nullptr}, .macro: "true" }, |
76 | {.schema: "pg_catalog" , .name: "pg_ts_config_is_visible" , .parameters: {"config_oid" , nullptr}, .macro: "true" }, |
77 | {.schema: "pg_catalog" , .name: "pg_ts_dict_is_visible" , .parameters: {"dict_oid" , nullptr}, .macro: "true" }, |
78 | {.schema: "pg_catalog" , .name: "pg_ts_parser_is_visible" , .parameters: {"parser_oid" , nullptr}, .macro: "true" }, |
79 | {.schema: "pg_catalog" , .name: "pg_ts_template_is_visible" , .parameters: {"template_oid" , nullptr}, .macro: "true" }, |
80 | {.schema: "pg_catalog" , .name: "pg_type_is_visible" , .parameters: {"type_oid" , nullptr}, .macro: "true" }, |
81 | |
82 | {.schema: "pg_catalog" , .name: "pg_size_pretty" , .parameters: {"bytes" , nullptr}, .macro: "format_bytes(bytes)" }, |
83 | |
84 | {DEFAULT_SCHEMA, .name: "round_even" , .parameters: {"x" , "n" , nullptr}, .macro: "CASE ((abs(x) * power(10, n+1)) % 10) WHEN 5 THEN round(x/2, n) * 2 ELSE round(x, n) END" }, |
85 | {DEFAULT_SCHEMA, .name: "roundbankers" , .parameters: {"x" , "n" , nullptr}, .macro: "round_even(x, n)" }, |
86 | {DEFAULT_SCHEMA, .name: "nullif" , .parameters: {"a" , "b" , nullptr}, .macro: "CASE WHEN a=b THEN NULL ELSE a END" }, |
87 | {DEFAULT_SCHEMA, .name: "list_append" , .parameters: {"l" , "e" , nullptr}, .macro: "list_concat(l, list_value(e))" }, |
88 | {DEFAULT_SCHEMA, .name: "array_append" , .parameters: {"arr" , "el" , nullptr}, .macro: "list_append(arr, el)" }, |
89 | {DEFAULT_SCHEMA, .name: "list_prepend" , .parameters: {"e" , "l" , nullptr}, .macro: "list_concat(list_value(e), l)" }, |
90 | {DEFAULT_SCHEMA, .name: "array_prepend" , .parameters: {"el" , "arr" , nullptr}, .macro: "list_prepend(el, arr)" }, |
91 | {DEFAULT_SCHEMA, .name: "array_pop_back" , .parameters: {"arr" , nullptr}, .macro: "arr[:LEN(arr)-1]" }, |
92 | {DEFAULT_SCHEMA, .name: "array_pop_front" , .parameters: {"arr" , nullptr}, .macro: "arr[2:]" }, |
93 | {DEFAULT_SCHEMA, .name: "array_push_back" , .parameters: {"arr" , "e" , nullptr}, .macro: "list_concat(arr, list_value(e))" }, |
94 | {DEFAULT_SCHEMA, .name: "array_push_front" , .parameters: {"arr" , "e" , nullptr}, .macro: "list_concat(list_value(e), arr)" }, |
95 | {DEFAULT_SCHEMA, .name: "array_to_string" , .parameters: {"arr" , "sep" , nullptr}, .macro: "list_aggr(arr, 'string_agg', sep)" }, |
96 | {DEFAULT_SCHEMA, .name: "generate_subscripts" , .parameters: {"arr" , "dim" , nullptr}, .macro: "unnest(generate_series(1, array_length(arr, dim)))" }, |
97 | {DEFAULT_SCHEMA, .name: "fdiv" , .parameters: {"x" , "y" , nullptr}, .macro: "floor(x/y)" }, |
98 | {DEFAULT_SCHEMA, .name: "fmod" , .parameters: {"x" , "y" , nullptr}, .macro: "(x-y*floor(x/y))" }, |
99 | {DEFAULT_SCHEMA, .name: "count_if" , .parameters: {"l" , nullptr}, .macro: "sum(if(l, 1, 0))" }, |
100 | {DEFAULT_SCHEMA, .name: "split_part" , .parameters: {"string" , "delimiter" , "position" , nullptr}, .macro: "coalesce(string_split(string, delimiter)[position],'')" }, |
101 | |
102 | // algebraic list aggregates |
103 | {DEFAULT_SCHEMA, .name: "list_avg" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'avg')" }, |
104 | {DEFAULT_SCHEMA, .name: "list_var_samp" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'var_samp')" }, |
105 | {DEFAULT_SCHEMA, .name: "list_var_pop" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'var_pop')" }, |
106 | {DEFAULT_SCHEMA, .name: "list_stddev_pop" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'stddev_pop')" }, |
107 | {DEFAULT_SCHEMA, .name: "list_stddev_samp" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'stddev_samp')" }, |
108 | {DEFAULT_SCHEMA, .name: "list_sem" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'sem')" }, |
109 | |
110 | // distributive list aggregates |
111 | {DEFAULT_SCHEMA, .name: "list_approx_count_distinct" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'approx_count_distinct')" }, |
112 | {DEFAULT_SCHEMA, .name: "list_bit_xor" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'bit_xor')" }, |
113 | {DEFAULT_SCHEMA, .name: "list_bit_or" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'bit_or')" }, |
114 | {DEFAULT_SCHEMA, .name: "list_bit_and" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'bit_and')" }, |
115 | {DEFAULT_SCHEMA, .name: "list_bool_and" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'bool_and')" }, |
116 | {DEFAULT_SCHEMA, .name: "list_bool_or" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'bool_or')" }, |
117 | {DEFAULT_SCHEMA, .name: "list_count" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'count')" }, |
118 | {DEFAULT_SCHEMA, .name: "list_entropy" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'entropy')" }, |
119 | {DEFAULT_SCHEMA, .name: "list_last" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'last')" }, |
120 | {DEFAULT_SCHEMA, .name: "list_first" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'first')" }, |
121 | {DEFAULT_SCHEMA, .name: "list_any_value" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'any_value')" }, |
122 | {DEFAULT_SCHEMA, .name: "list_kurtosis" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'kurtosis')" }, |
123 | {DEFAULT_SCHEMA, .name: "list_min" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'min')" }, |
124 | {DEFAULT_SCHEMA, .name: "list_max" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'max')" }, |
125 | {DEFAULT_SCHEMA, .name: "list_product" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'product')" }, |
126 | {DEFAULT_SCHEMA, .name: "list_skewness" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'skewness')" }, |
127 | {DEFAULT_SCHEMA, .name: "list_sum" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'sum')" }, |
128 | {DEFAULT_SCHEMA, .name: "list_string_agg" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'string_agg')" }, |
129 | |
130 | // holistic list aggregates |
131 | {DEFAULT_SCHEMA, .name: "list_mode" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'mode')" }, |
132 | {DEFAULT_SCHEMA, .name: "list_median" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'median')" }, |
133 | {DEFAULT_SCHEMA, .name: "list_mad" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'mad')" }, |
134 | |
135 | // nested list aggregates |
136 | {DEFAULT_SCHEMA, .name: "list_histogram" , .parameters: {"l" , nullptr}, .macro: "list_aggr(l, 'histogram')" }, |
137 | |
138 | // date functions |
139 | {DEFAULT_SCHEMA, .name: "date_add" , .parameters: {"date" , "interval" , nullptr}, .macro: "date + interval" }, |
140 | |
141 | {.schema: nullptr, .name: nullptr, .parameters: {nullptr}, .macro: nullptr} |
142 | }; |
143 | |
144 | unique_ptr<CreateMacroInfo> DefaultFunctionGenerator::CreateInternalTableMacroInfo(DefaultMacro &default_macro, unique_ptr<MacroFunction> function) { |
145 | for (idx_t param_idx = 0; default_macro.parameters[param_idx] != nullptr; param_idx++) { |
146 | function->parameters.push_back( |
147 | x: make_uniq<ColumnRefExpression>(args&: default_macro.parameters[param_idx])); |
148 | } |
149 | |
150 | auto bind_info = make_uniq<CreateMacroInfo>(); |
151 | bind_info->schema = default_macro.schema; |
152 | bind_info->name = default_macro.name; |
153 | bind_info->temporary = true; |
154 | bind_info->internal = true; |
155 | bind_info->type = function->type == MacroType::TABLE_MACRO ? CatalogType::TABLE_MACRO_ENTRY : CatalogType::MACRO_ENTRY; |
156 | bind_info->function = std::move(function); |
157 | return bind_info; |
158 | |
159 | } |
160 | |
161 | unique_ptr<CreateMacroInfo> DefaultFunctionGenerator::CreateInternalMacroInfo(DefaultMacro &default_macro) { |
162 | // parse the expression |
163 | auto expressions = Parser::ParseExpressionList(select_list: default_macro.macro); |
164 | D_ASSERT(expressions.size() == 1); |
165 | |
166 | auto result = make_uniq<ScalarMacroFunction>(args: std::move(expressions[0])); |
167 | return CreateInternalTableMacroInfo(default_macro, function: std::move(result)); |
168 | } |
169 | |
170 | unique_ptr<CreateMacroInfo> DefaultFunctionGenerator::CreateInternalTableMacroInfo(DefaultMacro &default_macro) { |
171 | Parser parser; |
172 | parser.ParseQuery(query: default_macro.macro); |
173 | D_ASSERT(parser.statements.size() == 1); |
174 | D_ASSERT(parser.statements[0]->type == StatementType::SELECT_STATEMENT); |
175 | |
176 | auto &select = parser.statements[0]->Cast<SelectStatement>(); |
177 | auto result = make_uniq<TableMacroFunction>(args: std::move(select.node)); |
178 | return CreateInternalTableMacroInfo(default_macro, function: std::move(result)); |
179 | } |
180 | |
181 | static unique_ptr<CreateFunctionInfo> GetDefaultFunction(const string &input_schema, const string &input_name) { |
182 | auto schema = StringUtil::Lower(str: input_schema); |
183 | auto name = StringUtil::Lower(str: input_name); |
184 | for (idx_t index = 0; internal_macros[index].name != nullptr; index++) { |
185 | if (internal_macros[index].schema == schema && internal_macros[index].name == name) { |
186 | return DefaultFunctionGenerator::CreateInternalMacroInfo(default_macro&: internal_macros[index]); |
187 | } |
188 | } |
189 | return nullptr; |
190 | } |
191 | |
192 | DefaultFunctionGenerator::DefaultFunctionGenerator(Catalog &catalog, SchemaCatalogEntry &schema) |
193 | : DefaultGenerator(catalog), schema(schema) { |
194 | } |
195 | |
196 | unique_ptr<CatalogEntry> DefaultFunctionGenerator::CreateDefaultEntry(ClientContext &context, |
197 | const string &entry_name) { |
198 | auto info = GetDefaultFunction(input_schema: schema.name, input_name: entry_name); |
199 | if (info) { |
200 | return make_uniq_base<CatalogEntry, ScalarMacroCatalogEntry>(args&: catalog, args&: schema, args&: info->Cast<CreateMacroInfo>()); |
201 | } |
202 | return nullptr; |
203 | } |
204 | |
205 | vector<string> DefaultFunctionGenerator::GetDefaultEntries() { |
206 | vector<string> result; |
207 | for (idx_t index = 0; internal_macros[index].name != nullptr; index++) { |
208 | if (internal_macros[index].schema == schema.name) { |
209 | result.emplace_back(args&: internal_macros[index].name); |
210 | } |
211 | } |
212 | return result; |
213 | } |
214 | |
215 | } // namespace duckdb |
216 | |