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