1 /* -*- mode: C -*-
2 *
3 * File: rec-rset.c
4 * Date: Thu Mar 5 18:12:10 2009
5 *
6 * GNU recutils - Record Sets
7 *
8 */
9
10 /* Copyright (C) 2009-2019 Jose E. Marchesi */
11
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include <config.h>
27
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <errno.h>
31 #include <locale.h>
32 #include <string.h>
33 #include <parse-datetime.h>
34
35 #if defined UUID_TYPE
36 # include <uuid/uuid.h>
37 #endif
38
39 #include <rec.h>
40 #include <rec-utils.h>
41
42 /* Record Set Data Structures.
43 *
44 * A record set is a set of zero or more non-special records
45 * intermixed with comments, maybe preceded by a record descriptor.
46 */
47
48 /* The fprops structure contains useful properties associated with
49 fields. Those properties are usually extracted from the record
50 descriptor of the rset. */
51
52 struct rec_rset_fprops_s
53 {
54 char *fname;
55
56 bool key_p; /* Primary key */
57 bool auto_p; /* Auto-field. */
58 #if defined REC_CRYPT_SUPPORT
59 bool confidential_p; /* Confidential field. */
60 #endif
61 rec_type_t type; /* The field has an anonymous type. */
62 char *type_name; /* The field has a type in the types registry. */
63
64 struct rec_rset_fprops_s *next;
65 };
66
67 typedef struct rec_rset_fprops_s *rec_rset_fprops_t;
68
69 /* The rec_rset_s structure contains the data associated with a record
70 set. */
71
72 struct rec_rset_s
73 {
74 rec_record_t descriptor;
75 size_t descriptor_pos; /* Position of the descriptor into the record
76 set. Some comments can appear before the
77 record descriptor in the rec file, so we
78 need to track it in order to write back
79 the record properly. */
80
81 /* Field properties. */
82 rec_rset_fprops_t field_props;
83
84 /* Type registry. */
85 rec_type_reg_t type_reg;
86
87 /* Simple fex containing the fields to use for ordering the records
88 of this record descriptor. */
89 rec_fex_t order_by_fields;
90
91 /* Size constraints. */
92 size_t min_size;
93 size_t max_size;
94
95 /* Sex-driven constraints. */
96 rec_sex_t *constraints;
97 size_t num_constraints;
98
99 /* Storage for records and comments. */
100 int record_type;
101 int comment_type;
102 rec_mset_t mset;
103 };
104
105 /* Static functions implemented below. */
106
107 static void rec_rset_init (rec_rset_t rset);
108
109 static void rec_rset_update_types (rec_rset_t rset);
110 static void rec_rset_update_field_props (rec_rset_t rset);
111 static void rec_rset_update_size_constraints (rec_rset_t rset);
112 static void rec_rset_update_sex_constraints (rec_rset_t rset);
113
114 static bool rec_rset_record_equal_fn (void *data1, void *data2);
115 static void rec_rset_record_disp_fn (void *data);
116 static void *rec_rset_record_dup_fn (void *data);
117 static int rec_rset_record_compare_fn (void *data1, void *data2, int type1);
118
119 static bool rec_rset_comment_equal_fn (void *data1, void *data2);
120 static void rec_rset_comment_disp_fn (void *data);
121 static void *rec_rset_comment_dup_fn (void *data);
122 static int rec_rset_comment_compare_fn (void *data1, void *data2, int type2);
123
124 static bool rec_rset_type_field_p (const char *str);
125 static rec_fex_t rec_rset_type_field_fex (const char *str);
126 static char *rec_rset_type_field_type (const char *str);
127
128 static rec_rset_fprops_t rec_rset_get_props (rec_rset_t rset,
129 const char *fname,
130 bool create_p);
131
132 static bool rec_rset_add_auto_field_int (rec_rset_t rset,
133 const char *field_name,
134 rec_record_t record);
135 static bool rec_rset_add_auto_field_date (rec_rset_t rset,
136 const char *field_name,
137 rec_record_t record);
138
139 #if defined UUID_TYPE
140 static bool rec_rset_add_auto_field_uuid (rec_rset_t rset,
141 const char *field_name,
142 rec_record_t record);
143 #endif
144
145 static rec_record_t rec_rset_merge_records (rec_record_t to_record,
146 rec_record_t from_record,
147 rec_fex_t excluded_fields);
148
149 static int rec_rset_compare_typed_records (rec_rset_t rset,
150 rec_record_t record1,
151 rec_record_t record2,
152 rec_fex_t fields);
153
154
155 /* The following macro is used by some functions to reduce
156 verbosity. */
157
158 #define FNAME(id) rec_std_field_name ((id))
159
160 /*
161 * Public functions.
162 */
163
164 rec_rset_t
rec_rset_new(void)165 rec_rset_new (void)
166 {
167 rec_rset_t rset;
168
169 rset = malloc (sizeof (struct rec_rset_s));
170 if (rset)
171 {
172 rec_rset_init (rset);
173
174 /* Create the mset. */
175 rset->mset = rec_mset_new ();
176 if (rset->mset)
177 {
178 /* No descriptor, initially. */
179 rset->descriptor = NULL;
180 rset->descriptor_pos = 0;
181 rset->min_size = 0;
182 rset->max_size = SIZE_MAX;
183 rset->constraints = NULL;
184 rset->num_constraints = 0;
185
186 /* Create an empty type registry. */
187 rset->type_reg = rec_type_reg_new ();
188 if (!rset->type_reg)
189 {
190 /* Out of memory. */
191 rec_rset_destroy (rset);
192 return NULL;
193 }
194
195 /* No field properties, initially. */
196 rset->field_props = NULL;
197
198 /* No order by field, initially. */
199 rset->order_by_fields = NULL;
200
201 /* register the types. See rec.h for the definition of
202 MSET_COMMENT and MSET_RECORD. */
203
204 rset->record_type = rec_mset_register_type (rset->mset,
205 "record",
206 rec_rset_record_disp_fn,
207 rec_rset_record_equal_fn,
208 rec_rset_record_dup_fn,
209 rec_rset_record_compare_fn);
210 rset->comment_type = rec_mset_register_type (rset->mset,
211 "comment",
212 rec_rset_comment_disp_fn,
213 rec_rset_comment_equal_fn,
214 rec_rset_comment_dup_fn,
215 rec_rset_comment_compare_fn);
216 }
217 else
218 {
219 /* Out of memory. */
220
221 rec_rset_destroy (rset);
222 rset = NULL;
223 }
224 }
225
226 return rset;
227 }
228
229 void
rec_rset_destroy(rec_rset_t rset)230 rec_rset_destroy (rec_rset_t rset)
231 {
232 rec_rset_fprops_t props, aux = NULL;
233 size_t i = 0;
234
235 if (rset)
236 {
237 rec_record_destroy (rset->descriptor);
238 rec_type_reg_destroy (rset->type_reg);
239
240 for (i = 0; i < rset->num_constraints; i++)
241 {
242 rec_sex_destroy (rset->constraints[i]);
243 }
244 free (rset->constraints);
245
246 props = rset->field_props;
247 while (props)
248 {
249 aux = props;
250
251 if (aux->type)
252 {
253 rec_type_destroy (aux->type);
254 }
255 free (aux->fname);
256 free (aux->type_name);
257 props = props->next;
258 free (aux);
259 }
260
261 rec_fex_destroy (rset->order_by_fields);
262
263 rec_mset_destroy (rset->mset);
264 free (rset);
265 }
266 }
267
268 rec_rset_t
rec_rset_dup(rec_rset_t rset)269 rec_rset_dup (rec_rset_t rset)
270 {
271 rec_rset_t new = NULL;
272
273 new = malloc (sizeof (struct rec_rset_s));
274 if (new)
275 {
276 rec_rset_init (new);
277
278 new->record_type = rset->record_type;
279 new->comment_type = rset->comment_type;
280 new->mset = NULL;
281 new->min_size = rset->min_size;
282 new->max_size = rset->max_size;
283 /* XXX: make copies of the following structures. */
284 new->type_reg = NULL;
285 new->field_props = NULL;
286 new->constraints = NULL;
287 new->num_constraints = 0;
288
289 if (rset->order_by_fields)
290 {
291 new->order_by_fields = rec_fex_dup (rset->order_by_fields);
292 if (!new->order_by_fields)
293 {
294 /* Out of memory. */
295 rec_rset_destroy (new);
296 return NULL;
297 }
298 }
299 }
300
301 new->mset = rec_mset_dup (rset->mset);
302 if (!new->mset)
303 {
304 /* Out of memory. */
305 rec_rset_destroy (new);
306 return NULL;
307 }
308
309 return new;
310 }
311
312 rec_mset_t
rec_rset_mset(rec_rset_t rset)313 rec_rset_mset (rec_rset_t rset)
314 {
315 return rset->mset;
316 }
317
318 size_t
rec_rset_num_elems(rec_rset_t rset)319 rec_rset_num_elems (rec_rset_t rset)
320 {
321 return rec_mset_count (rset->mset, MSET_ANY);
322 }
323
324 size_t
rec_rset_num_records(rec_rset_t rset)325 rec_rset_num_records (rec_rset_t rset)
326 {
327 return rec_mset_count (rset->mset, rset->record_type);
328 }
329
330 size_t
rec_rset_num_comments(rec_rset_t rset)331 rec_rset_num_comments (rec_rset_t rset)
332 {
333 return rec_mset_count (rset->mset, rset->comment_type);
334 }
335
336 rec_record_t
rec_rset_descriptor(rec_rset_t rset)337 rec_rset_descriptor (rec_rset_t rset)
338 {
339 return rset->descriptor;
340 }
341
342 void
rec_rset_set_descriptor(rec_rset_t rset,rec_record_t record)343 rec_rset_set_descriptor (rec_rset_t rset, rec_record_t record)
344 {
345 if (rset->descriptor)
346 {
347 rec_record_destroy (rset->descriptor);
348 rset->descriptor = NULL;
349 }
350 rset->descriptor = record;
351
352 /* Update the types registry and the auto fields. */
353 rec_rset_update_types (rset);
354 rec_rset_update_field_props (rset);
355 rec_rset_update_size_constraints (rset);
356 rec_rset_update_sex_constraints (rset);
357 }
358
359 size_t
rec_rset_descriptor_pos(rec_rset_t rset)360 rec_rset_descriptor_pos (rec_rset_t rset)
361 {
362 return rset->descriptor_pos;
363 }
364
365 void
rec_rset_set_descriptor_pos(rec_rset_t rset,size_t position)366 rec_rset_set_descriptor_pos (rec_rset_t rset,
367 size_t position)
368 {
369 rset->descriptor_pos = position;
370 }
371
372 void
rec_rset_set_type(rec_rset_t rset,const char * type)373 rec_rset_set_type (rec_rset_t rset,
374 const char *type)
375 {
376 rec_field_t rec_field;
377
378 if (!type)
379 {
380 /* This is a no-op for the default record set. */
381 return;
382 }
383
384 if (!rset->descriptor)
385 {
386 /* Create a record descriptor. */
387 rset->descriptor = rec_record_new ();
388
389 }
390
391 rec_field = rec_record_get_field_by_name (rset->descriptor,
392 FNAME(REC_FIELD_REC),
393 0);
394
395 if (rec_field)
396 {
397 rec_field_set_value (rec_field, type);
398 }
399 else
400 {
401 rec_field = rec_field_new (FNAME(REC_FIELD_REC), type);
402 rec_mset_append (rec_record_mset (rset->descriptor), MSET_FIELD, (void *) rec_field, MSET_FIELD);
403 }
404 }
405
406 char *
rec_rset_type(rec_rset_t rset)407 rec_rset_type (rec_rset_t rset)
408 {
409 char *res;
410 rec_field_t field;
411
412 res = NULL;
413 if (rset->descriptor)
414 {
415 field = rec_record_get_field_by_name (rset->descriptor,
416 FNAME(REC_FIELD_REC),
417 0);
418 if (field)
419 {
420 res = rec_extract_type (rec_field_value (field));
421 }
422 }
423
424 return res;
425 }
426
427 char *
rec_rset_url(rec_rset_t rset)428 rec_rset_url (rec_rset_t rset)
429 {
430 char *res;
431 rec_field_t field;
432
433 res = NULL;
434 if (rset->descriptor)
435 {
436 field = rec_record_get_field_by_name (rset->descriptor,
437 FNAME(REC_FIELD_REC),
438 0);
439 if (field)
440 {
441 res = rec_extract_url (rec_field_value (field));
442 }
443 }
444
445 return res;
446 }
447
448 rec_type_reg_t
rec_rset_get_type_reg(rec_rset_t rset)449 rec_rset_get_type_reg (rec_rset_t rset)
450 {
451 return rset->type_reg;
452 }
453
454 void
rec_rset_rename_field(rec_rset_t rset,const char * field_name,const char * new_field_name)455 rec_rset_rename_field (rec_rset_t rset,
456 const char *field_name,
457 const char *new_field_name)
458 {
459 size_t j;
460 rec_record_t descriptor;
461 rec_fex_t fex;
462 char *fex_str;
463 char *type_str;
464 rec_buf_t buf;
465 char *result;
466 size_t result_size;
467 rec_fex_elem_t fex_elem;
468 const char *fex_fname;
469
470 descriptor = rec_rset_descriptor (rset);
471 if (descriptor)
472 {
473 rec_mset_t descriptor_mset = rec_record_mset (descriptor);
474 rec_mset_iterator_t iter = rec_mset_iterator (descriptor_mset);
475 rec_field_t field;
476
477 while (rec_mset_iterator_next (&iter, MSET_FIELD, (void *) &field, NULL))
478 {
479 if (rec_field_name_equal_p (rec_field_name (field), FNAME(REC_FIELD_TYPE)))
480 {
481 /* Process a %type entry. Invalid entries are
482 skipped. */
483 if (!rec_rset_type_field_p (rec_field_value (field)))
484 {
485 continue;
486 }
487
488 fex = rec_rset_type_field_fex (rec_field_value (field));
489 if (fex)
490 {
491 for (j = 0; j < rec_fex_size (fex); j++)
492 {
493 fex_elem = rec_fex_get (fex, j);
494 fex_fname = rec_fex_elem_field_name (fex_elem);
495 if (rec_field_name_equal_p (field_name, fex_fname))
496 {
497 /* Replace it with new_field_name. */
498 rec_fex_elem_set_field_name (fex_elem, new_field_name);
499 }
500 }
501
502 fex_str = rec_fex_str (fex, REC_FEX_CSV);
503 type_str = rec_rset_type_field_type (rec_field_value (field));
504
505 buf = rec_buf_new (&result, &result_size);
506 rec_buf_puts (fex_str, buf);
507 rec_buf_putc (' ', buf);
508 rec_buf_puts (type_str, buf);
509 rec_buf_close (buf);
510
511 rec_field_set_value (field, result);
512
513 free (fex_str);
514 free (type_str);
515 rec_fex_destroy (fex);
516 }
517 }
518 else if (rec_field_name_equal_p (rec_field_name (field), FNAME(REC_FIELD_KEY))
519 || rec_field_name_equal_p (rec_field_name (field), FNAME(REC_FIELD_MANDATORY))
520 || rec_field_name_equal_p (rec_field_name (field), FNAME(REC_FIELD_UNIQUE))
521 || rec_field_name_equal_p (rec_field_name (field), FNAME(REC_FIELD_PROHIBIT))
522 #if defined REC_CRYPT_SUPPORT
523 || rec_field_name_equal_p (rec_field_name (field), FNAME(REC_FIELD_CONFIDENTIAL))
524 #endif
525 || rec_field_name_equal_p (rec_field_name (field), FNAME(REC_FIELD_SORT)))
526 {
527 /* Rename the field in the fex expression that is the
528 value of the field. Skip invalid entries. */
529 fex = rec_fex_new (rec_field_value (field), REC_FEX_SIMPLE);
530 if (fex)
531 {
532 for (j = 0; j < rec_fex_size (fex); j++)
533 {
534 fex_elem = rec_fex_get (fex, j);
535
536 fex_fname = rec_fex_elem_field_name (fex_elem);
537 if (rec_field_name_equal_p (field_name, fex_fname))
538 {
539 /* Replace it with new_field_name. */
540 rec_fex_elem_set_field_name (fex_elem, new_field_name);
541 }
542 }
543
544 fex_str = rec_fex_str (fex, REC_FEX_SIMPLE);
545 rec_field_set_value (field, fex_str);
546 free (fex_str);
547 }
548 }
549 }
550
551 rec_mset_iterator_free (&iter);
552 }
553
554 /* Update the types registry. */
555 rec_rset_update_field_props (rset);
556 }
557
558 const char *
rec_rset_key(rec_rset_t rset)559 rec_rset_key (rec_rset_t rset)
560 {
561 const char *key = NULL;
562 rec_rset_fprops_t props = rset->field_props;
563
564 while (props)
565 {
566 if (props->key_p)
567 {
568 /* There must be only one field marked as key. */
569 key = props->fname;
570 break;
571 }
572 props = props->next;
573 }
574
575 return key;
576 }
577
578 rec_fex_t
rec_rset_auto(rec_rset_t rset)579 rec_rset_auto (rec_rset_t rset)
580 {
581 rec_fex_t fex;
582 rec_rset_fprops_t props;
583
584 fex = rec_fex_new (NULL, REC_FEX_SIMPLE);
585
586 props = rset->field_props;
587 while (props)
588 {
589 if (props->auto_p)
590 {
591 rec_fex_append (fex,
592 props->fname,
593 -1, -1);
594 }
595 props = props->next;
596 }
597
598 return fex;
599 }
600
601 #if defined REC_CRYPT_SUPPORT
602
603 bool
rec_rset_field_confidential_p(rec_rset_t rset,const char * field_name)604 rec_rset_field_confidential_p (rec_rset_t rset,
605 const char *field_name)
606 {
607 rec_fex_t fex;
608 size_t fex_size;
609 size_t i;
610 bool result = false;
611 const char *fex_field_name;
612
613 fex = rec_rset_confidential (rset);
614 fex_size = rec_fex_size (fex);
615
616 for (i = 0; i < fex_size; i++)
617 {
618 fex_field_name = rec_fex_elem_field_name (rec_fex_get (fex, i));
619 if (rec_field_name_equal_p (field_name, fex_field_name))
620 {
621 result = true;
622 break;
623 }
624 }
625
626 return result;
627 }
628
629 rec_fex_t
rec_rset_confidential(rec_rset_t rset)630 rec_rset_confidential (rec_rset_t rset)
631 {
632 rec_fex_t fex;
633 rec_rset_fprops_t props;
634
635 fex = rec_fex_new (NULL, REC_FEX_SIMPLE);
636
637 props = rset->field_props;
638 while (props)
639 {
640 if (props->confidential_p)
641 {
642 rec_fex_append (fex,
643 props->fname,
644 -1, -1);
645 }
646
647 props = props->next;
648 }
649
650 return fex;
651 }
652
653 #endif /* REC_CRYPT_SUPPORT */
654
655 rec_type_t
rec_rset_get_field_type(rec_rset_t rset,const char * field_name)656 rec_rset_get_field_type (rec_rset_t rset,
657 const char *field_name)
658 {
659 rec_type_t type = NULL;
660 rec_rset_fprops_t props = NULL;
661
662 props = rec_rset_get_props (rset, field_name, false);
663 if (props)
664 {
665 type = props->type;
666 if (!type && props->type_name)
667 {
668 type = rec_type_reg_get (rset->type_reg, props->type_name);
669 }
670 }
671
672 return type;
673 }
674
675 size_t
rec_rset_min_records(rec_rset_t rset)676 rec_rset_min_records (rec_rset_t rset)
677 {
678 return rset->min_size;
679 }
680
681 size_t
rec_rset_max_records(rec_rset_t rset)682 rec_rset_max_records (rec_rset_t rset)
683 {
684 return rset->max_size;
685 }
686
687 char *
rec_rset_source(rec_rset_t rset)688 rec_rset_source (rec_rset_t rset)
689 {
690 rec_record_t record;
691
692 /* The source of the record set is considered to be the source of
693 its first record: either the descriptor or some other record. */
694
695 record = rec_rset_descriptor (rset);
696 if (!record)
697 {
698 record = (rec_record_t) rec_mset_get_at (rset->mset, MSET_RECORD, 0);
699 }
700
701 return rec_record_source (record);
702 }
703
704
705 bool
rec_rset_set_order_by_fields(rec_rset_t rset,rec_fex_t field_names)706 rec_rset_set_order_by_fields (rec_rset_t rset,
707 rec_fex_t field_names)
708 {
709 rec_fex_destroy (rset->order_by_fields);
710 rset->order_by_fields = rec_fex_dup (field_names);
711 return (rset->order_by_fields != NULL);
712 }
713
714 rec_fex_t
rec_rset_order_by_fields(rec_rset_t rset)715 rec_rset_order_by_fields (rec_rset_t rset)
716 {
717 return rset->order_by_fields;
718 }
719
720 rec_rset_t
rec_rset_sort(rec_rset_t rset,rec_fex_t sort_by)721 rec_rset_sort (rec_rset_t rset,
722 rec_fex_t sort_by)
723 {
724 if (sort_by)
725 {
726 rec_rset_set_order_by_fields (rset, sort_by);
727 }
728
729 if (rset->order_by_fields)
730 {
731 /* Duplicate the multi-set indicating that the elements must be
732 sorted. */
733
734 if (!rec_mset_sort (rset->mset))
735 {
736 /* Out of memory. */
737 return NULL;
738 }
739
740 /* Update field properties, in case order_by_fields was changed
741 above. */
742
743 rec_rset_update_field_props (rset);
744 }
745
746 return rset;
747 }
748
749 rec_rset_t
rec_rset_group(rec_rset_t rset,rec_fex_t group_by)750 rec_rset_group (rec_rset_t rset,
751 rec_fex_t group_by)
752 {
753 rec_mset_iterator_t iter;
754 rec_record_t record;
755 rec_mset_elem_t elem;
756 size_t map_size;
757 bool *deletion_map;
758 size_t num_record;
759
760 /* Create and initialize the deletion map. */
761
762 map_size = sizeof(bool) * rec_rset_num_records (rset);
763 deletion_map = malloc (map_size);
764 if (!deletion_map)
765 {
766 /* Out of memory. */
767 return NULL;
768 }
769
770 memset (deletion_map, false, map_size);
771
772 /* Iterate on the records of RSET, grouping records and marking the
773 grouped records for deletion. */
774
775 num_record = 0;
776 iter = rec_mset_iterator (rec_rset_mset (rset));
777 while (rec_mset_iterator_next (&iter, MSET_RECORD, (const void **)&record, NULL))
778 {
779 if (!deletion_map[num_record])
780 {
781 size_t num_record_2 = num_record;
782 rec_mset_iterator_t iter2 = iter;
783 rec_record_t record2;
784
785 while (rec_mset_iterator_next (&iter2, MSET_RECORD, (const void**)&record2, NULL))
786 {
787 num_record_2++;
788
789 if (rec_rset_compare_typed_records (rset, record, record2, group_by) != 0)
790 {
791 break;
792 }
793 else
794 {
795 /* Insert all the elements of record2 into record,
796 but not the group-by fields. Also, remove any
797 duplicated field created in record2 as the result
798 of the operation. */
799
800 if (!rec_rset_merge_records (record,
801 record2,
802 group_by))
803 {
804 /* Out of memory. */
805 return NULL;
806 }
807
808 /* Mark record2 for removal. */
809 deletion_map[num_record_2] = true;
810 }
811 }
812 }
813
814 num_record++;
815 }
816 rec_mset_iterator_free (&iter);
817
818 /* Delete the records marked for deletion. */
819
820 num_record = 0;
821 iter = rec_mset_iterator (rec_rset_mset (rset));
822 while (rec_mset_iterator_next (&iter, MSET_RECORD, (const void **) &record, &elem))
823 {
824 if (deletion_map[num_record])
825 {
826 rec_mset_remove_elem (rec_rset_mset (rset), elem);
827 }
828
829 num_record++;
830 }
831 rec_mset_iterator_free (&iter);
832
833 free (deletion_map);
834
835 return rset;
836 }
837
838 rec_rset_t
rec_rset_add_auto_fields(rec_rset_t rset,rec_record_t record)839 rec_rset_add_auto_fields (rec_rset_t rset,
840 rec_record_t record)
841 {
842 rec_fex_t auto_fields;
843 rec_type_t type;
844 size_t i;
845
846 if ((auto_fields = rec_rset_auto (rset)))
847 {
848 size_t num_auto_fields = rec_fex_size (auto_fields);
849
850 for (i = 0; i < num_auto_fields; i++)
851 {
852 const char *auto_field_name =
853 rec_fex_elem_field_name (rec_fex_get (auto_fields, i));
854
855 if (!rec_record_field_p (record, auto_field_name))
856 {
857 /* The auto field is not already present in record, so
858 add one automatically. Depending on its type the
859 value is calculated differently. If the record does
860 not have a type, or the type is incorrect, ignore
861 it. */
862
863 type = rec_rset_get_field_type (rset, auto_field_name);
864 if (type)
865 {
866 switch (rec_type_kind (type))
867 {
868 case REC_TYPE_INT:
869 case REC_TYPE_RANGE:
870 {
871 if (!rec_rset_add_auto_field_int (rset, auto_field_name, record))
872 {
873 /* Out of memory. */
874 return NULL;
875 }
876
877 break;
878 }
879 case REC_TYPE_DATE:
880 {
881 if (!rec_rset_add_auto_field_date (rset, auto_field_name, record))
882 {
883 /* Out of memory. */
884 return NULL;
885 }
886
887 break;
888 }
889 #if defined UUID_TYPE
890 case REC_TYPE_UUID:
891 {
892 if (!rec_rset_add_auto_field_uuid (rset, auto_field_name, record))
893 {
894 /* Out of memory. */
895 return NULL;
896 }
897
898 break;
899 }
900 #endif /* UUID_TYPE */
901 default:
902 {
903 /* Do nothing for other types. */
904 break;
905 }
906 }
907 }
908 }
909 }
910 }
911
912 return rset;
913 }
914
915 size_t
rec_rset_num_sex_constraints(rec_rset_t rset)916 rec_rset_num_sex_constraints (rec_rset_t rset)
917 {
918 return rset->num_constraints;
919 }
920
921 rec_sex_t
rec_rset_sex_constraint(rec_rset_t rset,size_t index)922 rec_rset_sex_constraint (rec_rset_t rset,
923 size_t index)
924 {
925 return rset->constraints[index];
926 }
927
928 /*
929 * Private functions
930 */
931
932 static void
rec_rset_init(rec_rset_t rset)933 rec_rset_init (rec_rset_t rset)
934 {
935 /* Initialize the rset structure so it can be safely passed to
936 rec_rset_destroy even if its contents are not completely
937 initialized with real values. */
938
939 memset (rset, 0 /* NULL */, sizeof (struct rec_rset_s));
940 }
941
942 static void
rec_rset_record_disp_fn(void * data)943 rec_rset_record_disp_fn (void *data)
944 {
945 rec_record_t record = (rec_record_t) data;
946 rec_record_destroy (record);
947 }
948
949 static bool
rec_rset_record_equal_fn(void * data1,void * data2)950 rec_rset_record_equal_fn (void *data1,
951 void *data2)
952 {
953 return (data1 == data2);
954 /* return rec_record_equal_p ((rec_record_t) data1,
955 (rec_record_t) data2); */
956 }
957
958 static void *
rec_rset_record_dup_fn(void * data)959 rec_rset_record_dup_fn (void *data)
960 {
961 rec_record_t record = (rec_record_t) data;
962 rec_record_t new = rec_record_dup (record);
963
964 return (void *) new;
965 }
966
967 static int
rec_rset_record_compare_fn(void * data1,void * data2,int type2)968 rec_rset_record_compare_fn (void *data1,
969 void *data2,
970 int type2)
971 {
972 /* data1 is a record. data2 can be either a record or a comment.
973
974 order_by_field can't be NULL, because this callback is invoked
975 only if rec_mset_add_sorted is used to add an element to the
976 list.
977
978 The following rules apply here:
979
980 1. If the fields in order_by_fields are not in both record1 and
981 record2, then data1 < data2.
982
983 2. Else, perform a lexicographic comparison, i.e.
984
985 (a1, a2, ...) < (b1, b2, ...) IFF
986 a1 < b1 OR (a1 = b2 AND a2 < b2) OR ...
987
988 Note that record1 will always be a regular record. Never a
989 descriptor.
990 */
991
992 rec_rset_t rset = NULL;
993 rec_record_t record1 = NULL;
994 rec_record_t record2 = NULL;
995 int type_comparison = 0;
996
997 /* If elem2 is a comment then elem1 > elem2. */
998 if (type2 == MSET_COMMENT)
999 {
1000 return 1;
1001 }
1002
1003 /* Get the records and the containing rset. */
1004 record1 = (rec_record_t) data1;
1005 record2 = (rec_record_t) data2;
1006 rset = (rec_rset_t) rec_record_container (record1);
1007
1008 /* Perform a lexicographic comparison of the order_by_fields in both
1009 registers. */
1010
1011 type_comparison = rec_rset_compare_typed_records (rset,
1012 record1,
1013 record2,
1014 rset->order_by_fields);
1015
1016 /* If both records are equal, return -1 instead of 0 in order to
1017 maintain the relative ordering between equal records. */
1018
1019 if (type_comparison == 0)
1020 {
1021 type_comparison = -1;
1022 }
1023
1024 return type_comparison;
1025 }
1026
1027 static void
rec_rset_comment_disp_fn(void * data)1028 rec_rset_comment_disp_fn (void *data)
1029 {
1030 rec_comment_t comment = (rec_comment_t) data;
1031 rec_comment_destroy (comment);
1032 }
1033
1034 static bool
rec_rset_comment_equal_fn(void * data1,void * data2)1035 rec_rset_comment_equal_fn (void *data1,
1036 void *data2)
1037 {
1038 return (data1 == data2);
1039 /* return rec_comment_equal_p ((rec_comment_t) data1,
1040 (rec_comment_t) data2);*/
1041 }
1042
1043 static void *
rec_rset_comment_dup_fn(void * data)1044 rec_rset_comment_dup_fn (void *data)
1045 {
1046 rec_comment_t comment = (rec_comment_t) data;
1047 rec_comment_t new = rec_comment_dup (comment);
1048 return (void *) new;
1049 }
1050
1051 static int
rec_rset_comment_compare_fn(void * data1,void * data2,int type2)1052 rec_rset_comment_compare_fn (void *data1,
1053 void *data2,
1054 int type2)
1055 {
1056 /* data1 is a comment, and data2 can be either a comment or a
1057 record. In any case, data1 < data2. */
1058
1059 return -1;
1060 }
1061
1062 static void
rec_rset_update_sex_constraints(rec_rset_t rset)1063 rec_rset_update_sex_constraints (rec_rset_t rset)
1064 {
1065 /* Reset the existing constraints. */
1066
1067 {
1068 size_t i = 0;
1069
1070 for (i = 0; i < rset->num_constraints; i++)
1071 {
1072 rec_sex_destroy (rset->constraints[i]);
1073 }
1074 rset->num_constraints = 0;
1075 }
1076
1077 /* If there is not a record descriptor in the record set then simply
1078 return. */
1079
1080 if (!rset->descriptor)
1081 {
1082 return;
1083 }
1084
1085 /* Allocate memory for the constraints memory. In case of
1086 not-enough-memory simply return. */
1087
1088 {
1089 size_t num_constraints =
1090 rec_record_get_num_fields_by_name (rset->descriptor, FNAME(REC_FIELD_CONSTRAINT));
1091 rset->constraints = malloc (num_constraints * sizeof(rec_sex_t));
1092
1093 if (!rset->constraints)
1094 {
1095 return;
1096 }
1097 }
1098
1099 /* Scan the record descriptor for %constraint: directives, and build
1100 the constraints. Not well formed constraint entries,
1101 i.e. entries not containing valid sexes, are simply ignored. */
1102
1103 {
1104 rec_field_t field = NULL;
1105 rec_mset_iterator_t iter;
1106
1107 iter = rec_mset_iterator (rec_record_mset (rset->descriptor));
1108 while (rec_mset_iterator_next (&iter, MSET_FIELD, (const void **)&field, NULL))
1109 {
1110 const char *field_name = rec_field_name (field);
1111 const char *field_value = rec_field_value (field);
1112
1113 if (rec_field_name_equal_p (field_name, FNAME(REC_FIELD_CONSTRAINT)))
1114 {
1115 rec_sex_t sex = rec_sex_new (false);
1116 if (!sex)
1117 {
1118 return;
1119 }
1120
1121 if (rec_sex_compile (sex, field_value))
1122 {
1123 rset->constraints[rset->num_constraints++] = sex;
1124 }
1125 else
1126 {
1127 rec_sex_destroy (sex);
1128 }
1129 }
1130 }
1131 rec_mset_iterator_free (&iter);
1132 }
1133 }
1134
1135 static void
rec_rset_update_size_constraints(rec_rset_t rset)1136 rec_rset_update_size_constraints (rec_rset_t rset)
1137 {
1138 rec_field_t field;
1139 enum rec_size_condition_e condition;
1140 size_t size = 0;
1141
1142 /* Reset the constraints. */
1143 rset->min_size = 0;
1144 rset->max_size = SIZE_MAX;
1145
1146 /* Scan the record descriptor for %size: directives, and build the
1147 new list. */
1148 if (rset->descriptor)
1149 {
1150 field = rec_record_get_field_by_name (rset->descriptor,
1151 FNAME(REC_FIELD_SIZE),
1152 0);
1153
1154 if (field && rec_match (rec_field_value (field), REC_INT_SIZE_RE))
1155 {
1156 /* Extract 'min' and 'max' and update the constraints in the
1157 rset. */
1158 condition = rec_extract_size_condition (rec_field_value (field));
1159 size = rec_extract_size (rec_field_value (field));
1160
1161 /* Set min_size and max_size depending on the
1162 condition. */
1163 switch (condition)
1164 {
1165 case SIZE_COND_E:
1166 {
1167 rset->min_size = size;
1168 rset->max_size = size;
1169 break;
1170 }
1171 case SIZE_COND_L:
1172 {
1173 rset->max_size = size - 1;
1174 break;
1175 }
1176 case SIZE_COND_LE:
1177 {
1178 rset->max_size = size;
1179 break;
1180 }
1181 case SIZE_COND_G:
1182 {
1183 rset->min_size = size + 1;
1184 break;
1185 }
1186 case SIZE_COND_GE:
1187 {
1188 rset->min_size = size;
1189 break;
1190 }
1191 }
1192 }
1193 }
1194 }
1195
1196 static void
rec_rset_update_field_props(rec_rset_t rset)1197 rec_rset_update_field_props (rec_rset_t rset)
1198 {
1199 rec_rset_fprops_t props = NULL;
1200 #if defined REC_CRYPT_SUPPORT
1201 const char *confidential_field_name;
1202 #endif
1203 char *type_name = NULL;
1204
1205 /* Reset the field properties. */
1206 props = rset->field_props;
1207 while (props)
1208 {
1209 props->key_p = false;
1210 props->auto_p = false;
1211 if (props->type)
1212 {
1213 rec_type_destroy (props->type);
1214 props->type = NULL;
1215 }
1216
1217 props = props->next;
1218 }
1219
1220 if (rset->descriptor)
1221 {
1222 /* Pass 1: scan the record descriptor for % directives, and update
1223 the fields properties accordingly. */
1224
1225 rec_field_t field;
1226 rec_mset_iterator_t iter;
1227
1228 iter = rec_mset_iterator (rec_record_mset (rset->descriptor));
1229 while (rec_mset_iterator_next (&iter, MSET_FIELD, (const void**) &field, NULL))
1230 {
1231 const char *field_name = rec_field_name (field);
1232 const char *field_value = rec_field_value (field);
1233
1234 /* Update field types. Only valid %type: descriptors are
1235 considered. Invalid descriptors are ignored. */
1236
1237 if (rec_field_name_equal_p (field_name, FNAME(REC_FIELD_TYPE))
1238 && rec_rset_type_field_p (field_value))
1239 {
1240 size_t i;
1241 rec_fex_t fex = rec_rset_type_field_fex (field_value);
1242
1243 for (i = 0; i < rec_fex_size (fex); i++)
1244 {
1245 char *field_type = rec_rset_type_field_type (field_value);
1246 rec_type_t type = rec_type_new (field_type);
1247
1248 if (!type)
1249 {
1250 /* Set field_type as a field property. Note
1251 that if the field is already associated with
1252 an anonymous type, or a type name, they are
1253 replaced. */
1254
1255 const char *p = field_type;
1256 rec_parse_regexp (&p, "^" REC_TYPE_NAME_RE, &type_name);
1257 props = rec_rset_get_props (rset,
1258 rec_fex_elem_field_name (rec_fex_get (fex, i)),
1259 true);
1260 if (props->type)
1261 {
1262 rec_type_destroy (props->type);
1263 props->type = NULL;
1264 }
1265 free (props->type_name);
1266 props->type_name = type_name;
1267 }
1268 else
1269 {
1270 /* Set the type as a field property. Note that
1271 if the field is already associated with an
1272 anonymous type, or a type name, they are
1273 replaced. */
1274
1275 props = rec_rset_get_props (rset,
1276 rec_fex_elem_field_name (rec_fex_get (fex, i)),
1277 true);
1278 if (props->type)
1279 {
1280 rec_type_destroy (props->type);
1281 }
1282 free (props->type_name);
1283 props->type_name = NULL;
1284 props->type = type;
1285 }
1286
1287 free (field_type);
1288 }
1289
1290 rec_fex_destroy (fex);
1291 }
1292
1293 /* Update the key field. */
1294 if (rec_field_name_equal_p (field_name, FNAME(REC_FIELD_KEY)))
1295 {
1296 /* %key: fields containing incorrect data are
1297 ignored. */
1298
1299 const char *field_value = rec_field_value (field);
1300 char *type_name = NULL;
1301
1302 rec_skip_blanks (&field_value);
1303 rec_parse_regexp (&field_value, "^" REC_RECORD_TYPE_RE, &type_name);
1304 props = rec_rset_get_props (rset, type_name, true);
1305 props->key_p = true;
1306 free (type_name);
1307 }
1308
1309 /* Update auto fields. */
1310 if (rec_field_name_equal_p (field_name, FNAME(REC_FIELD_AUTO)))
1311 {
1312 /* %auto: fields containing incorrect data are
1313 ignored. */
1314
1315 rec_fex_t fex = rec_fex_new (rec_field_value (field), REC_FEX_SIMPLE);
1316 if (fex)
1317 {
1318 size_t i;
1319
1320 for (i = 0; i < rec_fex_size (fex); i++)
1321 {
1322 const char *auto_field_name
1323 = rec_fex_elem_field_name (rec_fex_get (fex, i));
1324 props = rec_rset_get_props (rset, auto_field_name, true);
1325 props->auto_p = true;
1326 }
1327 }
1328 }
1329
1330 /* Update sort fields. The last field takes precedence. */
1331 if (rec_field_name_equal_p (field_name, FNAME(REC_FIELD_SORT)))
1332 {
1333 /* Parse the simple fex in the field value. Invalid
1334 entries are just ignored. */
1335
1336 const char *field_value = rec_field_value (field);
1337
1338 rec_fex_t fex = rec_fex_new (field_value, REC_FEX_SIMPLE);
1339 if (fex)
1340 {
1341 rec_fex_destroy (rset->order_by_fields);
1342 rset->order_by_fields = fex;
1343 }
1344 }
1345
1346 #if defined REC_CRYPT_SUPPORT
1347 /* Update confidential fields. */
1348 if (rec_field_name_equal_p (field_name, FNAME(REC_FIELD_CONFIDENTIAL)))
1349 {
1350 /* Parse the field names in the field value. Ignore
1351 invalid entries. */
1352
1353 rec_fex_t fex = rec_fex_new (rec_field_value (field), REC_FEX_SIMPLE);
1354 if (fex)
1355 {
1356 size_t i;
1357
1358 for (i = 0; i < rec_fex_size (fex); i++)
1359 {
1360 confidential_field_name =
1361 rec_fex_elem_field_name (rec_fex_get (fex, i));
1362 props = rec_rset_get_props (rset, confidential_field_name, true);
1363 props->confidential_p = true;
1364 }
1365 }
1366 }
1367 #endif /* REC_CRYPT_SUPPORT */
1368
1369 }
1370
1371 rec_mset_iterator_free (&iter);
1372 }
1373
1374 /* Pass 2: scan the fields having properties on the record set. */
1375
1376 props = rset->field_props;
1377 while (props)
1378 {
1379 /* Auto fields not having an explicit type are implicitly
1380 typed as integers. */
1381
1382 if (props->auto_p && !props->type && !props->type_name)
1383 {
1384 props->type = rec_type_new ("int");
1385 }
1386
1387 props = props->next;
1388 }
1389 }
1390
1391 static void
rec_rset_update_types(rec_rset_t rset)1392 rec_rset_update_types (rec_rset_t rset)
1393 {
1394 rec_field_t field;
1395 rec_mset_iterator_t iter;
1396 const char *p, *q = NULL;
1397 rec_type_t type;
1398 char *type_name, *to_type = NULL;
1399
1400
1401 /* Scan the record descriptor for %typedef directives and update the
1402 types registry accordingly. */
1403 if (rset->descriptor)
1404 {
1405 /* Purge the registry. */
1406
1407 rec_type_reg_destroy (rset->type_reg);
1408 rset->type_reg = rec_type_reg_new ();
1409
1410 /* Iterate on the fields of the descriptor. */
1411
1412 iter = rec_mset_iterator (rec_record_mset (rset->descriptor));
1413 while (rec_mset_iterator_next (&iter, MSET_FIELD, (const void **) &field, NULL))
1414 {
1415 const char *field_name = rec_field_name (field);
1416 const char *field_value = rec_field_value (field);
1417
1418 if (rec_field_name_equal_p (field_name, FNAME(REC_FIELD_TYPEDEF)))
1419 {
1420 p = field_value;
1421 rec_skip_blanks (&p);
1422
1423 /* Get the name of the type. */
1424 if (rec_parse_regexp (&p, "^" REC_TYPE_NAME_RE, &type_name))
1425 {
1426 /* Get the type. */
1427 type = rec_type_new (p);
1428 if (type)
1429 {
1430 /* Set the name of the type. */
1431 rec_type_set_name (type, type_name);
1432
1433 /* Create and insert the type in the type
1434 registry. */
1435 rec_type_reg_add (rset->type_reg, type);
1436 }
1437 else
1438 {
1439 /* This could be a synonym. Try to parse a type
1440 name and, if the operation succeeds, insert
1441 the synonym in the registry. */
1442 rec_skip_blanks (&p);
1443 q = p;
1444 if (rec_parse_regexp (&q,
1445 "^" REC_TYPE_NAME_RE "[ \t\n]*",
1446 NULL))
1447 {
1448 rec_parse_regexp (&p, "^" REC_TYPE_NAME_RE, &to_type);
1449 rec_type_reg_add_synonym (rset->type_reg,
1450 type_name,
1451 to_type);
1452 }
1453 }
1454
1455 free (type_name);
1456 }
1457 }
1458 }
1459
1460 rec_mset_iterator_free (&iter);
1461 }
1462 }
1463
1464 static bool
rec_rset_type_field_p(const char * str)1465 rec_rset_type_field_p (const char *str)
1466 {
1467 const char *p = str;
1468
1469 /* Check the fex */
1470
1471 rec_skip_blanks (&p);
1472 if (!rec_parse_regexp (&p,
1473 "^" REC_FNAME_LIST_CS_RE,
1474 NULL))
1475 {
1476 return false;
1477 }
1478 rec_skip_blanks (&p);
1479
1480 /* Check the type description, or the name of a type. */
1481
1482 return (rec_type_descr_p (p)
1483 || rec_parse_regexp (&p, "^" REC_TYPE_NAME_RE "[ \t\n]*$", NULL));
1484 }
1485
1486 static rec_fex_t
rec_rset_type_field_fex(const char * str)1487 rec_rset_type_field_fex (const char *str)
1488 {
1489 rec_fex_t fex = NULL;
1490 const char *p;
1491 char *name;
1492
1493 p = str;
1494
1495 if (rec_parse_regexp (&p,
1496 "^" REC_FNAME_LIST_CS_RE,
1497 &name))
1498 {
1499 fex = rec_fex_new (name, REC_FEX_CSV);
1500 free (name);
1501 }
1502
1503 return fex;
1504 }
1505
1506 static char*
rec_rset_type_field_type(const char * str)1507 rec_rset_type_field_type (const char *str)
1508 {
1509 char *result = NULL;
1510 const char *p;
1511
1512 if (rec_rset_type_field_p (str))
1513 {
1514 p = str;
1515
1516 rec_skip_blanks (&p);
1517 rec_parse_regexp (&p, "^" REC_FNAME_LIST_CS_RE, NULL);
1518 rec_skip_blanks (&p);
1519
1520 /* Return the rest of the string. */
1521 result = strdup (p);
1522 }
1523
1524 return result;
1525 }
1526
1527 static rec_rset_fprops_t
rec_rset_get_props(rec_rset_t rset,const char * fname,bool create_p)1528 rec_rset_get_props (rec_rset_t rset,
1529 const char *fname,
1530 bool create_p)
1531 {
1532 rec_rset_fprops_t props = NULL;
1533
1534 props = rset->field_props;
1535 while (props)
1536 {
1537 if (rec_field_name_equal_p (fname, props->fname))
1538 {
1539 break;
1540 }
1541
1542 props = props->next;
1543 }
1544
1545 if (!props && create_p)
1546 {
1547 /* Create a new properties structure for this field name and
1548 initialize it. */
1549 props = malloc (sizeof (struct rec_rset_fprops_s));
1550 if (props)
1551 {
1552 props->fname = strdup (fname);
1553 props->auto_p = false;
1554 props->key_p = false;
1555
1556 #if defined REC_CRYPT_SUPPORT
1557 props->confidential_p = false;
1558 #endif
1559
1560 props->type = NULL;
1561 props->type_name = NULL;
1562
1563 /* Prepend it to the field properties list. */
1564 props->next = rset->field_props;
1565 rset->field_props = props;
1566 }
1567 }
1568
1569 return props;
1570 }
1571
1572 static bool
rec_rset_add_auto_field_int(rec_rset_t rset,const char * field_name,rec_record_t record)1573 rec_rset_add_auto_field_int (rec_rset_t rset,
1574 const char *field_name,
1575 rec_record_t record)
1576 {
1577 rec_mset_iterator_t iter;
1578 rec_record_t rec;
1579 rec_field_t field;
1580 size_t num_fields, i;
1581 int auto_value, field_value;
1582 char *end;
1583 char *auto_value_str;
1584
1585 /* Find the auto value. */
1586
1587 auto_value = 0;
1588
1589 iter = rec_mset_iterator (rec_rset_mset (rset));
1590 while (rec_mset_iterator_next (&iter, MSET_RECORD, (const void **) &rec, NULL))
1591 {
1592 num_fields = rec_record_get_num_fields_by_name (rec, field_name);
1593 for (i = 0; i < num_fields; i++)
1594 {
1595 field = rec_record_get_field_by_name (rec, field_name, i);
1596
1597 /* Ignore fields that can't be converted to integer
1598 values. */
1599 errno = 0;
1600 field_value = strtol (rec_field_value (field), &end, 10);
1601 if ((errno == 0) && (*end == '\0'))
1602 {
1603 if (auto_value <= field_value)
1604 {
1605 auto_value = field_value + 1;
1606 }
1607 }
1608 }
1609 }
1610
1611 rec_mset_iterator_free (&iter);
1612
1613 /* Create and insert the auto field. */
1614
1615 if (asprintf (&auto_value_str, "%d", auto_value) != -1)
1616 {
1617 field = rec_field_new (field_name, auto_value_str);
1618 if (!field)
1619 {
1620 /* Out of memory. */
1621 free (auto_value_str);
1622 return false;
1623 }
1624
1625 if (!rec_mset_insert_at (rec_record_mset (record), MSET_FIELD, (void *) field, 0))
1626 {
1627 /* Out of memory. */
1628 free (auto_value_str);
1629 return false;
1630 }
1631
1632 free (auto_value_str);
1633 }
1634
1635 return true;
1636 }
1637
1638 static bool
rec_rset_add_auto_field_date(rec_rset_t rset,const char * field_name,rec_record_t record)1639 rec_rset_add_auto_field_date (rec_rset_t rset,
1640 const char *field_name,
1641 rec_record_t record)
1642 {
1643 rec_field_t auto_field;
1644 time_t t;
1645 char outstr[200];
1646 struct tm *tmp;
1647
1648 t = time (NULL);
1649 tmp = localtime (&t);
1650
1651 setlocale (LC_TIME, "C"); /* We want english dates that can be
1652 parsed with parse_datetime */
1653 strftime (outstr, sizeof(outstr), "%a, %d %b %Y %T %z", tmp);
1654 setlocale (LC_TIME, ""); /* And restore the locale from the
1655 environment. */
1656
1657 auto_field = rec_field_new (field_name, outstr);
1658 if (!auto_field)
1659 {
1660 /* Out of memory. */
1661 return false;
1662 }
1663
1664 if (!rec_mset_insert_at (rec_record_mset (record), MSET_FIELD, (void *) auto_field, 0))
1665 {
1666 /* Out of memory. */
1667 return false;
1668 }
1669
1670 return true;
1671 }
1672
1673 #if defined UUID_TYPE
1674
1675 static bool
rec_rset_add_auto_field_uuid(rec_rset_t rset,const char * field_name,rec_record_t record)1676 rec_rset_add_auto_field_uuid (rec_rset_t rset,
1677 const char *field_name,
1678 rec_record_t record)
1679 {
1680 rec_field_t auto_field;
1681 uuid_t uu;
1682 char uu_str[40]; /* Enough to hold any standard UUID. */
1683
1684 /* Generate a new time-based UUID using the libuuid library and use
1685 it for the value of the new auto field. */
1686
1687 uuid_generate_time (uu);
1688 uuid_unparse (uu, uu_str);
1689
1690 auto_field = rec_field_new (field_name, uu_str);
1691 if (!auto_field)
1692 {
1693 /* Out of memory. */
1694 return false;
1695 }
1696
1697 if (!rec_mset_insert_at (rec_record_mset (record), MSET_FIELD, (void *) auto_field, 0))
1698 {
1699 /* Out of memory. */
1700 return false;
1701 }
1702
1703 return true;
1704 }
1705
1706 #endif /* UUID_TYPE */
1707
1708 static rec_record_t
rec_rset_merge_records(rec_record_t to_record,rec_record_t from_record,rec_fex_t group_by_fields)1709 rec_rset_merge_records (rec_record_t to_record,
1710 rec_record_t from_record,
1711 rec_fex_t group_by_fields)
1712 {
1713 rec_mset_elem_t elem;
1714 void *data;
1715 rec_mset_iterator_t iter;
1716
1717 iter = rec_mset_iterator (rec_record_mset (from_record));
1718 while (rec_mset_iterator_next (&iter, MSET_ANY, (const void**) &data, &elem))
1719 {
1720 if (rec_mset_elem_type (elem) == MSET_FIELD)
1721 {
1722 rec_field_t field = (rec_field_t) data;
1723
1724 /* Don't add the field if it is in the list of group-by
1725 fields. */
1726
1727 if (rec_fex_member_p (group_by_fields, rec_field_name (field), -1, -1))
1728 {
1729 continue;
1730 }
1731
1732 /* Don't allow duplicated fields in the resulting record
1733 generated as a result of this operation. This is
1734 commented out because it fucks up the usage of aggregated
1735 functions in grouped-by record sets. */
1736
1737 /* if (rec_record_contains_field (to_record,
1738 rec_field_name (field),
1739 rec_field_value (field)))
1740 {
1741 continue;
1742 } */
1743
1744 /* Ok, add this field. */
1745
1746 if (!rec_mset_append (rec_record_mset (to_record),
1747 MSET_FIELD,
1748 (void *) rec_field_dup (field),
1749 MSET_ANY))
1750 {
1751 /* Out of memory. */
1752 return NULL;
1753 }
1754 }
1755 else
1756 {
1757 rec_comment_t comment = (rec_comment_t) data;
1758 rec_mset_append (rec_record_mset (to_record),
1759 MSET_COMMENT,
1760 (void *) rec_comment_dup (comment),
1761 MSET_ANY);
1762 }
1763 }
1764 rec_mset_iterator_free (&iter);
1765
1766 return to_record;
1767 }
1768
1769 static int
rec_rset_compare_typed_records(rec_rset_t rset,rec_record_t record1,rec_record_t record2,rec_fex_t fields)1770 rec_rset_compare_typed_records (rec_rset_t rset,
1771 rec_record_t record1,
1772 rec_record_t record2,
1773 rec_fex_t fields)
1774 {
1775 int result = 0;
1776 size_t i = 0;
1777 size_t num_fields = rec_fex_size (fields);
1778
1779 for (i = 0; i < num_fields; i++)
1780 {
1781 rec_fex_elem_t elem = rec_fex_get (fields, i);
1782 const char *field_name = rec_fex_elem_field_name (elem);
1783 rec_field_t field1 = rec_record_get_field_by_name (record1, field_name, 0);
1784 rec_field_t field2 = rec_record_get_field_by_name (record2, field_name, 0);
1785
1786 /* If any of the fields is not present in some of the records
1787 then that record is considered to be smaller than the record
1788 featuring the other one. */
1789
1790 if (field1 && !field2)
1791 {
1792 result = 1; /* field1 > field2 */
1793 break;
1794 }
1795 else if (!field1 && field2)
1796 {
1797 result = -1; /* field1 < field2 */
1798 break;
1799 }
1800 else if (!field1 && !field2)
1801 {
1802 result = -1; /* field1 < field2 */
1803 break;
1804 }
1805
1806 /* A field with such a name exists in both records. Compare the
1807 field typed values. */
1808
1809 result = rec_type_values_cmp (rec_rset_get_field_type (rset, field_name),
1810 rec_field_value (field1),
1811 rec_field_value (field2));
1812
1813 if (result != 0)
1814 {
1815 /* Either (a1, a2, ...) < (b1, b2, ...) or (a1, a2, ...) >
1816 (b1, b2, ...) */
1817
1818 break;
1819 }
1820 }
1821
1822 return result;
1823 }
1824
1825 /* End of rec-rset.c */
1826