/src/tarantool/src/box/func_def.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file. |
3 | | * |
4 | | * Redistribution and use in source and binary forms, with or |
5 | | * without modification, are permitted provided that the following |
6 | | * conditions are met: |
7 | | * |
8 | | * 1. Redistributions of source code must retain the above |
9 | | * copyright notice, this list of conditions and the |
10 | | * following disclaimer. |
11 | | * |
12 | | * 2. Redistributions in binary form must reproduce the above |
13 | | * copyright notice, this list of conditions and the following |
14 | | * disclaimer in the documentation and/or other materials |
15 | | * provided with the distribution. |
16 | | * |
17 | | * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND |
18 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
19 | | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
21 | | * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
22 | | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
25 | | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
26 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
28 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | | * SUCH DAMAGE. |
30 | | */ |
31 | | #include "func_def.h" |
32 | | #include "opt_def.h" |
33 | | #include "string.h" |
34 | | #include "diag.h" |
35 | | #include "error.h" |
36 | | #include "salad/grp_alloc.h" |
37 | | #include "trivia/util.h" |
38 | | |
39 | | #include <assert.h> |
40 | | #include <stdint.h> |
41 | | #include <stddef.h> |
42 | | #include <stdlib.h> |
43 | | |
44 | | const char *func_language_strs[] = { |
45 | | "LUA", "C", "SQL", "SQL_BUILTIN", "SQL_EXPR" |
46 | | }; |
47 | | |
48 | | const char *func_aggregate_strs[] = {"none", "group"}; |
49 | | |
50 | | const struct func_opts func_opts_default = { |
51 | | /* .is_multikey = */ false, |
52 | | /* .takes_raw_args = */ false, |
53 | | }; |
54 | | |
55 | | const struct opt_def func_opts_reg[] = { |
56 | | OPT_DEF("is_multikey", OPT_BOOL, struct func_opts, is_multikey), |
57 | | OPT_DEF("takes_raw_args", OPT_BOOL, struct func_opts, takes_raw_args), |
58 | | OPT_END, |
59 | | }; |
60 | | |
61 | | struct func_def * |
62 | | func_def_new(uint32_t fid, uint32_t uid, const char *name, uint32_t name_len, |
63 | | enum func_language language, const char *body, uint32_t body_len, |
64 | | const char *comment, uint32_t comment_len, const char *triggers) |
65 | 212 | { |
66 | 212 | struct func_def *def; |
67 | 212 | struct grp_alloc all = grp_alloc_initializer(); |
68 | 212 | grp_alloc_reserve_data(&all, sizeof(*def)); |
69 | 212 | grp_alloc_reserve_str(&all, name_len); |
70 | 212 | if (body_len != 0) |
71 | 0 | grp_alloc_reserve_str(&all, body_len); |
72 | 212 | if (comment_len != 0) |
73 | 0 | grp_alloc_reserve_str(&all, comment_len); |
74 | 212 | size_t triggers_len = 0; |
75 | 212 | if (triggers != NULL) { |
76 | 0 | const char *triggers_end = triggers; |
77 | 0 | mp_next(&triggers_end); |
78 | 0 | triggers_len = triggers_end - triggers; |
79 | 0 | grp_alloc_reserve_data(&all, triggers_len); |
80 | 0 | } |
81 | 212 | grp_alloc_use(&all, xmalloc(grp_alloc_size(&all))); |
82 | 212 | def = grp_alloc_create_data(&all, sizeof(*def)); |
83 | 212 | def->name = grp_alloc_create_str(&all, name, name_len); |
84 | 212 | def->name_len = name_len; |
85 | 212 | def->body = body_len == 0 ? NULL : |
86 | 212 | grp_alloc_create_str(&all, body, body_len); |
87 | 212 | def->comment = comment_len == 0 ? NULL : |
88 | 212 | grp_alloc_create_str(&all, comment, comment_len); |
89 | 212 | def->triggers = NULL; |
90 | 212 | if (triggers != NULL) { |
91 | 0 | def->triggers = grp_alloc_create_data(&all, triggers_len); |
92 | 0 | memcpy(def->triggers, triggers, triggers_len); |
93 | 0 | } |
94 | 212 | assert(grp_alloc_size(&all) == 0); |
95 | 212 | def->fid = fid; |
96 | 212 | def->uid = uid; |
97 | 212 | def->setuid = false; |
98 | 212 | def->is_deterministic = false; |
99 | 212 | def->is_sandboxed = false; |
100 | 212 | def->param_count = 0; |
101 | 212 | def->returns = FIELD_TYPE_ANY; |
102 | 212 | def->aggregate = FUNC_AGGREGATE_NONE; |
103 | 212 | def->language = language; |
104 | 212 | def->exports.all = 0; |
105 | 212 | func_opts_create(&def->opts); |
106 | 212 | return def; |
107 | 212 | } |
108 | | |
109 | | void |
110 | | func_def_delete(struct func_def *def) |
111 | 0 | { |
112 | 0 | free(def); |
113 | 0 | } |
114 | | |
115 | | static int |
116 | | func_opts_cmp(const struct func_opts *o1, const struct func_opts *o2) |
117 | 0 | { |
118 | 0 | if (o1->is_multikey != o2->is_multikey) |
119 | 0 | return o1->is_multikey - o2->is_multikey; |
120 | 0 | if (o1->takes_raw_args != o2->takes_raw_args) |
121 | 0 | return o1->takes_raw_args - o2->takes_raw_args; |
122 | 0 | return 0; |
123 | 0 | } |
124 | | |
125 | | int |
126 | | func_def_cmp(const struct func_def *def1, const struct func_def *def2) |
127 | 0 | { |
128 | 0 | if (def1->fid != def2->fid) |
129 | 0 | return def1->fid - def2->fid; |
130 | 0 | if (def1->uid != def2->uid) |
131 | 0 | return def1->uid - def2->uid; |
132 | 0 | if (def1->setuid != def2->setuid) |
133 | 0 | return def1->setuid - def2->setuid; |
134 | 0 | if (def1->language != def2->language) |
135 | 0 | return def1->language - def2->language; |
136 | 0 | if (def1->is_deterministic != def2->is_deterministic) |
137 | 0 | return def1->is_deterministic - def2->is_deterministic; |
138 | 0 | if (def1->is_sandboxed != def2->is_sandboxed) |
139 | 0 | return def1->is_sandboxed - def2->is_sandboxed; |
140 | 0 | if (strcmp(def1->name, def2->name) != 0) |
141 | 0 | return strcmp(def1->name, def2->name); |
142 | 0 | if ((def1->body != NULL) != (def2->body != NULL)) |
143 | 0 | return def1->body - def2->body; |
144 | 0 | if (def1->body != NULL && strcmp(def1->body, def2->body) != 0) |
145 | 0 | return strcmp(def1->body, def2->body); |
146 | 0 | if (def1->returns != def2->returns) |
147 | 0 | return def1->returns - def2->returns; |
148 | 0 | if (def1->exports.all != def2->exports.all) |
149 | 0 | return def1->exports.all - def2->exports.all; |
150 | 0 | if (def1->aggregate != def2->aggregate) |
151 | 0 | return def1->aggregate - def2->aggregate; |
152 | 0 | if (def1->param_count != def2->param_count) |
153 | 0 | return def1->param_count - def2->param_count; |
154 | 0 | if ((def1->comment != NULL) != (def2->comment != NULL)) |
155 | 0 | return def1->comment - def2->comment; |
156 | 0 | if (def1->comment != NULL && strcmp(def1->comment, def2->comment) != 0) |
157 | 0 | return strcmp(def1->comment, def2->comment); |
158 | 0 | if ((def1->triggers != NULL) != (def2->triggers != NULL)) |
159 | 0 | return def1->triggers - def2->triggers; |
160 | 0 | if (def1->triggers != NULL) { |
161 | 0 | const char *triggers1_end = def1->triggers; |
162 | 0 | mp_next(&triggers1_end); |
163 | 0 | const char *triggers2_end = def2->triggers; |
164 | 0 | mp_next(&triggers2_end); |
165 | 0 | size_t triggers1_len = triggers1_end - def1->triggers; |
166 | 0 | size_t triggers2_len = triggers2_end - def2->triggers; |
167 | 0 | if (triggers1_len != triggers2_len) |
168 | 0 | return triggers1_len - triggers2_len; |
169 | 0 | int cmp_res = memcmp(def1->triggers, def2->triggers, |
170 | 0 | triggers1_len); |
171 | 0 | if (cmp_res != 0) |
172 | 0 | return cmp_res; |
173 | 0 | } |
174 | 0 | return func_opts_cmp(&def1->opts, &def2->opts); |
175 | 0 | } |
176 | | |
177 | | struct func_def * |
178 | | func_def_dup(const struct func_def *def) |
179 | 0 | { |
180 | 0 | struct func_def *copy = func_def_new( |
181 | 0 | def->fid, def->uid, def->name, def->name_len, def->language, |
182 | 0 | def->body, def->body != NULL ? strlen(def->body) : 0, |
183 | 0 | def->comment, def->comment != NULL ? strlen(def->comment) : 0, |
184 | 0 | def->triggers); |
185 | 0 | copy->setuid = def->setuid; |
186 | 0 | copy->is_deterministic = def->is_deterministic; |
187 | 0 | copy->is_sandboxed = def->is_sandboxed; |
188 | 0 | copy->param_count = def->param_count; |
189 | 0 | copy->returns = def->returns; |
190 | 0 | copy->aggregate = def->aggregate; |
191 | 0 | copy->exports.all = def->exports.all; |
192 | 0 | copy->opts = def->opts; |
193 | 0 | return copy; |
194 | 0 | } |
195 | | |
196 | | /** |
197 | | * Check if a function definition is valid. |
198 | | * @retval 0 the definition is correct |
199 | | * @retval -1 the definition has incompatible options set, |
200 | | * diagnostics message is provided |
201 | | */ |
202 | | int |
203 | | func_def_check(const struct func_def *def) |
204 | 0 | { |
205 | 0 | switch (def->language) { |
206 | 0 | case FUNC_LANGUAGE_C: |
207 | 0 | if (def->body != NULL || def->is_sandboxed) { |
208 | 0 | diag_set(ClientError, ER_CREATE_FUNCTION, def->name, |
209 | 0 | "body and is_sandboxed options are not compatible " |
210 | 0 | "with C language"); |
211 | 0 | return -1; |
212 | 0 | } |
213 | 0 | break; |
214 | 0 | case FUNC_LANGUAGE_LUA: |
215 | 0 | if (def->is_sandboxed && def->body == NULL) { |
216 | 0 | diag_set(ClientError, ER_CREATE_FUNCTION, def->name, |
217 | 0 | "is_sandboxed option may be set only for a persistent " |
218 | 0 | "Lua function (one with a non-empty body)"); |
219 | 0 | return -1; |
220 | 0 | } |
221 | 0 | break; |
222 | 0 | default: |
223 | 0 | break; |
224 | 0 | } |
225 | 0 | return 0; |
226 | 0 | } |