]>
Commit | Line | Data |
---|---|---|
cb22974d | 1 | /* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. |
f85f8ebb BP |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | ||
18 | #include "table.h" | |
19 | ||
87ab878c | 20 | #include <limits.h> |
f85f8ebb BP |
21 | |
22 | #include "json.h" | |
23 | #include "column.h" | |
24 | #include "ovsdb-error.h" | |
25 | #include "ovsdb-parser.h" | |
26 | #include "ovsdb-types.h" | |
27 | #include "row.h" | |
28 | ||
29 | static void | |
30 | add_column(struct ovsdb_table_schema *ts, struct ovsdb_column *column) | |
31 | { | |
cb22974d | 32 | ovs_assert(!shash_find(&ts->columns, column->name)); |
f85f8ebb BP |
33 | column->index = shash_count(&ts->columns); |
34 | shash_add(&ts->columns, column->name, column); | |
35 | } | |
36 | ||
37 | struct ovsdb_table_schema * | |
87ab878c | 38 | ovsdb_table_schema_create(const char *name, bool mutable, |
c5f341ab | 39 | unsigned int max_rows, bool is_root) |
f85f8ebb BP |
40 | { |
41 | struct ovsdb_column *uuid, *version; | |
42 | struct ovsdb_table_schema *ts; | |
43 | ||
44 | ts = xzalloc(sizeof *ts); | |
45 | ts->name = xstrdup(name); | |
f85f8ebb BP |
46 | ts->mutable = mutable; |
47 | shash_init(&ts->columns); | |
87ab878c | 48 | ts->max_rows = max_rows; |
c5f341ab | 49 | ts->is_root = is_root; |
f85f8ebb | 50 | |
2e57b537 | 51 | uuid = ovsdb_column_create("_uuid", false, true, &ovsdb_type_uuid); |
f85f8ebb | 52 | add_column(ts, uuid); |
cb22974d | 53 | ovs_assert(uuid->index == OVSDB_COL_UUID); |
f85f8ebb | 54 | |
2e57b537 | 55 | version = ovsdb_column_create("_version", false, false, &ovsdb_type_uuid); |
f85f8ebb | 56 | add_column(ts, version); |
cb22974d | 57 | ovs_assert(version->index == OVSDB_COL_VERSION); |
f85f8ebb | 58 | |
6910a6e6 BP |
59 | ts->n_indexes = 0; |
60 | ts->indexes = NULL; | |
61 | ||
f85f8ebb BP |
62 | return ts; |
63 | } | |
64 | ||
58985e09 BP |
65 | struct ovsdb_table_schema * |
66 | ovsdb_table_schema_clone(const struct ovsdb_table_schema *old) | |
67 | { | |
68 | struct ovsdb_table_schema *new; | |
69 | struct shash_node *node; | |
6910a6e6 | 70 | size_t i; |
58985e09 | 71 | |
c5f341ab BP |
72 | new = ovsdb_table_schema_create(old->name, old->mutable, |
73 | old->max_rows, old->is_root); | |
58985e09 BP |
74 | SHASH_FOR_EACH (node, &old->columns) { |
75 | const struct ovsdb_column *column = node->data; | |
76 | ||
77 | if (column->name[0] == '_') { | |
78 | /* Added automatically by ovsdb_table_schema_create(). */ | |
79 | continue; | |
80 | } | |
81 | ||
82 | add_column(new, ovsdb_column_clone(column)); | |
83 | } | |
6910a6e6 BP |
84 | |
85 | new->n_indexes = old->n_indexes; | |
86 | new->indexes = xmalloc(new->n_indexes * sizeof *new->indexes); | |
87 | for (i = 0; i < new->n_indexes; i++) { | |
88 | const struct ovsdb_column_set *old_index = &old->indexes[i]; | |
89 | struct ovsdb_column_set *new_index = &new->indexes[i]; | |
90 | size_t j; | |
91 | ||
92 | ovsdb_column_set_init(new_index); | |
93 | for (j = 0; j < old_index->n_columns; j++) { | |
94 | const struct ovsdb_column *old_column = old_index->columns[j]; | |
95 | const struct ovsdb_column *new_column; | |
96 | ||
97 | new_column = ovsdb_table_schema_get_column(new, old_column->name); | |
98 | ovsdb_column_set_add(new_index, new_column); | |
99 | } | |
100 | } | |
101 | ||
58985e09 BP |
102 | return new; |
103 | } | |
104 | ||
f85f8ebb BP |
105 | void |
106 | ovsdb_table_schema_destroy(struct ovsdb_table_schema *ts) | |
107 | { | |
108 | struct shash_node *node; | |
6910a6e6 BP |
109 | size_t i; |
110 | ||
111 | for (i = 0; i < ts->n_indexes; i++) { | |
112 | ovsdb_column_set_destroy(&ts->indexes[i]); | |
113 | } | |
114 | free(ts->indexes); | |
f85f8ebb BP |
115 | |
116 | SHASH_FOR_EACH (node, &ts->columns) { | |
117 | ovsdb_column_destroy(node->data); | |
118 | } | |
119 | shash_destroy(&ts->columns); | |
f85f8ebb BP |
120 | free(ts->name); |
121 | free(ts); | |
122 | } | |
123 | ||
124 | struct ovsdb_error * | |
125 | ovsdb_table_schema_from_json(const struct json *json, const char *name, | |
126 | struct ovsdb_table_schema **tsp) | |
127 | { | |
128 | struct ovsdb_table_schema *ts; | |
6910a6e6 | 129 | const struct json *columns, *mutable, *max_rows, *is_root, *indexes; |
f85f8ebb BP |
130 | struct shash_node *node; |
131 | struct ovsdb_parser parser; | |
132 | struct ovsdb_error *error; | |
87ab878c | 133 | long long int n_max_rows; |
f85f8ebb BP |
134 | |
135 | *tsp = NULL; | |
136 | ||
137 | ovsdb_parser_init(&parser, json, "table schema for table %s", name); | |
f85f8ebb BP |
138 | columns = ovsdb_parser_member(&parser, "columns", OP_OBJECT); |
139 | mutable = ovsdb_parser_member(&parser, "mutable", | |
140 | OP_TRUE | OP_FALSE | OP_OPTIONAL); | |
87ab878c BP |
141 | max_rows = ovsdb_parser_member(&parser, "maxRows", |
142 | OP_INTEGER | OP_OPTIONAL); | |
c5f341ab | 143 | is_root = ovsdb_parser_member(&parser, "isRoot", OP_BOOLEAN | OP_OPTIONAL); |
6910a6e6 | 144 | indexes = ovsdb_parser_member(&parser, "indexes", OP_ARRAY | OP_OPTIONAL); |
f85f8ebb BP |
145 | error = ovsdb_parser_finish(&parser); |
146 | if (error) { | |
147 | return error; | |
148 | } | |
149 | ||
87ab878c BP |
150 | if (max_rows) { |
151 | if (json_integer(max_rows) <= 0) { | |
152 | return ovsdb_syntax_error(json, NULL, | |
153 | "maxRows must be at least 1"); | |
154 | } | |
155 | n_max_rows = max_rows->u.integer; | |
156 | } else { | |
157 | n_max_rows = UINT_MAX; | |
158 | } | |
159 | ||
f85f8ebb BP |
160 | if (shash_is_empty(json_object(columns))) { |
161 | return ovsdb_syntax_error(json, NULL, | |
162 | "table must have at least one column"); | |
163 | } | |
164 | ||
165 | ts = ovsdb_table_schema_create(name, | |
87ab878c | 166 | mutable ? json_boolean(mutable) : true, |
c5f341ab BP |
167 | MIN(n_max_rows, UINT_MAX), |
168 | is_root ? json_boolean(is_root) : false); | |
f85f8ebb BP |
169 | SHASH_FOR_EACH (node, json_object(columns)) { |
170 | struct ovsdb_column *column; | |
171 | ||
172 | if (node->name[0] == '_') { | |
173 | error = ovsdb_syntax_error(json, NULL, "names beginning with " | |
174 | "\"_\" are reserved"); | |
b966380b BP |
175 | } else if (!ovsdb_parser_is_id(node->name)) { |
176 | error = ovsdb_syntax_error(json, NULL, "name must be a valid id"); | |
f85f8ebb BP |
177 | } else { |
178 | error = ovsdb_column_from_json(node->data, node->name, &column); | |
179 | } | |
180 | if (error) { | |
6910a6e6 | 181 | goto error; |
f85f8ebb BP |
182 | } |
183 | ||
184 | add_column(ts, column); | |
185 | } | |
6910a6e6 BP |
186 | |
187 | if (indexes) { | |
188 | size_t i; | |
189 | ||
190 | ts->indexes = xmalloc(indexes->u.array.n * sizeof *ts->indexes); | |
191 | for (i = 0; i < indexes->u.array.n; i++) { | |
192 | struct ovsdb_column_set *index = &ts->indexes[i]; | |
193 | size_t j; | |
194 | ||
195 | error = ovsdb_column_set_from_json(indexes->u.array.elems[i], | |
196 | ts, index); | |
197 | if (error) { | |
198 | goto error; | |
199 | } | |
200 | if (index->n_columns == 0) { | |
201 | error = ovsdb_syntax_error(json, NULL, "index must have " | |
202 | "at least one column"); | |
203 | goto error; | |
204 | } | |
205 | ts->n_indexes++; | |
206 | ||
207 | for (j = 0; j < index->n_columns; j++) { | |
208 | const struct ovsdb_column *column = index->columns[j]; | |
209 | ||
210 | if (!column->persistent) { | |
211 | error = ovsdb_syntax_error(json, NULL, "ephemeral columns " | |
212 | "(such as %s) may not be " | |
213 | "indexed", column->name); | |
214 | goto error; | |
215 | } | |
216 | } | |
217 | } | |
218 | } | |
219 | ||
f85f8ebb | 220 | *tsp = ts; |
e3c17733 | 221 | return NULL; |
6910a6e6 BP |
222 | |
223 | error: | |
224 | ovsdb_table_schema_destroy(ts); | |
225 | return error; | |
f85f8ebb BP |
226 | } |
227 | ||
c5f341ab BP |
228 | /* Returns table schema 'ts' serialized into JSON. |
229 | * | |
230 | * The "isRoot" member is included in the JSON only if its value would differ | |
231 | * from 'default_is_root'. Ordinarily 'default_is_root' should be false, | |
232 | * because ordinarily a table would be not be part of the root set if its | |
233 | * "isRoot" member is omitted. However, garbage collection was not orginally | |
234 | * included in OVSDB, so in older schemas that do not include any "isRoot" | |
235 | * members, every table is implicitly part of the root set. To serialize such | |
236 | * a schema in a way that can be read by older OVSDB tools, specify | |
237 | * 'default_is_root' as true. */ | |
f85f8ebb | 238 | struct json * |
c5f341ab BP |
239 | ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts, |
240 | bool default_is_root) | |
f85f8ebb BP |
241 | { |
242 | struct json *json, *columns; | |
243 | struct shash_node *node; | |
244 | ||
245 | json = json_object_create(); | |
f85f8ebb BP |
246 | if (!ts->mutable) { |
247 | json_object_put(json, "mutable", json_boolean_create(false)); | |
248 | } | |
c5f341ab BP |
249 | if (default_is_root != ts->is_root) { |
250 | json_object_put(json, "isRoot", json_boolean_create(ts->is_root)); | |
251 | } | |
f85f8ebb BP |
252 | |
253 | columns = json_object_create(); | |
254 | ||
255 | SHASH_FOR_EACH (node, &ts->columns) { | |
bd76d25d | 256 | const struct ovsdb_column *column = node->data; |
f85f8ebb BP |
257 | if (node->name[0] != '_') { |
258 | json_object_put(columns, column->name, | |
259 | ovsdb_column_to_json(column)); | |
260 | } | |
261 | } | |
262 | json_object_put(json, "columns", columns); | |
87ab878c BP |
263 | if (ts->max_rows != UINT_MAX) { |
264 | json_object_put(json, "maxRows", json_integer_create(ts->max_rows)); | |
265 | } | |
f85f8ebb | 266 | |
6910a6e6 BP |
267 | if (ts->n_indexes) { |
268 | struct json **indexes; | |
269 | size_t i; | |
270 | ||
271 | indexes = xmalloc(ts->n_indexes * sizeof *indexes); | |
272 | for (i = 0; i < ts->n_indexes; i++) { | |
273 | indexes[i] = ovsdb_column_set_to_json(&ts->indexes[i]); | |
274 | } | |
275 | json_object_put(json, "indexes", | |
276 | json_array_create(indexes, ts->n_indexes)); | |
277 | } | |
278 | ||
f85f8ebb BP |
279 | return json; |
280 | } | |
281 | ||
282 | const struct ovsdb_column * | |
283 | ovsdb_table_schema_get_column(const struct ovsdb_table_schema *ts, | |
284 | const char *name) | |
285 | { | |
286 | return shash_find_data(&ts->columns, name); | |
287 | } | |
288 | \f | |
289 | struct ovsdb_table * | |
290 | ovsdb_table_create(struct ovsdb_table_schema *ts) | |
291 | { | |
292 | struct ovsdb_table *table; | |
6910a6e6 | 293 | size_t i; |
f85f8ebb BP |
294 | |
295 | table = xmalloc(sizeof *table); | |
296 | table->schema = ts; | |
71c93bd4 | 297 | table->txn_table = NULL; |
6910a6e6 BP |
298 | table->indexes = xmalloc(ts->n_indexes * sizeof *table->indexes); |
299 | for (i = 0; i < ts->n_indexes; i++) { | |
300 | hmap_init(&table->indexes[i]); | |
301 | } | |
f85f8ebb BP |
302 | hmap_init(&table->rows); |
303 | ||
304 | return table; | |
305 | } | |
306 | ||
307 | void | |
308 | ovsdb_table_destroy(struct ovsdb_table *table) | |
309 | { | |
310 | if (table) { | |
311 | struct ovsdb_row *row, *next; | |
6910a6e6 | 312 | size_t i; |
f85f8ebb | 313 | |
4e8e4213 | 314 | HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) { |
f85f8ebb BP |
315 | ovsdb_row_destroy(row); |
316 | } | |
317 | hmap_destroy(&table->rows); | |
318 | ||
6910a6e6 BP |
319 | for (i = 0; i < table->schema->n_indexes; i++) { |
320 | hmap_destroy(&table->indexes[i]); | |
321 | } | |
322 | free(table->indexes); | |
323 | ||
f85f8ebb BP |
324 | ovsdb_table_schema_destroy(table->schema); |
325 | free(table); | |
326 | } | |
327 | } | |
328 | ||
629cd2f1 BP |
329 | const struct ovsdb_row * |
330 | ovsdb_table_get_row(const struct ovsdb_table *table, const struct uuid *uuid) | |
f85f8ebb BP |
331 | { |
332 | struct ovsdb_row *row; | |
333 | ||
4e8e4213 | 334 | HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) { |
f85f8ebb BP |
335 | if (uuid_equals(ovsdb_row_get_uuid(row), uuid)) { |
336 | return row; | |
337 | } | |
338 | } | |
339 | ||
340 | return NULL; | |
341 | } |