From 68c93246b38e3dcf06df5414fdbcbb6626f616a4 Mon Sep 17 00:00:00 2001
From: Aleksey Demakov <ademakov@gmail.com>
Date: Mon, 23 Jan 2012 22:41:59 +0400
Subject: [PATCH] Make field type conflict a config error and optimize
 validate_indexes()

---
 mod/box/box.m                   | 92 +++++++++++++++++++++++----------
 test/box/configuration.result   |  7 +++
 test/box/configuration.test     | 14 +++++
 test/box/tarantool_bad_type.cfg | 24 +++++++++
 4 files changed, 111 insertions(+), 26 deletions(-)
 create mode 100644 test/box/tarantool_bad_type.cfg

diff --git a/mod/box/box.m b/mod/box/box.m
index ad926279e0..708830bd68 100644
--- a/mod/box/box.m
+++ b/mod/box/box.m
@@ -141,31 +141,40 @@ tuple_txn_ref(struct box_txn *txn, struct box_tuple *tuple)
 static void
 validate_indexes(struct box_txn *txn)
 {
-	int n = index_count(&space[txn->n]);
-	if (n <= 1)
-		return;
-
-	for (int i = 1; i < n; i++) {
-		Index *index = space[txn->n].index[i];
-
-		for (u32 f = 0; f < index->key_def->part_count; ++f) {
-			if (index->key_def->parts[f].fieldno >= txn->tuple->cardinality)
-				tnt_raise(IllegalParams, :"tuple must have all indexed fields");
-
-			if (index->key_def->parts[f].type == STRING)
-				continue;
+	struct space *sp = &space[txn->n];
+	int n = index_count(sp);
 
-			void *field = tuple_field(txn->tuple, index->key_def->parts[f].fieldno);
-			u32 len = load_varint32(&field);
+	/* Only secondary indexes are validated here. So check to see
+	   if there are any.*/
+	if (n <= 1) {
+		return;
+	}
 
-			if (index->key_def->parts[f].type == NUM && len != sizeof(u32))
+	/* Check to see if the tuple has a sufficient number of fields. */
+	if (txn->tuple->cardinality < sp->field_count)
+		tnt_raise(IllegalParams, :"tuple must have all indexed fields");
+
+	/* Sweep through the tuple and check the field sizes. */
+	u8 *data = txn->tuple->data;
+	for (int f = 0; f < sp->field_count; ++f) {
+		/* Get the size of the current field and advance. */
+		u32 len = load_varint32((void **) &data);
+		data += len;
+
+		/* Check fixed size fields (NUM and NUM64) and skip undefined
+		   size fields (STRING and UNKNOWN). */
+		if (sp->field_types[f] == NUM) {
+			if (len != sizeof(u32))
 				tnt_raise(IllegalParams, :"field must be NUM");
-
-			if (index->key_def->parts[f].type == NUM64 && len != sizeof(u64))
+		} else if (sp->field_types[f] == NUM64) {
+			if (len != sizeof(u64))
 				tnt_raise(IllegalParams, :"field must be NUM64");
 		}
+	}
 
-		/* Check key uniqueness */
+	/* Check key uniqueness */
+	for (int i = 1; i < n; ++i) {
+		Index *index = space[txn->n].index[i];
 		if (index->key_def->is_unique) {
 			struct box_tuple *tuple = [index findByTuple: txn->tuple];
 			if (tuple != NULL && tuple != txn->old_tuple)
@@ -1062,19 +1071,16 @@ space_init_field_types(struct space *space)
 		}
 	}
 
+#ifndef NDEBUG
 	/* validate field type info */
 	for (i = 0; i < key_count; i++) {
 		struct key_def *def = &key_defs[i];
 		for (int pi = 0; pi < def->part_count; pi++) {
 			struct key_part *part = &def->parts[pi];
-			if (space->field_types[part->fieldno] != part->type
-			    && space->field_types[part->fieldno] != UNKNOWN) {
-				space->field_types[part->fieldno] = UNKNOWN;
-				say_warn("type conflict in space %d for field %d",
-					 space_n(space), part->fieldno);
-			}
+			assert(space->field_types[part->fieldno] == part->type);
 		}
 	}
+#endif
 }
 
 static void
@@ -1361,6 +1367,8 @@ check_spaces(struct tarantool_cfg *conf)
 			return -1;
 		}
 
+		int max_key_fieldno = -1;
+
 		/* check spaces indexes */
 		for (size_t j = 0; space->index[j] != NULL; ++j) {
 			typeof(space->index[j]) index = space->index[j];
@@ -1404,6 +1412,10 @@ check_spaces(struct tarantool_cfg *conf)
 					return -1;
 				}
 
+				if (max_key_fieldno < key->fieldno) {
+					max_key_fieldno = key->fieldno;
+				}
+
 				++index_cardinality;
 			}
 
