Commit 7c6b884e authored by mskold@mysql.com's avatar mskold@mysql.com

Added support for NULL in unique index

parent a454b977
...@@ -21,6 +21,28 @@ insert into t1 values(7,8,3); ...@@ -21,6 +21,28 @@ insert into t1 values(7,8,3);
select * from t1 where b = 4 order by a; select * from t1 where b = 4 order by a;
a b c a b c
3 4 6 3 4 6
insert into t1 values(8, 2, 3);
ERROR 23000: Can't write, because of unique constraint, to table 't1'
select * from t1 order by a;
a b c
1 2 3
2 3 5
3 4 6
4 5 8
5 6 2
6 7 2
7 8 3
delete from t1 where a = 1;
insert into t1 values(8, 2, 3);
select * from t1 order by a;
a b c
2 3 5
3 4 6
4 5 8
5 6 2
6 7 2
7 8 3
8 2 3
drop table t1; drop table t1;
CREATE TABLE t2 ( CREATE TABLE t2 (
a int unsigned NOT NULL PRIMARY KEY, a int unsigned NOT NULL PRIMARY KEY,
...@@ -42,6 +64,28 @@ insert into t2 values(7,8,3); ...@@ -42,6 +64,28 @@ insert into t2 values(7,8,3);
select * from t2 where b = 4 order by a; select * from t2 where b = 4 order by a;
a b c a b c
3 4 6 3 4 6
insert into t2 values(8, 2, 3);
ERROR 23000: Can't write, because of unique constraint, to table 't2'
select * from t2 order by a;
a b c
1 2 3
2 3 5
3 4 6
4 5 8
5 6 2
6 7 2
7 8 3
delete from t2 where a = 1;
insert into t2 values(8, 2, 3);
select * from t2 order by a;
a b c
2 3 5
3 4 6
4 5 8
5 6 2
6 7 2
7 8 3
8 2 3
drop table t2; drop table t2;
CREATE TABLE t3 ( CREATE TABLE t3 (
a int unsigned NOT NULL, a int unsigned NOT NULL,
...@@ -74,8 +118,10 @@ INSERT INTO t1 VALUES (8,'dummy'); ...@@ -74,8 +118,10 @@ INSERT INTO t1 VALUES (8,'dummy');
CREATE TABLE t2 ( CREATE TABLE t2 (
cid bigint(20) unsigned NOT NULL auto_increment, cid bigint(20) unsigned NOT NULL auto_increment,
cap varchar(255) NOT NULL default '', cap varchar(255) NOT NULL default '',
PRIMARY KEY (cid) PRIMARY KEY (cid),
UNIQUE KEY (cid, cap)
) engine=ndbcluster; ) engine=ndbcluster;
INSERT INTO t2 VALUES (NULL,'another dummy');
CREATE TABLE t3 ( CREATE TABLE t3 (
gid bigint(20) unsigned NOT NULL auto_increment, gid bigint(20) unsigned NOT NULL auto_increment,
gn varchar(255) NOT NULL default '', gn varchar(255) NOT NULL default '',
...@@ -132,6 +178,9 @@ cid cv ...@@ -132,6 +178,9 @@ cid cv
8 dummy 8 dummy
select * from t1 where cv = 'test'; select * from t1 where cv = 'test';
cid cv cid cv
select * from t2 where cap = 'another dummy';
cid cap
0 another dummy
select * from t4 where uid = 1 and gid=1 and rid=2 and cid=4; select * from t4 where uid = 1 and gid=1 and rid=2 and cid=4;
uid gid rid cid uid gid rid cid
1 1 2 4 1 1 2 4
......
...@@ -114,3 +114,23 @@ select * from t1 where b=4 and c<=5 order by a; ...@@ -114,3 +114,23 @@ select * from t1 where b=4 and c<=5 order by a;
select * from t1 where b<=4 and c<=5 order by a; select * from t1 where b<=4 and c<=5 order by a;
select * from t1 where b<=5 and c=0 or b<=5 and c=2; select * from t1 where b<=5 and c=0 or b<=5 and c=2;
drop table t1; drop table t1;
#
# Indexing NULL values
#
#CREATE TABLE t1 (
# a int unsigned NOT NULL PRIMARY KEY,
# b int unsigned,
# c int unsigned,
# KEY bc(b,c)
#) engine = ndb;
#insert into t1 values(1,1,1),(2,NULL,2),(3,NULL,NULL),(4,4,NULL);
#select * from t1 use index (bc);
#select count(*) from t1 use index (bc);
#select count(*) from t1 use index (PRIMARY) where b IS NULL;
#select count(*) from t1 use index (bc) where b IS NULL;
#select count(*) from t1 use index (bc) where b IS NULL and c = 2;
#select count(*) from t1 use index (bc) where b IS NOT NULL;
#drop table t1;
...@@ -21,6 +21,13 @@ select * from t1 where b = 4 order by b; ...@@ -21,6 +21,13 @@ select * from t1 where b = 4 order by b;
insert into t1 values(7,8,3); insert into t1 values(7,8,3);
select * from t1 where b = 4 order by a; select * from t1 where b = 4 order by a;
-- error 1169
insert into t1 values(8, 2, 3);
select * from t1 order by a;
delete from t1 where a = 1;
insert into t1 values(8, 2, 3);
select * from t1 order by a;
drop table t1; drop table t1;
...@@ -42,6 +49,13 @@ select * from t2 where c = 6; ...@@ -42,6 +49,13 @@ select * from t2 where c = 6;
insert into t2 values(7,8,3); insert into t2 values(7,8,3);
select * from t2 where b = 4 order by a; select * from t2 where b = 4 order by a;
-- error 1169
insert into t2 values(8, 2, 3);
select * from t2 order by a;
delete from t2 where a = 1;
insert into t2 values(8, 2, 3);
select * from t2 order by a;
drop table t2; drop table t2;
# #
...@@ -64,6 +78,48 @@ select * from t3 where b = 4 order by a; ...@@ -64,6 +78,48 @@ select * from t3 where b = 4 order by a;
drop table t3; drop table t3;
#
# Indexes on NULL-able columns
#
#CREATE TABLE t1 (
# pk int NOT NULL PRIMARY KEY,
# a int unsigned,
# UNIQUE KEY (a)
#) engine=ndbcluster;
#insert into t1 values (-1,NULL), (0,0), (1,NULL),(2,2),(3,NULL),(4,4);
#select * from t1 order by pk;
#--error 1169
#insert into t1 values (5,0);
#select * from t1 order by pk;
#delete from t1 where a = 0;
#insert into t1 values (5,0);
#select * from t1 order by pk;
#CREATE TABLE t2 (
# pk int NOT NULL PRIMARY KEY,
# a int unsigned,
# b tinyint NOT NULL,
# c VARCHAR(10),
# UNIQUE KEY si(a, c)
#) engine=ndbcluster;
#insert into t2 values (-1,1,17,NULL),(0,NULL,18,NULL),(1,3,19,'abc');
#select * from t2 order by pk;
#--error 1169
#insert into t2 values(2,3,19,'abc');
#select * from t2 order by pk;
#delete from t2 where c IS NOT NULL;
#insert into t2 values(2,3,19,'abc');
#select * from t2 order by pk;
#drop table t1, t2;
# #
# More complex tables # More complex tables
# #
...@@ -78,8 +134,10 @@ INSERT INTO t1 VALUES (8,'dummy'); ...@@ -78,8 +134,10 @@ INSERT INTO t1 VALUES (8,'dummy');
CREATE TABLE t2 ( CREATE TABLE t2 (
cid bigint(20) unsigned NOT NULL auto_increment, cid bigint(20) unsigned NOT NULL auto_increment,
cap varchar(255) NOT NULL default '', cap varchar(255) NOT NULL default '',
PRIMARY KEY (cid) PRIMARY KEY (cid),
UNIQUE KEY (cid, cap)
) engine=ndbcluster; ) engine=ndbcluster;
INSERT INTO t2 VALUES (NULL,'another dummy');
CREATE TABLE t3 ( CREATE TABLE t3 (
gid bigint(20) unsigned NOT NULL auto_increment, gid bigint(20) unsigned NOT NULL auto_increment,
gn varchar(255) NOT NULL default '', gn varchar(255) NOT NULL default '',
...@@ -134,6 +192,7 @@ INSERT INTO t7 VALUES(10, 5, 1, 1, 10); ...@@ -134,6 +192,7 @@ INSERT INTO t7 VALUES(10, 5, 1, 1, 10);
select * from t1 where cv = 'dummy'; select * from t1 where cv = 'dummy';
select * from t1 where cv = 'test'; select * from t1 where cv = 'test';
select * from t2 where cap = 'another dummy';
select * from t4 where uid = 1 and gid=1 and rid=2 and cid=4; select * from t4 where uid = 1 and gid=1 and rid=2 and cid=4;
select * from t4 where uid = 1 and gid=1 and rid=1 and cid=4; select * from t4 where uid = 1 and gid=1 and rid=1 and cid=4;
select * from t4 where uid = 1 order by cid; select * from t4 where uid = 1 order by cid;
......
...@@ -6255,16 +6255,6 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) ...@@ -6255,16 +6255,6 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr)
jam(); jam();
found = true; found = true;
const Uint32 a = aRec->attributeDescriptor; const Uint32 a = aRec->attributeDescriptor;
bool isNullable = AttributeDescriptor::getNullable(a);
// We do not allow more than one NULLable attribute for hash index
if (isNullable &&
indexPtr.p->isHashIndex() &&
(opPtr.p->m_attrList.sz > 1)) {
jam();
opPtr.p->m_errorCode = CreateIndxRef::AttributeNullable;
opPtr.p->m_errorLine = __LINE__;
return;
}
if (indexPtr.p->isHashIndex()) { if (indexPtr.p->isHashIndex()) {
const Uint32 s1 = AttributeDescriptor::getSize(a); const Uint32 s1 = AttributeDescriptor::getSize(a);
const Uint32 s2 = AttributeDescriptor::getArraySize(a); const Uint32 s2 = AttributeDescriptor::getArraySize(a);
......
...@@ -139,6 +139,7 @@ ...@@ -139,6 +139,7 @@
#define ZNOT_FOUND 626 #define ZNOT_FOUND 626
#define ZALREADYEXIST 630 #define ZALREADYEXIST 630
#define ZINCONSISTENTHASHINDEX 892 #define ZINCONSISTENTHASHINDEX 892
#define ZNOTUNIQUE 893
#endif #endif
class Dbtc: public SimulatedBlock { class Dbtc: public SimulatedBlock {
......
...@@ -4925,7 +4925,9 @@ void Dbtc::execLQHKEYREF(Signal* signal) ...@@ -4925,7 +4925,9 @@ void Dbtc::execLQHKEYREF(Signal* signal)
// The operation executed an index trigger // The operation executed an index trigger
const Uint32 opType = regTcPtr->operation; const Uint32 opType = regTcPtr->operation;
if (!(opType == ZDELETE && errCode == ZNOT_FOUND)) { if (errCode == ZALREADYEXIST)
errCode = terrorCode = ZNOTUNIQUE;
else if (!(opType == ZDELETE && errCode == ZNOT_FOUND)) {
jam(); jam();
/** /**
* "Normal path" * "Normal path"
...@@ -12168,34 +12170,33 @@ void Dbtc::insertIntoIndexTable(Signal* signal, ...@@ -12168,34 +12170,33 @@ void Dbtc::insertIntoIndexTable(Signal* signal,
// Calculate key length and renumber attribute id:s // Calculate key length and renumber attribute id:s
AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool; AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
LocalDataBuffer<11> afterValues(pool, firedTriggerData->afterValues); LocalDataBuffer<11> afterValues(pool, firedTriggerData->afterValues);
bool skipNull = false;
for(bool moreKeyAttrs = afterValues.first(iter); moreKeyAttrs; attrId++) { for(bool moreKeyAttrs = afterValues.first(iter); moreKeyAttrs; attrId++) {
jam(); jam();
AttributeHeader* attrHeader = (AttributeHeader *) iter.data; AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
// Filter out NULL valued attributes
if (attrHeader->isNULL()) {
skipNull = true;
break;
}
attrHeader->setAttributeId(attrId); attrHeader->setAttributeId(attrId);
keyLength += attrHeader->getDataSize(); keyLength += attrHeader->getDataSize();
hops = attrHeader->getHeaderSize() + attrHeader->getDataSize(); hops = attrHeader->getHeaderSize() + attrHeader->getDataSize();
moreKeyAttrs = afterValues.next(iter, hops); moreKeyAttrs = afterValues.next(iter, hops);
} }
if (skipNull) {
// Filter out single NULL attributes
if (attrId == 1) {
jam(); jam();
afterValues.first(iter); opRecord->triggerExecutionCount--;
AttributeHeader* attrHeader = (AttributeHeader *) iter.data; if (opRecord->triggerExecutionCount == 0) {
if (attrHeader->isNULL() && !afterValues.next(iter)) { /*
jam(); We have completed current trigger execution
opRecord->triggerExecutionCount--; Continue triggering operation
if (opRecord->triggerExecutionCount == 0) { */
/* jam();
We have completed current trigger execution continueTriggeringOp(signal, opRecord);
Continue triggering operation
*/
jam();
continueTriggeringOp(signal, opRecord);
}//if
return;
}//if }//if
return;
}//if }//if
// Calculate total length of primary key to be stored in index table // Calculate total length of primary key to be stored in index table
...@@ -12523,36 +12524,36 @@ void Dbtc::deleteFromIndexTable(Signal* signal, ...@@ -12523,36 +12524,36 @@ void Dbtc::deleteFromIndexTable(Signal* signal,
// Calculate key length and renumber attribute id:s // Calculate key length and renumber attribute id:s
AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool; AttributeBuffer::DataBufferPool & pool = c_theAttributeBufferPool;
LocalDataBuffer<11> beforeValues(pool, firedTriggerData->beforeValues); LocalDataBuffer<11> beforeValues(pool, firedTriggerData->beforeValues);
bool skipNull = false;
for(bool moreKeyAttrs = beforeValues.first(iter); for(bool moreKeyAttrs = beforeValues.first(iter);
(moreKeyAttrs); (moreKeyAttrs);
attrId++) { attrId++) {
jam(); jam();
AttributeHeader* attrHeader = (AttributeHeader *) iter.data; AttributeHeader* attrHeader = (AttributeHeader *) iter.data;
// Filter out NULL valued attributes
if (attrHeader->isNULL()) {
skipNull = true;
break;
}
attrHeader->setAttributeId(attrId); attrHeader->setAttributeId(attrId);
keyLength += attrHeader->getDataSize(); keyLength += attrHeader->getDataSize();
hops = attrHeader->getHeaderSize() + attrHeader->getDataSize(); hops = attrHeader->getHeaderSize() + attrHeader->getDataSize();
moreKeyAttrs = beforeValues.next(iter, hops); moreKeyAttrs = beforeValues.next(iter, hops);
} }
// Filter out single NULL attributes if (skipNull) {
if (attrId == 1) {
jam(); jam();
beforeValues.first(iter); opRecord->triggerExecutionCount--;
AttributeHeader* attrHeader = (AttributeHeader *) iter.data; if (opRecord->triggerExecutionCount == 0) {
if (attrHeader->isNULL() && !beforeValues.next(iter)) { /*
jam();
opRecord->triggerExecutionCount--;
if (opRecord->triggerExecutionCount == 0) {
/*
We have completed current trigger execution We have completed current trigger execution
Continue triggering operation Continue triggering operation
*/ */
jam(); jam();
continueTriggeringOp(signal, opRecord); continueTriggeringOp(signal, opRecord);
}//if
return;
}//if }//if
return;
}//if }//if
TcKeyReq::setKeyLength(tcKeyRequestInfo, keyLength); TcKeyReq::setKeyLength(tcKeyRequestInfo, keyLength);
......
...@@ -814,8 +814,8 @@ void Trix::executeInsertTransaction(Signal* signal, ...@@ -814,8 +814,8 @@ void Trix::executeInsertTransaction(Signal* signal,
for(Uint32 i = 0; i < headerPtr.sz; i++) { for(Uint32 i = 0; i < headerPtr.sz; i++) {
AttributeHeader* keyAttrHead = (AttributeHeader *) headerBuffer + i; AttributeHeader* keyAttrHead = (AttributeHeader *) headerBuffer + i;
// Filter out single NULL attributes // Filter out NULL attributes
if (keyAttrHead->isNULL() && (i == (Uint32)0) && (headerPtr.sz == (Uint32)2)) if (keyAttrHead->isNULL())
return; return;
if (i < subRec->noOfIndexColumns) if (i < subRec->noOfIndexColumns)
......
...@@ -1851,13 +1851,6 @@ NdbDictInterface::createIndex(Ndb & ndb, ...@@ -1851,13 +1851,6 @@ NdbDictInterface::createIndex(Ndb & ndb,
m_error.code = 4245; m_error.code = 4245;
return -1; return -1;
} }
if (it == DictTabInfo::UniqueHashIndex &&
(col->m_nullable) && (attributeList.sz > 1)) {
// We only support one NULL attribute
m_error.code = 4246;
return -1;
}
attributeList.id[i] = col->m_attrId; attributeList.id[i] = col->m_attrId;
} }
if (it == DictTabInfo::UniqueHashIndex) { if (it == DictTabInfo::UniqueHashIndex) {
......
...@@ -1040,11 +1040,11 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op, ...@@ -1040,11 +1040,11 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
bounds[bound], bounds[bound],
field->field_name)); field->field_name));
DBUG_DUMP("key", (char*)key_ptr, field_len); DBUG_DUMP("key", (char*)key_ptr, field_len);
if (op->setBound(field->field_name, if (op->setBound(field->field_name,
bound, bound,
key_ptr, field->is_null() ? 0 : key_ptr,
field_len) != 0) field->is_null() ? 0 : field_len) != 0)
ERR_RETURN(op->getNdbError()); ERR_RETURN(op->getNdbError());
key_ptr+= field_len; key_ptr+= field_len;
...@@ -1293,8 +1293,6 @@ int ha_ndbcluster::write_row(byte *record) ...@@ -1293,8 +1293,6 @@ int ha_ndbcluster::write_row(byte *record)
update_timestamp(record+table->timestamp_default_now-1); update_timestamp(record+table->timestamp_default_now-1);
has_auto_increment= (table->next_number_field && record == table->record[0]); has_auto_increment= (table->next_number_field && record == table->record[0]);
skip_auto_increment= table->auto_increment_field_not_null; skip_auto_increment= table->auto_increment_field_not_null;
if ((has_auto_increment) && (!skip_auto_increment))
update_auto_increment();
if (!(op= trans->getNdbOperation(m_tabname))) if (!(op= trans->getNdbOperation(m_tabname)))
ERR_RETURN(trans->getNdbError()); ERR_RETURN(trans->getNdbError());
...@@ -1313,6 +1311,10 @@ int ha_ndbcluster::write_row(byte *record) ...@@ -1313,6 +1311,10 @@ int ha_ndbcluster::write_row(byte *record)
else else
{ {
int res; int res;
if ((has_auto_increment) && (!skip_auto_increment))
update_auto_increment();
if ((res= set_primary_key(op))) if ((res= set_primary_key(op)))
return res; return res;
} }
...@@ -1323,7 +1325,10 @@ int ha_ndbcluster::write_row(byte *record) ...@@ -1323,7 +1325,10 @@ int ha_ndbcluster::write_row(byte *record)
Field *field= table->field[i]; Field *field= table->field[i];
if (!(field->flags & PRI_KEY_FLAG) && if (!(field->flags & PRI_KEY_FLAG) &&
set_ndb_value(op, field, i)) set_ndb_value(op, field, i))
{
skip_auto_increment= true;
ERR_RETURN(op->getNdbError()); ERR_RETURN(op->getNdbError());
}
} }
/* /*
...@@ -1345,7 +1350,10 @@ int ha_ndbcluster::write_row(byte *record) ...@@ -1345,7 +1350,10 @@ int ha_ndbcluster::write_row(byte *record)
(int)rows_inserted, (int)bulk_insert_rows)); (int)rows_inserted, (int)bulk_insert_rows));
bulk_insert_not_flushed= false; bulk_insert_not_flushed= false;
if (trans->execute(NoCommit) != 0) if (trans->execute(NoCommit) != 0)
{
skip_auto_increment= true;
DBUG_RETURN(ndb_err(trans)); DBUG_RETURN(ndb_err(trans));
}
} }
if ((has_auto_increment) && (skip_auto_increment)) if ((has_auto_increment) && (skip_auto_increment))
{ {
...@@ -3068,6 +3076,7 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): ...@@ -3068,6 +3076,7 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
m_ndb(NULL), m_ndb(NULL),
m_table(NULL), m_table(NULL),
m_table_flags(HA_REC_NOT_IN_SEQ | m_table_flags(HA_REC_NOT_IN_SEQ |
//HA_NULL_IN_KEY |
HA_NOT_EXACT_COUNT | HA_NOT_EXACT_COUNT |
HA_NO_PREFIX_CHAR_KEYS), HA_NO_PREFIX_CHAR_KEYS),
m_use_write(false), m_use_write(false),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment