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