@@ -1458,6 +1470,34 @@ check_spaces(struct tarantool_cfg *conf)
 				assert(false);
 			}
 		}
+
+		/* Check for index field type conflicts */
+		if (max_key_fieldno >= 0) {
+			char *types = alloca(max_key_fieldno + 1);
+			memset(types, UNKNOWN, max_key_fieldno + 1);
+			for (size_t j = 0; space->index[j] != NULL; ++j) {
+				typeof(space->index[j]) index = space->index[j];
+				for (size_t k = 0; index->key_field[k] != NULL; ++k) {
+					typeof(index->key_field[k]) key = index->key_field[k];
+					int f = key->fieldno;
+					if (f == -1) {
+						break;
+					}
+					enum field_data_type t = STR2ENUM(field_data_type, key->type);
+					assert(t != field_data_type_MAX);
+					if (types[f] != t) {
+						if (types[f] == UNKNOWN) {
+							types[f] = t;
+						} else {
+							out_warning(0, "(space = %zu fieldno = %zu) "
+								    "index field type mismatch", i, f);
+							return -1;
+						}
+					}
+				}
+
+			}
+		}
 	}
 
 	return 0;
@@ -1602,7 +1642,7 @@ mod_init(void)
 	/* build secondary indexes */
 	build_indexes();
 
-	/* enable secondary indexes while now */
+	/* enable secondary indexes now */
 	secondary_indexes_enabled = true;
 
 	title("orphan");
diff --git a/test/box/configuration.result b/test/box/configuration.result
index f4c527e1c6..ded392434a 100644
--- a/test/box/configuration.result
+++ b/test/box/configuration.result
@@ -185,3 +185,10 @@ lua box.cfg.wal_fsync_delay
 ---
  - 0.01
 ...
+
+#  Test field type conflict in keys
+
+tarantool_box -c tarantool_bad_type.cfg
+tarantool_box: can't load config:
+ - (space = 0 fieldno = 0) index field type mismatch
+
diff --git a/test/box/configuration.test b/test/box/configuration.test
index e58585a34a..7eb7cb3bd6 100644
--- a/test/box/configuration.test
+++ b/test/box/configuration.test
@@ -1,5 +1,9 @@
 # encoding: tarantool
 #
+
+import os
+import sys
+
 print """
 # Bug #708685:
 #  Addition of required configuration file options broke backward
@@ -37,6 +41,16 @@ server.deploy("box/tarantool_bug876541.cfg")
 # check values
 exec admin "lua box.cfg.wal_fsync_delay"
 
+print """
+#  Test field type conflict in keys
+"""
+# stop current server
+server.stop()
+# start server with memcached space conflict
+sys.stdout.push_filter("(/\S+)+/tarantool", "tarantool")
+server.test_option("-c " + os.path.join(os.getcwd(), "box/tarantool_bad_type.cfg"))
+sys.stdout.pop_filter()
+
 # restore default server
 server.stop()
 server.deploy(self.suite_ini["config"])
diff --git a/test/box/tarantool_bad_type.cfg b/test/box/tarantool_bad_type.cfg
new file mode 100644
index 0000000000..3fdfd3044a
--- /dev/null
+++ b/test/box/tarantool_bad_type.cfg
@@ -0,0 +1,24 @@
+slab_alloc_arena = 0.1
+
+pid_file = "box.pid"
+
+logger="cat - >> tarantool.log"
+
+primary_port = 33013
+secondary_port = 33014
+admin_port = 33015
+
+rows_per_wal = 50
+# This is one of the few modifiable settings, change it
+too_long_threshold=2
+
+space[0].enabled = 1
+space[0].index[0].type = "HASH"
+space[0].index[0].unique = 1
+space[0].index[0].key_field[0].fieldno = 0
+space[0].index[0].key_field[0].type = "NUM"
+space[0].index[1].type = "TREE"
+space[0].index[1].unique = 1
+space[0].index[1].key_field[0].fieldno = 0
+space[0].index[1].key_field[0].type = "NUM64"
+
-- 
GitLab