]>
Commit | Line | Data |
---|---|---|
ebc56baa | 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 "row.h" | |
19 | ||
f85f8ebb BP |
20 | #include <stddef.h> |
21 | ||
3e8a2ad1 | 22 | #include "openvswitch/dynamic-string.h" |
ee89ea7b | 23 | #include "openvswitch/json.h" |
f85f8ebb | 24 | #include "ovsdb-error.h" |
ee89ea7b | 25 | #include "openvswitch/shash.h" |
f85f8ebb BP |
26 | #include "sort.h" |
27 | #include "table.h" | |
ee89ea7b | 28 | #include "util.h" |
f85f8ebb BP |
29 | |
30 | static struct ovsdb_row * | |
31 | allocate_row(const struct ovsdb_table *table) | |
32 | { | |
33 | size_t n_fields = shash_count(&table->schema->columns); | |
6910a6e6 | 34 | size_t n_indexes = table->schema->n_indexes; |
f85f8ebb | 35 | size_t row_size = (offsetof(struct ovsdb_row, fields) |
6910a6e6 BP |
36 | + sizeof(struct ovsdb_datum) * n_fields |
37 | + sizeof(struct hmap_node) * n_indexes); | |
f85f8ebb | 38 | struct ovsdb_row *row = xmalloc(row_size); |
ebc56baa | 39 | row->table = CONST_CAST(struct ovsdb_table *, table); |
f85f8ebb | 40 | row->txn_row = NULL; |
417e7e66 BW |
41 | ovs_list_init(&row->src_refs); |
42 | ovs_list_init(&row->dst_refs); | |
0d0f05b9 | 43 | row->n_refs = 0; |
f85f8ebb BP |
44 | return row; |
45 | } | |
46 | ||
1b1d2e6d BP |
47 | /* Creates and returns a new row suitable for insertion into 'table'. Does not |
48 | * actually insert the row into 'table' (use ovsdb_txn_row_insert()). The | |
49 | * caller must assign a UUID to the row. */ | |
f85f8ebb BP |
50 | struct ovsdb_row * |
51 | ovsdb_row_create(const struct ovsdb_table *table) | |
52 | { | |
53 | struct shash_node *node; | |
54 | struct ovsdb_row *row; | |
55 | ||
56 | row = allocate_row(table); | |
57 | SHASH_FOR_EACH (node, &table->schema->columns) { | |
58 | const struct ovsdb_column *column = node->data; | |
59 | ovsdb_datum_init_default(&row->fields[column->index], &column->type); | |
60 | } | |
61 | return row; | |
62 | } | |
63 | ||
64 | struct ovsdb_row * | |
65 | ovsdb_row_clone(const struct ovsdb_row *old) | |
66 | { | |
67 | const struct ovsdb_table *table = old->table; | |
68 | const struct shash_node *node; | |
69 | struct ovsdb_row *new; | |
70 | ||
71 | new = allocate_row(table); | |
72 | SHASH_FOR_EACH (node, &table->schema->columns) { | |
73 | const struct ovsdb_column *column = node->data; | |
74 | ovsdb_datum_clone(&new->fields[column->index], | |
75 | &old->fields[column->index], | |
76 | &column->type); | |
77 | } | |
78 | return new; | |
79 | } | |
80 | ||
81 | /* The caller is responsible for ensuring that 'row' has been removed from its | |
82 | * table and that it is not participating in a transaction. */ | |
83 | void | |
84 | ovsdb_row_destroy(struct ovsdb_row *row) | |
85 | { | |
86 | if (row) { | |
87 | const struct ovsdb_table *table = row->table; | |
7360012b | 88 | struct ovsdb_weak_ref *weak, *next; |
f85f8ebb BP |
89 | const struct shash_node *node; |
90 | ||
4e8e4213 | 91 | LIST_FOR_EACH_SAFE (weak, next, dst_node, &row->dst_refs) { |
417e7e66 BW |
92 | ovs_list_remove(&weak->src_node); |
93 | ovs_list_remove(&weak->dst_node); | |
7360012b BP |
94 | free(weak); |
95 | } | |
96 | ||
4e8e4213 | 97 | LIST_FOR_EACH_SAFE (weak, next, src_node, &row->src_refs) { |
417e7e66 BW |
98 | ovs_list_remove(&weak->src_node); |
99 | ovs_list_remove(&weak->dst_node); | |
7360012b BP |
100 | free(weak); |
101 | } | |
102 | ||
f85f8ebb BP |
103 | SHASH_FOR_EACH (node, &table->schema->columns) { |
104 | const struct ovsdb_column *column = node->data; | |
105 | ovsdb_datum_destroy(&row->fields[column->index], &column->type); | |
106 | } | |
107 | free(row); | |
108 | } | |
109 | } | |
110 | ||
111 | uint32_t | |
112 | ovsdb_row_hash_columns(const struct ovsdb_row *row, | |
113 | const struct ovsdb_column_set *columns, | |
114 | uint32_t basis) | |
115 | { | |
116 | size_t i; | |
117 | ||
118 | for (i = 0; i < columns->n_columns; i++) { | |
119 | const struct ovsdb_column *column = columns->columns[i]; | |
120 | basis = ovsdb_datum_hash(&row->fields[column->index], &column->type, | |
121 | basis); | |
122 | } | |
123 | ||
124 | return basis; | |
125 | } | |
126 | ||
127 | int | |
128 | ovsdb_row_compare_columns_3way(const struct ovsdb_row *a, | |
129 | const struct ovsdb_row *b, | |
130 | const struct ovsdb_column_set *columns) | |
131 | { | |
132 | size_t i; | |
133 | ||
134 | for (i = 0; i < columns->n_columns; i++) { | |
135 | const struct ovsdb_column *column = columns->columns[i]; | |
136 | int cmp = ovsdb_datum_compare_3way(&a->fields[column->index], | |
137 | &b->fields[column->index], | |
138 | &column->type); | |
139 | if (cmp) { | |
140 | return cmp; | |
141 | } | |
142 | } | |
143 | ||
144 | return 0; | |
145 | } | |
146 | ||
147 | bool | |
148 | ovsdb_row_equal_columns(const struct ovsdb_row *a, | |
149 | const struct ovsdb_row *b, | |
150 | const struct ovsdb_column_set *columns) | |
151 | { | |
152 | size_t i; | |
153 | ||
154 | for (i = 0; i < columns->n_columns; i++) { | |
155 | const struct ovsdb_column *column = columns->columns[i]; | |
156 | if (!ovsdb_datum_equals(&a->fields[column->index], | |
157 | &b->fields[column->index], | |
158 | &column->type)) { | |
159 | return false; | |
160 | } | |
161 | } | |
162 | ||
163 | return true; | |
164 | } | |
165 | ||
166 | void | |
167 | ovsdb_row_update_columns(struct ovsdb_row *dst, | |
168 | const struct ovsdb_row *src, | |
169 | const struct ovsdb_column_set *columns) | |
170 | { | |
171 | size_t i; | |
172 | ||
173 | for (i = 0; i < columns->n_columns; i++) { | |
174 | const struct ovsdb_column *column = columns->columns[i]; | |
175 | ovsdb_datum_destroy(&dst->fields[column->index], &column->type); | |
176 | ovsdb_datum_clone(&dst->fields[column->index], | |
177 | &src->fields[column->index], | |
178 | &column->type); | |
179 | } | |
180 | } | |
181 | ||
25d49835 BP |
182 | /* Appends the string form of the value in 'row' of each of the columns in |
183 | * 'columns' to 'out', e.g. "1, \"xyz\", and [1, 2, 3]". */ | |
184 | void | |
185 | ovsdb_row_columns_to_string(const struct ovsdb_row *row, | |
186 | const struct ovsdb_column_set *columns, | |
187 | struct ds *out) | |
188 | { | |
189 | size_t i; | |
190 | ||
191 | for (i = 0; i < columns->n_columns; i++) { | |
192 | const struct ovsdb_column *column = columns->columns[i]; | |
193 | ||
194 | ds_put_cstr(out, english_list_delimiter(i, columns->n_columns)); | |
195 | ovsdb_datum_to_string(&row->fields[column->index], &column->type, out); | |
196 | } | |
197 | } | |
198 | ||
f85f8ebb BP |
199 | struct ovsdb_error * |
200 | ovsdb_row_from_json(struct ovsdb_row *row, const struct json *json, | |
fbf925e4 | 201 | struct ovsdb_symbol_table *symtab, |
f85f8ebb BP |
202 | struct ovsdb_column_set *included) |
203 | { | |
204 | struct ovsdb_table_schema *schema = row->table->schema; | |
205 | struct ovsdb_error *error; | |
206 | struct shash_node *node; | |
207 | ||
208 | if (json->type != JSON_OBJECT) { | |
209 | return ovsdb_syntax_error(json, NULL, "row must be JSON object"); | |
210 | } | |
211 | ||
212 | SHASH_FOR_EACH (node, json_object(json)) { | |
213 | const char *column_name = node->name; | |
214 | const struct ovsdb_column *column; | |
215 | struct ovsdb_datum datum; | |
216 | ||
217 | column = ovsdb_table_schema_get_column(schema, column_name); | |
218 | if (!column) { | |
219 | return ovsdb_syntax_error(json, "unknown column", | |
220 | "No column %s in table %s.", | |
221 | column_name, schema->name); | |
222 | } | |
223 | ||
224 | error = ovsdb_datum_from_json(&datum, &column->type, node->data, | |
225 | symtab); | |
226 | if (error) { | |
227 | return error; | |
228 | } | |
229 | ovsdb_datum_swap(&row->fields[column->index], &datum); | |
230 | ovsdb_datum_destroy(&datum, &column->type); | |
231 | if (included) { | |
232 | ovsdb_column_set_add(included, column); | |
233 | } | |
234 | } | |
235 | ||
236 | return NULL; | |
237 | } | |
238 | ||
239 | static void | |
240 | put_json_column(struct json *object, const struct ovsdb_row *row, | |
241 | const struct ovsdb_column *column) | |
242 | { | |
243 | json_object_put(object, column->name, | |
244 | ovsdb_datum_to_json(&row->fields[column->index], | |
245 | &column->type)); | |
246 | } | |
247 | ||
248 | struct json * | |
249 | ovsdb_row_to_json(const struct ovsdb_row *row, | |
250 | const struct ovsdb_column_set *columns) | |
251 | { | |
252 | struct json *json; | |
253 | size_t i; | |
254 | ||
255 | json = json_object_create(); | |
256 | for (i = 0; i < columns->n_columns; i++) { | |
257 | put_json_column(json, row, columns->columns[i]); | |
258 | } | |
259 | return json; | |
260 | } | |
261 | \f | |
262 | void | |
263 | ovsdb_row_set_init(struct ovsdb_row_set *set) | |
264 | { | |
265 | set->rows = NULL; | |
266 | set->n_rows = set->allocated_rows = 0; | |
267 | } | |
268 | ||
269 | void | |
270 | ovsdb_row_set_destroy(struct ovsdb_row_set *set) | |
271 | { | |
272 | free(set->rows); | |
273 | } | |
274 | ||
275 | void | |
276 | ovsdb_row_set_add_row(struct ovsdb_row_set *set, const struct ovsdb_row *row) | |
277 | { | |
278 | if (set->n_rows >= set->allocated_rows) { | |
279 | set->rows = x2nrealloc(set->rows, &set->allocated_rows, | |
280 | sizeof *set->rows); | |
281 | } | |
282 | set->rows[set->n_rows++] = row; | |
283 | } | |
284 | ||
285 | struct json * | |
286 | ovsdb_row_set_to_json(const struct ovsdb_row_set *rows, | |
287 | const struct ovsdb_column_set *columns) | |
288 | { | |
289 | struct json **json_rows; | |
290 | size_t i; | |
291 | ||
292 | json_rows = xmalloc(rows->n_rows * sizeof *json_rows); | |
293 | for (i = 0; i < rows->n_rows; i++) { | |
294 | json_rows[i] = ovsdb_row_to_json(rows->rows[i], columns); | |
295 | } | |
296 | return json_array_create(json_rows, rows->n_rows); | |
297 | } | |
298 | ||
299 | struct ovsdb_row_set_sort_cbdata { | |
300 | struct ovsdb_row_set *set; | |
301 | const struct ovsdb_column_set *columns; | |
302 | }; | |
303 | ||
304 | static int | |
305 | ovsdb_row_set_sort_compare_cb(size_t a, size_t b, void *cbdata_) | |
306 | { | |
307 | struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_; | |
308 | return ovsdb_row_compare_columns_3way(cbdata->set->rows[a], | |
309 | cbdata->set->rows[b], | |
310 | cbdata->columns); | |
311 | } | |
312 | ||
313 | static void | |
314 | ovsdb_row_set_sort_swap_cb(size_t a, size_t b, void *cbdata_) | |
315 | { | |
316 | struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_; | |
317 | const struct ovsdb_row *tmp = cbdata->set->rows[a]; | |
318 | cbdata->set->rows[a] = cbdata->set->rows[b]; | |
319 | cbdata->set->rows[b] = tmp; | |
320 | } | |
321 | ||
322 | void | |
323 | ovsdb_row_set_sort(struct ovsdb_row_set *set, | |
324 | const struct ovsdb_column_set *columns) | |
325 | { | |
326 | if (columns && columns->n_columns && set->n_rows > 1) { | |
327 | struct ovsdb_row_set_sort_cbdata cbdata; | |
328 | cbdata.set = set; | |
329 | cbdata.columns = columns; | |
330 | sort(set->n_rows, | |
331 | ovsdb_row_set_sort_compare_cb, | |
332 | ovsdb_row_set_sort_swap_cb, | |
333 | &cbdata); | |
334 | } | |
335 | } | |
336 | \f | |
337 | void | |
338 | ovsdb_row_hash_init(struct ovsdb_row_hash *rh, | |
339 | const struct ovsdb_column_set *columns) | |
340 | { | |
341 | hmap_init(&rh->rows); | |
342 | ovsdb_column_set_clone(&rh->columns, columns); | |
343 | } | |
344 | ||
345 | void | |
346 | ovsdb_row_hash_destroy(struct ovsdb_row_hash *rh, bool destroy_rows) | |
347 | { | |
4ec3d7c7 | 348 | struct ovsdb_row_hash_node *node; |
f85f8ebb | 349 | |
4ec3d7c7 | 350 | HMAP_FOR_EACH_POP (node, hmap_node, &rh->rows) { |
f85f8ebb | 351 | if (destroy_rows) { |
ebc56baa | 352 | ovsdb_row_destroy(CONST_CAST(struct ovsdb_row *, node->row)); |
f85f8ebb BP |
353 | } |
354 | free(node); | |
355 | } | |
356 | hmap_destroy(&rh->rows); | |
357 | ovsdb_column_set_destroy(&rh->columns); | |
358 | } | |
359 | ||
360 | size_t | |
361 | ovsdb_row_hash_count(const struct ovsdb_row_hash *rh) | |
362 | { | |
363 | return hmap_count(&rh->rows); | |
364 | } | |
365 | ||
366 | bool | |
367 | ovsdb_row_hash_contains(const struct ovsdb_row_hash *rh, | |
368 | const struct ovsdb_row *row) | |
369 | { | |
370 | size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0); | |
371 | return ovsdb_row_hash_contains__(rh, row, hash); | |
372 | } | |
373 | ||
374 | /* Returns true if every row in 'b' has an equal row in 'a'. */ | |
375 | bool | |
376 | ovsdb_row_hash_contains_all(const struct ovsdb_row_hash *a, | |
377 | const struct ovsdb_row_hash *b) | |
378 | { | |
379 | struct ovsdb_row_hash_node *node; | |
380 | ||
cb22974d | 381 | ovs_assert(ovsdb_column_set_equals(&a->columns, &b->columns)); |
4e8e4213 | 382 | HMAP_FOR_EACH (node, hmap_node, &b->rows) { |
f85f8ebb BP |
383 | if (!ovsdb_row_hash_contains__(a, node->row, node->hmap_node.hash)) { |
384 | return false; | |
385 | } | |
386 | } | |
387 | return true; | |
388 | } | |
389 | ||
390 | bool | |
391 | ovsdb_row_hash_insert(struct ovsdb_row_hash *rh, const struct ovsdb_row *row) | |
392 | { | |
393 | size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0); | |
394 | return ovsdb_row_hash_insert__(rh, row, hash); | |
395 | } | |
396 | ||
397 | bool | |
398 | ovsdb_row_hash_contains__(const struct ovsdb_row_hash *rh, | |
399 | const struct ovsdb_row *row, size_t hash) | |
400 | { | |
401 | struct ovsdb_row_hash_node *node; | |
4e8e4213 | 402 | HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash, &rh->rows) { |
f85f8ebb BP |
403 | if (ovsdb_row_equal_columns(row, node->row, &rh->columns)) { |
404 | return true; | |
405 | } | |
406 | } | |
407 | return false; | |
408 | } | |
409 | ||
410 | bool | |
411 | ovsdb_row_hash_insert__(struct ovsdb_row_hash *rh, const struct ovsdb_row *row, | |
412 | size_t hash) | |
413 | { | |
414 | if (!ovsdb_row_hash_contains__(rh, row, hash)) { | |
415 | struct ovsdb_row_hash_node *node = xmalloc(sizeof *node); | |
416 | node->row = row; | |
417 | hmap_insert(&rh->rows, &node->hmap_node, hash); | |
418 | return true; | |
419 | } else { | |
420 | return false; | |
421 | } | |
422 | } |