[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[COMMITTED] src,torture,doc: support for singular fields
From: |
Jose E. Marchesi |
Subject: |
[COMMITTED] src,torture,doc: support for singular fields |
Date: |
Sat, 16 Apr 2022 13:17:42 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) |
2022-04-16 Jose E. Marchesi <jemarch@gnu.org>
* src/rec.h (enum rec_std_field_e): New entry REC_FIELD_SINGULAR.
* src/rec-field-name.c (fnames): Add "singular".
* src/rec-rset.c (rec_rset_rename_field): Handle
REC_FIELD_SINGULAR.
* src/rec-int.c (rec_int_check_record_singular): Define.
* torture/utils/recfix.sh: New tests.
* doc/recutils.texi: Document singular fields.
---
ChangeLog | 10 ++++++
doc/recutils.texi | 17 ++++++++++
src/rec-field-name.c | 3 +-
src/rec-int.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++-
src/rec-rset.c | 3 +-
src/rec.h | 3 +-
torture/utils/recfix.sh | 22 ++++++++++++-
7 files changed, 139 insertions(+), 5 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 0f42fd4..0d71572 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2022-04-16 Jose E. Marchesi <jemarch@gnu.org>
+
+ * src/rec.h (enum rec_std_field_e): New entry REC_FIELD_SINGULAR.
+ * src/rec-field-name.c (fnames): Add "singular".
+ * src/rec-rset.c (rec_rset_rename_field): Handle
+ REC_FIELD_SINGULAR.
+ * src/rec-int.c (rec_int_check_record_singular): Define.
+ * torture/utils/recfix.sh: New tests.
+ * doc/recutils.texi: Document singular fields.
+
2022-01-20 Liliana Marie Prikler <liliana.prikler@ist.tugraz.at>
* utils/recfmt.c (recfmt_apply_template): Add open brace as excluded
diff --git a/doc/recutils.texi b/doc/recutils.texi
index 837adf5..a32ea81 100644
--- a/doc/recutils.texi
+++ b/doc/recutils.texi
@@ -828,6 +828,8 @@ Restricting the size of your database. @xref{Size
Constraints}.
Enforcing arbitrary constraints. @xref{Arbitrary Constraints}.
@item %confidential
Storing confidential information. @xref{Encryption}.
+@item %singular
+Fields without repeating values.
@end table
@node Querying Recfiles
@@ -2851,6 +2853,7 @@ structure of the records.
* Prohibited Fields:: Forbidding the presence of fields.
* Allowed Fields:: Restricting the presence of fields.
* Keys and Unique Fields:: Fields characterizing records.
+* Singular Fields:: Fields with unique contents.
* Size Constraints:: Constraints on the number of records in a set.
* Arbitrary Constraints:: Constraints records must comply with.
@end menu
@@ -3084,6 +3087,20 @@ violations, and will be reported by a checking tool.
Elsewhere, we discuss how primary keys can be used to link one record set to
another using primary keys together with foreign keys. @xref{Queries which
Join Records}.
+@node Singular Fields
+@section Singular Fields
+
+Sometimes we require certain fields with a given name to not appear in
+a record set featuring the same contents, but we don't want (or we
+can't) declare such fields as the key of the record set.
+
+In these circumstances we can use @dfn{singular fields}, which are
+declared as such in the record descriptor using the @code{%singular}
+special field:
+
+@example
+%singular: @var{field}
+@end example
@node Size Constraints
@section Size Constraints
diff --git a/src/rec-field-name.c b/src/rec-field-name.c
index d063fe9..2a92606 100644
--- a/src/rec-field-name.c
+++ b/src/rec-field-name.c
@@ -49,7 +49,8 @@ static const char *fnames[] =
"%typedef",
"%unique",
"%constraint",
- "%allowed"
+ "%allowed",
+ "%singular",
};
const char *
diff --git a/src/rec-int.c b/src/rec-int.c
index d0a69b9..6449474 100644
--- a/src/rec-int.c
+++ b/src/rec-int.c
@@ -487,6 +487,89 @@ rec_int_check_record_key (rec_rset_t rset,
return res;
}
+static int
+rec_int_check_record_singular (rec_rset_t rset,
+ rec_record_t orig_record,
+ rec_record_t record,
+ rec_buf_t errors)
+{
+ int res = 0;
+ rec_record_t descriptor;
+ size_t i;
+
+ descriptor = rec_rset_descriptor (rset);
+ if (!descriptor)
+ return 0;
+
+ for (i = 0;
+ i < rec_record_get_num_fields_by_name (descriptor,
+ FNAME (REC_FIELD_SINGULAR));
+ ++i)
+ {
+ size_t j;
+ bool duplicated_singular = false;
+ rec_field_t field = rec_record_get_field_by_name (descriptor,
+ FNAME
(REC_FIELD_SINGULAR), i);
+ char *singular_field_name = rec_parse_field_name_str (rec_field_value
(field));
+
+ /* Ignore invalid %singular entries. */
+ if (!singular_field_name)
+ continue;
+
+ /* Iterate over all the fields in the record having this name. */
+ for (j = 0;
+ j < rec_record_get_num_fields_by_name (record, singular_field_name);
+ ++j)
+ {
+ /* Check that the value in this field is singular in the
+ whole record set. */
+ rec_mset_iterator_t iter;
+ rec_record_t other_record;
+ rec_field_t orig_field = rec_record_get_field_by_name (record,
+
singular_field_name,
+ j);
+
+ iter = rec_mset_iterator (rec_rset_mset (rset));
+ while (rec_mset_iterator_next (&iter, MSET_RECORD, (const void**)
&other_record, NULL))
+ {
+ size_t k;
+
+ for (k = 0;
+ k < rec_record_get_num_fields_by_name (other_record,
+ singular_field_name);
+ ++k)
+ {
+ rec_field_t f = rec_record_get_field_by_name (other_record,
+
singular_field_name,
+ k);
+ if (other_record != orig_record
+ && strcmp (rec_field_value (f),
+ rec_field_value (orig_field)) == 0)
+
+ {
+ duplicated_singular = true;
+ break;
+ }
+ }
+ }
+ rec_mset_iterator_free (&iter);
+ }
+
+ if (duplicated_singular)
+ {
+ ADD_ERROR (errors,
+ _("%s:%s: error: duplicated value in singular field '%s'
in record\n"),
+ rec_record_source (orig_record),
+ rec_record_location_str (orig_record),
+ singular_field_name);
+ res++;
+ break;
+ }
+ }
+
+ return res;
+}
+
static bool
rec_int_rec_type_p (const char *str)
{
@@ -1123,7 +1206,8 @@ rec_int_check_record (rec_db_t db,
#endif
+ rec_int_check_record_prohibit (rset, record, errors)
+ rec_int_check_record_sex_constraints (rset, record, errors)
- + rec_int_check_record_allowed (rset, record, errors);
+ + rec_int_check_record_allowed (rset, record, errors)
+ + rec_int_check_record_singular (rset, orig_record, record, errors);
return res;
}
diff --git a/src/rec-rset.c b/src/rec-rset.c
index 4ff27e8..d96bdb1 100644
--- a/src/rec-rset.c
+++ b/src/rec-rset.c
@@ -1268,7 +1268,8 @@ rec_rset_rename_field (rec_rset_t rset,
#if defined REC_CRYPT_SUPPORT
|| rec_field_name_equal_p (rec_field_name (field),
FNAME(REC_FIELD_CONFIDENTIAL))
#endif
- || rec_field_name_equal_p (rec_field_name (field),
FNAME(REC_FIELD_SORT)))
+ || rec_field_name_equal_p (rec_field_name (field),
FNAME(REC_FIELD_SORT))
+ || rec_field_name_equal_p (rec_field_name (field),
FNAME(REC_FIELD_SINGULAR)))
{
/* Rename the field in the fex expression that is the
value of the field. Skip invalid entries. */
diff --git a/src/rec.h b/src/rec.h
index 1a712be..4e03e08 100644
--- a/src/rec.h
+++ b/src/rec.h
@@ -397,7 +397,8 @@ enum rec_std_field_e
REC_FIELD_TYPEDEF,
REC_FIELD_UNIQUE,
REC_FIELD_CONSTRAINT,
- REC_FIELD_ALLOWED
+ REC_FIELD_ALLOWED,
+ REC_FIELD_SINGULAR
};
/******************* Field name utilities **********************/
diff --git a/torture/utils/recfix.sh b/torture/utils/recfix.sh
index 0f268cf..a8beccb 100755
--- a/torture/utils/recfix.sh
+++ b/torture/utils/recfix.sh
@@ -1380,6 +1380,21 @@ zzz: 40
yyy: 30
'
+test_declare_input_file multiple-singulars \
+'%rec: Foo
+%singular: Id
+%unique: Id
+
+Id: 0
+Name: Name1
+
+Id: 2
+Name: Name2
+
+Id: 2
+Name: Name3
+'
+
test_declare_input_file blanks-before-fex-in-type \
'%rec: Bug
%mandatory: Id Title Desc Status Reporter Time
@@ -2446,7 +2461,12 @@ test_tool recfix-blanks-before-fex-in-type ok \
'--check' \
blanks-before-fex-in-type \
''
-
+
+test_tool recfix-multiple-singulars xfail \
+ recfix \
+ '--check' \
+ multiple-singulars
+
#
# Cleanup.
#
--
2.11.0
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [COMMITTED] src,torture,doc: support for singular fields,
Jose E. Marchesi <=