Tarantool development patches archive
 help / color / mirror / Atom feed
From: Sergey Petrenko <sergepetrenko@tarantool.org>
To: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Cc: tarantool-patches@freelists.org
Subject: [tarantool-patches] Re: [PATCH] Allow nullability mismatch in space indices and format.
Date: Thu, 9 Aug 2018 10:09:49 +0300	[thread overview]
Message-ID: <ABCD1629-D5E3-45E7-B312-2C1783E29380@tarantool.org> (raw)
In-Reply-To: <8702b347-f250-8f9f-3571-17013e63d8c4@tarantool.org>

Hi! Thank you for review. I fixed your comments.
Please find the new diff below.

> 8 авг. 2018 г., в 15:50, Vladislav Shpilevoy <v.shpilevoy@tarantool.org> написал(а):
> 
> Hi! Thanks for the patch! See 8 comments below.
> 
> 1. Please, prefix the commit title with a subsystem name to
> which the patch is related. (Here it is 'box: ' I think).

Done.

> 
>> diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c
>> index 2e19d2e3c..a640c23d3 100644
>> --- a/src/box/tuple_format.c
>> +++ b/src/box/tuple_format.c
>> @@ -84,12 +84,11 @@ tuple_format_create(struct tuple_format *format, struct key_def * const *keys,
>>  			if (part->fieldno >= field_count) {
>>  				field->is_nullable = part->is_nullable;
>>  			} else if (field->is_nullable != part->is_nullable) {
>> -				diag_set(ClientError, ER_NULLABLE_MISMATCH,
>> -					 part->fieldno + TUPLE_INDEX_BASE,
>> -					 field->is_nullable ? "nullable" :
>> -					 "not nullable", part->is_nullable ?
>> -					 "nullable" : "not nullable");
>> -				return -1;
>> +				/*
> 
> 2. On the branch here a leading white space is. Please, remove.
> 
> 3. Please, wrap the comment by 66 symbols.
> 
> 4. ER_NULLABLE_MISMATCH is now unused. Please, replace it with
> a placeholder in errcode.h file. (For examples grep ER_UNUSED in
> the file).

Done, done, done.

>> +				 * In case of mismatch set the most strict
>> +				 * option for is_nullable.
>> +				 */
>> +				field->is_nullable = false;
>>  			}
>>    			/*
>> diff --git a/test/engine/ddl.result b/test/engine/ddl.result
>> index 0cb9ed6c2..c8fd8e6f2 100644
>> --- a/test/engine/ddl.result
>> +++ b/test/engine/ddl.result
>> @@ -1431,9 +1431,12 @@ s:create_index('primary', { parts = { 'field1' } })
>>  ---
>>  - error: Primary index of the space 'test' can not contain nullable parts
>>  ...
>> -s:create_index('primary', { parts = {{'field1', is_nullable = false}} })
>> +-- this is allowed, but the actual part is_nullable stays false
> 
> 5. Please, in comments start sentences from a capital letter and finish
> with the dot.

Done. 

> 
>> +pk = s:create_index('primary', { parts = {{'field1', is_nullable = false}} })
>> +---
>> +...
>> +pk:drop()
>>  ---
>> -- error: Field 1 is nullable in space format, but not nullable in index parts
>>  ...
>>  format[1].is_nullable = false
>>  ---
>> diff --git a/test/engine/null.result b/test/engine/null.result
>> index 4abf85021..e0ad7df8e 100644
>> --- a/test/engine/null.result
>> +++ b/test/engine/null.result
>> @@ -70,48 +70,8 @@ ok
>>  ---
>>  - false
>>  ...
>> --- Conflict of is_nullable in format and in parts.
>> -parts[1].is_nullable = false
>> ----
>> -...
>>  sk = s:create_index('sk', { parts = parts }) -- Fail.
> 
> 6. Now the comment is wrong - this line does not fail.

Fixed.

> 
>>  ---
>> -- error: Field 2 is nullable in space format, but not nullable in index parts
>> -...
>> --- Try skip nullable in format and specify in part.
>> -parts[1].is_nullable = true
>> ----
>> -...
>> -sk = s:create_index('sk', { parts = parts }) -- Ok.
>> ----
>> -...
>> -format[2].is_nullable = nil
>> ----
>> -...
>> -s:format(format) -- Fail.
>> ----
>> -- error: Field 2 is not nullable in space format, but nullable in index parts
>> -...
>> -sk:drop()
>> ----
>> -...
>> --- Try to set nullable in part with no format.
>> -s:format({})
>> ----
>> -...
>> -sk = s:create_index('sk', { parts = parts })
>> ----
>> -...
>> --- And then set format with no nullable.
>> -s:format(format) -- Fail.
>> ----
>> -- error: Field 2 is not nullable in space format, but nullable in index parts
>> -...
>> -format[2].is_nullable = true
>> ----
>> -...
>> -s:format(format) -- Ok.
>> ----
>>  ...
>>  -- Test insert.
>>  s:insert{1, 1}
>> @@ -1560,3 +1520,168 @@ i2:select{}
>>  s:drop()
>>  ---
>>  ...
>> +-- gh-3430 allow different nullability in space format and indices.
>> +-- resulting field nullability is the strictest of the two.
>> +s = box.schema.space.create('test', {engine=engine})
>> +---
>> +...
>> +pk = s:create_index('primary', {parts={1, 'unsigned'}})
>> +---
>> +...
>> +sk = s:create_index('secondary', {parts={2, 'unsigned', is_nullable=false}})
>> +---
>> +...
>> +format = {}
>> +---
>> +...
>> +format[1] = {name = 'first', type = 'unsigned', is_nullable = false}
>> +---
>> +...
>> +format[2] = {name = 'second', type = 'unsigned', is_nullable = false}
>> +---
>> +...
>> +s:format(format)
>> +---
>> +...
>> +-- field 2 is not nullable
>> +s:insert{5}
>> +---
>> +- error: Tuple field count 1 is less than required by space format or defined indexes
>> +    (expected at least 2)
>> +...
>> +s:insert{5, nil}
> 
> 7. {5, nil} == {5}, so I think you can omit this insertion. It is
> covered by the previous one.

Done.

> 8. What if I insert NULL into the space and then set this field to
> be not nullable in format or one of indexes? Please, add a test.

Added an extensive test, setting in format fails, setting in index fails.

Here’s diff:

 src/box/errcode.h         |   2 +-
 src/box/tuple_format.c    |  12 +--
 test/box/misc.result      |   5 +-
 test/engine/ddl.result    |  10 ++-
 test/engine/ddl.test.lua  |   6 +-
 test/engine/null.result   | 213 +++++++++++++++++++++++++++++++++++++---------
 test/engine/null.test.lua |  78 +++++++++++++----
 7 files changed, 255 insertions(+), 71 deletions(-)

diff --git a/src/box/errcode.h b/src/box/errcode.h
index 3d5f66af8..4115e6b65 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -205,7 +205,7 @@ struct errcode_record {
 	/*150 */_(ER_CANT_CREATE_COLLATION,	"Failed to initialize collation: %s.") \
 	/*151 */_(ER_WRONG_COLLATION_OPTIONS,	"Wrong collation options (field %u): %s") \
 	/*152 */_(ER_NULLABLE_PRIMARY,		"Primary index of the space '%s' can not contain nullable parts") \
-	/*153 */_(ER_NULLABLE_MISMATCH,		"Field %d is %s in space format, but %s in index parts") \
+	/*153 */_(ER_UNUSED,			"") \
 	/*154 */_(ER_TRANSACTION_YIELD,		"Transaction has been aborted by a fiber yield") \
 	/*155 */_(ER_NO_SUCH_GROUP,		"Replication group '%s' does not exist") \
 
diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c
index 2e19d2e3c..8a008858e 100644
--- a/src/box/tuple_format.c
+++ b/src/box/tuple_format.c
@@ -84,12 +84,12 @@ tuple_format_create(struct tuple_format *format, struct key_def * const *keys,
 			if (part->fieldno >= field_count) {
 				field->is_nullable = part->is_nullable;
 			} else if (field->is_nullable != part->is_nullable) {
-				diag_set(ClientError, ER_NULLABLE_MISMATCH,
-					 part->fieldno + TUPLE_INDEX_BASE,
-					 field->is_nullable ? "nullable" :
-					 "not nullable", part->is_nullable ?
-					 "nullable" : "not nullable");
-				return -1;
+				/*
+				 * In case of mismatch set the
+				 * most strict option for
+				 * is_nullable.
+				 */
+				field->is_nullable = false;
 			}
 
 			/*
diff --git a/test/box/misc.result b/test/box/misc.result
index 4895a78a2..8934e0c87 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -398,13 +398,12 @@ t;
   - 'box.error.UPDATE_ARG_TYPE : 26'
   - 'box.error.CROSS_ENGINE_TRANSACTION : 81'
   - 'box.error.FORMAT_MISMATCH_INDEX_PART : 27'
-  - 'box.error.injection : table: <address>
   - 'box.error.FUNCTION_TX_ACTIVE : 30'
+  - 'box.error.injection : table: <address>
   - 'box.error.ITERATOR_TYPE : 72'
-  - 'box.error.TRANSACTION_YIELD : 154'
   - 'box.error.NO_SUCH_ENGINE : 57'
   - 'box.error.COMMIT_IN_SUB_STMT : 122'
-  - 'box.error.NULLABLE_MISMATCH : 153'
+  - 'box.error.TRANSACTION_YIELD : 154'
   - 'box.error.UNSUPPORTED : 5'
   - 'box.error.LAST_DROP : 15'
   - 'box.error.SPACE_FIELD_IS_DUPLICATE : 149'
diff --git a/test/engine/ddl.result b/test/engine/ddl.result
index 0cb9ed6c2..c114fb98b 100644
--- a/test/engine/ddl.result
+++ b/test/engine/ddl.result
@@ -1431,9 +1431,12 @@ s:create_index('primary', { parts = { 'field1' } })
 ---
 - error: Primary index of the space 'test' can not contain nullable parts
 ...
-s:create_index('primary', { parts = {{'field1', is_nullable = false}} })
+-- This is allowed, but the actual part is_nullable stays false.
+pk = s:create_index('primary', { parts = {{'field1', is_nullable = false}} })
+---
+...
+pk:drop()
 ---
-- error: Field 1 is nullable in space format, but not nullable in index parts
 ...
 format[1].is_nullable = false
 ---
@@ -1462,9 +1465,10 @@ s:insert({1, NULL})
 format[1].is_nullable = true
 ---
 ...
+-- The format is allowed since in primary index parts
+-- is_nullable is still set to false.
 s:format(format)
 ---
-- error: Field 1 is nullable in space format, but not nullable in index parts
 ...
 format[1].is_nullable = false
 ---
diff --git a/test/engine/ddl.test.lua b/test/engine/ddl.test.lua
index a128f6000..57aa756ab 100644
--- a/test/engine/ddl.test.lua
+++ b/test/engine/ddl.test.lua
@@ -534,7 +534,9 @@ format[1] = { name = 'field1', type = 'unsigned', is_nullable = true }
 format[2] = { name = 'field2', type = 'unsigned', is_nullable = true }
 s = box.schema.space.create('test', {engine = engine, format = format})
 s:create_index('primary', { parts = { 'field1' } })
-s:create_index('primary', { parts = {{'field1', is_nullable = false}} })
+-- This is allowed, but the actual part is_nullable stays false.
+pk = s:create_index('primary', { parts = {{'field1', is_nullable = false}} })
+pk:drop()
 format[1].is_nullable = false
 s:format(format)
 s:create_index('primary', { parts = {{'field1', is_nullable = true}} })
@@ -545,6 +547,8 @@ i.parts
 -- Check that is_nullable can't be set to false on non-empty space
 s:insert({1, NULL})
 format[1].is_nullable = true
+-- The format is allowed since in primary index parts
+-- is_nullable is still set to false.
 s:format(format)
 format[1].is_nullable = false
 format[2].is_nullable = false
diff --git a/test/engine/null.result b/test/engine/null.result
index 4abf85021..48d678443 100644
--- a/test/engine/null.result
+++ b/test/engine/null.result
@@ -70,49 +70,9 @@ ok
 ---
 - false
 ...
--- Conflict of is_nullable in format and in parts.
-parts[1].is_nullable = false
----
-...
-sk = s:create_index('sk', { parts = parts }) -- Fail.
----
-- error: Field 2 is nullable in space format, but not nullable in index parts
-...
--- Try skip nullable in format and specify in part.
-parts[1].is_nullable = true
----
-...
-sk = s:create_index('sk', { parts = parts }) -- Ok.
----
-...
-format[2].is_nullable = nil
----
-...
-s:format(format) -- Fail.
----
-- error: Field 2 is not nullable in space format, but nullable in index parts
-...
-sk:drop()
----
-...
--- Try to set nullable in part with no format.
-s:format({})
----
-...
 sk = s:create_index('sk', { parts = parts })
 ---
 ...
--- And then set format with no nullable.
-s:format(format) -- Fail.
----
-- error: Field 2 is not nullable in space format, but nullable in index parts
-...
-format[2].is_nullable = true
----
-...
-s:format(format) -- Ok.
----
-...
 -- Test insert.
 s:insert{1, 1}
 ---
@@ -1560,3 +1520,176 @@ i2:select{}
 s:drop()
 ---
 ...
+-- gh-3430 allow different nullability in space format and indices.
+-- Resulting field nullability is the strictest of the two.
+s = box.schema.space.create('test', {engine=engine})
+---
+...
+pk = s:create_index('primary', {parts={1, 'unsigned'}})
+---
+...
+sk = s:create_index('secondary', {parts={2, 'unsigned', is_nullable=false}})
+---
+...
+format = {}
+---
+...
+format[1] = {name = 'first', type = 'unsigned', is_nullable = false}
+---
+...
+format[2] = {name = 'second', type = 'unsigned', is_nullable = false}
+---
+...
+s:format(format)
+---
+...
+-- Field 2 is not nullable.
+s:insert{5}
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+s:insert{5, box.NULL}
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=true}}} -- This is allowed.
+---
+...
+-- Without space format setting this fails.
+s:insert{5, box.NULL}
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s:insert{5}
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}}
+---
+...
+format[2].is_nullable = true
+---
+...
+s:format(format) -- This is also allowed.
+---
+...
+-- inserts still fail due to not nullable index parts.
+s:insert{5, box.NULL}
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s:insert{5}
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=true}}}
+---
+...
+-- Now the field really is nullable.
+-- Success.
+s:insert{5, box.NULL}
+---
+- [5, null]
+...
+s:insert{6}
+---
+- [6]
+...
+s:insert{7, 8}
+---
+- [7, 8]
+...
+s:select{}
+---
+- - [5, null]
+  - [6]
+  - [7, 8]
+...
+-- Check that we cannot set field nullability to false when the
+-- space has tuples with NULL in this field.
+format[2].is_nullable = false
+---
+...
+s:format(format) -- Fail.
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}} -- Fail.
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+_ = s:delete{5}
+---
+...
+s:format(format) -- Still fail.
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}} -- Still fail.
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+-- Now check we can set nullability to false step by step.
+_ = s:delete{6}
+---
+...
+format[2].is_nullable = false
+---
+...
+s:format(format)
+---
+...
+s:insert{5, box.NULL} -- Fail.
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s:insert{5} -- Fail.
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+format[2].is_nullable = true
+---
+...
+s:format(format)
+---
+...
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}}
+---
+...
+s:insert{5, box.NULL} -- Fail.
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s:insert{5} -- Fail.
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+format[2].is_nullable = false
+---
+...
+s:format(format)
+---
+...
+s:select{}
+---
+- - [7, 8]
+...
+s:insert{5} -- Fail.
+---
+- error: Tuple field count 1 is less than required by space format or defined indexes
+    (expected at least 2)
+...
+s:insert{9, 10} -- Success.
+---
+- [9, 10]
+...
+s:drop()
+---
+...
diff --git a/test/engine/null.test.lua b/test/engine/null.test.lua
index 7f5a7dd33..5eb068499 100644
--- a/test/engine/null.test.lua
+++ b/test/engine/null.test.lua
@@ -35,24 +35,7 @@ pk = s:create_index('pk')
 ok = pcall(s.create_index, s, 'sk', { parts = parts, type = 'hash' }) -- Fail.
 ok
 
--- Conflict of is_nullable in format and in parts.
-parts[1].is_nullable = false
-sk = s:create_index('sk', { parts = parts }) -- Fail.
-
--- Try skip nullable in format and specify in part.
-parts[1].is_nullable = true
-sk = s:create_index('sk', { parts = parts }) -- Ok.
-format[2].is_nullable = nil
-s:format(format) -- Fail.
-sk:drop()
-
--- Try to set nullable in part with no format.
-s:format({})
 sk = s:create_index('sk', { parts = parts })
--- And then set format with no nullable.
-s:format(format) -- Fail.
-format[2].is_nullable = true
-s:format(format) -- Ok.
 
 -- Test insert.
 
@@ -461,3 +444,64 @@ i2:select{}
 i2:select{box.NULL, 50}
 i2:select{}
 s:drop()
+
+-- gh-3430 allow different nullability in space format and indices.
+-- Resulting field nullability is the strictest of the two.
+s = box.schema.space.create('test', {engine=engine})
+pk = s:create_index('primary', {parts={1, 'unsigned'}})
+sk = s:create_index('secondary', {parts={2, 'unsigned', is_nullable=false}})
+format = {}
+format[1] = {name = 'first', type = 'unsigned', is_nullable = false}
+format[2] = {name = 'second', type = 'unsigned', is_nullable = false}
+s:format(format)
+-- Field 2 is not nullable.
+s:insert{5}
+s:insert{5, box.NULL}
+
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=true}}} -- This is allowed.
+-- Without space format setting this fails.
+s:insert{5, box.NULL}
+s:insert{5}
+
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}}
+format[2].is_nullable = true
+s:format(format) -- This is also allowed.
+-- inserts still fail due to not nullable index parts.
+s:insert{5, box.NULL}
+s:insert{5}
+
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=true}}}
+-- Now the field really is nullable.
+-- Success.
+s:insert{5, box.NULL}
+s:insert{6}
+s:insert{7, 8}
+s:select{}
+
+-- Check that we cannot set field nullability to false when the
+-- space has tuples with NULL in this field.
+format[2].is_nullable = false
+s:format(format) -- Fail.
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}} -- Fail.
+_ = s:delete{5}
+s:format(format) -- Still fail.
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}} -- Still fail.
+-- Now check we can set nullability to false step by step.
+_ = s:delete{6}
+
+format[2].is_nullable = false
+s:format(format)
+s:insert{5, box.NULL} -- Fail.
+s:insert{5} -- Fail.
+format[2].is_nullable = true
+s:format(format)
+s.index.secondary:alter{parts={{2, 'unsigned', is_nullable=false}}}
+s:insert{5, box.NULL} -- Fail.
+s:insert{5} -- Fail.
+format[2].is_nullable = false
+s:format(format)
+s:select{}
+s:insert{5} -- Fail.
+s:insert{9, 10} -- Success.
+
+s:drop()
-- 
2.15.2 (Apple Git-101.1)

  reply	other threads:[~2018-08-09  7:09 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-07 14:48 [tarantool-patches] " Serge Petrenko
2018-08-08 12:50 ` [tarantool-patches] " Vladislav Shpilevoy
2018-08-09  7:09   ` Sergey Petrenko [this message]
2018-08-13 13:42     ` Vladislav Shpilevoy
2018-08-16  9:37       ` Vladimir Davydov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ABCD1629-D5E3-45E7-B312-2C1783E29380@tarantool.org \
    --to=sergepetrenko@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='[tarantool-patches] Re: [PATCH] Allow nullability mismatch in space indices and format.' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox