diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2012-08-31 10:49:36 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2012-08-31 10:49:36 +0400 |
commit | 6cce520472989216ac57349f1ad6d4b2afe03c91 (patch) | |
tree | 84e2d4ec4557d4c6f7971c3cfd40090d3895b43c | |
parent | 29e9406a82a00e6ec81a65e0254d9527ba25ba20 (diff) | |
download | mariadb-git-6cce520472989216ac57349f1ad6d4b2afe03c91.tar.gz |
Cassandra SE
- add support for Cassandra's UUID datatype. We map it to CHAR(36).
-rw-r--r-- | mysql-test/r/cassandra.result | 34 | ||||
-rw-r--r-- | mysql-test/t/cassandra.test | 49 | ||||
-rw-r--r-- | storage/cassandra/cassandra_se.cc | 3 | ||||
-rw-r--r-- | storage/cassandra/ha_cassandra.cc | 150 | ||||
-rw-r--r-- | storage/cassandra/ha_cassandra.h | 3 |
5 files changed, 222 insertions, 17 deletions
diff --git a/mysql-test/r/cassandra.result b/mysql-test/r/cassandra.result index 8f4b261b5ae..a340ae75f62 100644 --- a/mysql-test/r/cassandra.result +++ b/mysql-test/r/cassandra.result @@ -226,3 +226,37 @@ inserts insert_batches delete from t2; drop table t2; drop table t0; +# +# UUID datatype support +# +CREATE TABLE t2 (rowkey bigint PRIMARY KEY, uuidcol char(36)) ENGINE=CASSANDRA +thrift_host='localhost' keyspace='mariadbtest2' column_family = 'cf5'; +delete from t2; +insert into t2 values(1,'9b5658dc-f32f-11e1-94cd-f46d046e9f09'); +insert into t2 values(2,'not-an-uuid'); +ERROR 22003: Out of range value for column 'uuidcol' at row 1 +insert into t2 values(3,'9b5658dc-f32f-11e1=94cd-f46d046e9f09'); +ERROR 22003: Out of range value for column 'uuidcol' at row 1 +insert into t2 values(4,'9b5658dc-fzzf-11e1-94cd-f46d046e9f09'); +ERROR 22003: Out of range value for column 'uuidcol' at row 1 +insert into t2 values +(5,'9b5658dc-f11f-11e1-94cd-f46d046e9f09'), +(6,'9b5658dc-f11f011e1-94cd-f46d046e9f09'); +ERROR 22003: Out of range value for column 'uuidcol' at row 2 +select * from t2; +rowkey uuidcol +1 9b5658dc-f32f-11e1-94cd-f46d046e9f09 +5 9b5658dc-f11f-11e1-94cd-f46d046e9f09 +delete from t2; +drop table t2; +CREATE TABLE t2 (rowkey char(36) PRIMARY KEY, col1 int) ENGINE=CASSANDRA +thrift_host='localhost' keyspace='mariadbtest2' column_family = 'cf6'; +delete from t2; +insert into t2 values('9b5658dc-f32f-11e1-94cd-f46d046e9f09', 1234); +insert into t2 values('not-an-uuid', 563); +ERROR 22003: Out of range value for column 'rowkey' at row 1 +select * from t2; +rowkey col1 +9b5658dc-f32f-11e1-94cd-f46d046e9f09 1234 +delete from t2; +drop table t2; diff --git a/mysql-test/t/cassandra.test b/mysql-test/t/cassandra.test index 9abe9448c45..365cb5f8230 100644 --- a/mysql-test/t/cassandra.test +++ b/mysql-test/t/cassandra.test @@ -49,6 +49,10 @@ create columnfamily cf3 (rowkey bigint primary key, intcol int); create columnfamily cf4 (rowkey bigint primary key, datecol timestamp); +create columnfamily cf5 (rowkey bigint primary key, uuidcol uuid); + +create columnfamily cf6 (rowkey uuid primary key, col1 int); + ./cassandra-cli CREATE COLUMN FAMILY cf10 @@ -277,12 +281,57 @@ delete from t2; drop table t2; drop table t0; +--echo # +--echo # UUID datatype support +--echo # +#create columnfamily cf5 (rowkey bigint primary key, uuidcol uuid); +CREATE TABLE t2 (rowkey bigint PRIMARY KEY, uuidcol char(36)) ENGINE=CASSANDRA + thrift_host='localhost' keyspace='mariadbtest2' column_family = 'cf5'; +delete from t2; + +insert into t2 values(1,'9b5658dc-f32f-11e1-94cd-f46d046e9f09'); + +--error ER_WARN_DATA_OUT_OF_RANGE +insert into t2 values(2,'not-an-uuid'); + +--error ER_WARN_DATA_OUT_OF_RANGE +insert into t2 values(3,'9b5658dc-f32f-11e1=94cd-f46d046e9f09'); + +--error ER_WARN_DATA_OUT_OF_RANGE +insert into t2 values(4,'9b5658dc-fzzf-11e1-94cd-f46d046e9f09'); + +--error ER_WARN_DATA_OUT_OF_RANGE +insert into t2 values + (5,'9b5658dc-f11f-11e1-94cd-f46d046e9f09'), + (6,'9b5658dc-f11f011e1-94cd-f46d046e9f09'); + +select * from t2; + +delete from t2; +drop table t2; + +# create columnfamily cf6 (rowkey uuid primary key, col1 int); +CREATE TABLE t2 (rowkey char(36) PRIMARY KEY, col1 int) ENGINE=CASSANDRA + thrift_host='localhost' keyspace='mariadbtest2' column_family = 'cf6'; +delete from t2; + +insert into t2 values('9b5658dc-f32f-11e1-94cd-f46d046e9f09', 1234); + +--error ER_WARN_DATA_OUT_OF_RANGE +insert into t2 values('not-an-uuid', 563); + +select * from t2; +delete from t2; +drop table t2; + ############################################################################ ## Cassandra cleanup ############################################################################ --disable_parsing drop columnfamily cf1; drop columnfamily cf2; +drop columnfamily cf3; +drop columnfamily cf4; --enable_parsing ############################################################################ ## Cassandra cleanup ends diff --git a/storage/cassandra/cassandra_se.cc b/storage/cassandra/cassandra_se.cc index 670c13c2e91..692bfb595da 100644 --- a/storage/cassandra/cassandra_se.cc +++ b/storage/cassandra/cassandra_se.cc @@ -416,9 +416,8 @@ bool Cassandra_se_impl::get_range_slices(bool last_key_as_start_key) cparent.column_family= column_family; /* SlicePredicate can be used to limit columns we will retrieve */ - // Try passing nothing... - KeyRange key_range; // Try passing nothing, too. + KeyRange key_range; key_range.__isset.start_key= true; key_range.__isset.end_key= true; diff --git a/storage/cassandra/ha_cassandra.cc b/storage/cassandra/ha_cassandra.cc index fcaf4bd0fc1..3be416e99cc 100644 --- a/storage/cassandra/ha_cassandra.cc +++ b/storage/cassandra/ha_cassandra.cc @@ -302,6 +302,7 @@ int ha_cassandra::open(const char *name, int mode, uint test_if_locked) } info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + insert_lineno= 0; DBUG_RETURN(0); } @@ -407,6 +408,7 @@ int ha_cassandra::create(const char *name, TABLE *table_arg, my_error(ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), "setup_field_converters"); DBUG_RETURN(HA_ERR_NO_CONNECTION); } + insert_lineno= 0; DBUG_RETURN(0); } @@ -430,8 +432,13 @@ public: /* This will get data from the Field pointer, store Cassandra's form in internal buffer, and return pointer/size. + + @return + false - OK + true - Failed to convert value (completely, there is no value to insert + at all). */ - virtual void mariadb_to_cassandra(char **cass_data, int *cass_data_len)=0; + virtual bool mariadb_to_cassandra(char **cass_data, int *cass_data_len)=0; virtual ~ColumnDataConverter() {}; }; @@ -447,11 +454,12 @@ public: field->store(*pdata); } - void mariadb_to_cassandra(char **cass_data, int *cass_data_len) + bool mariadb_to_cassandra(char **cass_data, int *cass_data_len) { buf= field->val_real(); *cass_data= (char*)&buf; *cass_data_len=sizeof(double); + return false; } ~DoubleDataConverter(){} }; @@ -468,11 +476,12 @@ public: field->store(*pdata); } - void mariadb_to_cassandra(char **cass_data, int *cass_data_len) + bool mariadb_to_cassandra(char **cass_data, int *cass_data_len) { buf= field->val_real(); *cass_data= (char*)&buf; *cass_data_len=sizeof(float); + return false; } ~FloatDataConverter(){} }; @@ -501,12 +510,13 @@ public: field->store(tmp); } - void mariadb_to_cassandra(char **cass_data, int *cass_data_len) + bool mariadb_to_cassandra(char **cass_data, int *cass_data_len) { longlong tmp= field->val_int(); flip64((const char*)&tmp, (char*)&buf); *cass_data= (char*)&buf; *cass_data_len=sizeof(longlong); + return false; } ~BigintDataConverter(){} }; @@ -531,12 +541,13 @@ public: field->store(tmp); } - void mariadb_to_cassandra(char **cass_data, int *cass_data_len) + bool mariadb_to_cassandra(char **cass_data, int *cass_data_len) { int32_t tmp= field->val_int(); flip32((const char*)&tmp, (char*)&buf); *cass_data= (char*)&buf; *cass_data_len=sizeof(int32_t); + return false; } ~Int32DataConverter(){} }; @@ -551,11 +562,12 @@ public: field->store(cass_data, cass_data_len,field->charset()); } - void mariadb_to_cassandra(char **cass_data, int *cass_data_len) + bool mariadb_to_cassandra(char **cass_data, int *cass_data_len) { String *pstr= field->val_str(&buf); *cass_data= (char*)pstr->c_ptr(); *cass_data_len= pstr->length(); + return false; } ~StringCopyConverter(){} }; @@ -579,7 +591,7 @@ public: ((Field_timestamp*)field)->store_TIME(tmp / 1000, (tmp % 1000)*1000); } - void mariadb_to_cassandra(char **cass_data, int *cass_data_len) + bool mariadb_to_cassandra(char **cass_data, int *cass_data_len) { my_time_t ts_time; ulong ts_microsec; @@ -592,10 +604,85 @@ public: *cass_data= (char*)&buf; *cass_data_len= 8; + return false; } ~TimestampDataConverter(){} }; + + +static int convert_hex_digit(const char c) +{ + int num; + if (c >= '0' && c <= '9') + num= c - '0'; + else if (c >= 'A' && c <= 'F') + num= c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + num= c - 'a' + 10; + else + return -1; /* Couldn't convert */ + return num; +} + + +const char map2number[]="0123456789abcdef"; + +class UuidDataConverter : public ColumnDataConverter +{ + char buf[16]; /* Binary UUID representation */ + String str_buf; +public: + void cassandra_to_mariadb(const char *cass_data, int cass_data_len) + { + DBUG_ASSERT(cass_data_len==16); + char str[37]; + char *ptr= str; + /* UUID arrives as 16-byte number in network byte order */ + for (uint i=0; i < 16; i++) + { + *(ptr++)= map2number[(cass_data[i] >> 4) & 0xF]; + *(ptr++)= map2number[cass_data[i] & 0xF]; + if (i == 3 || i == 5 || i == 7 || i == 9) + *(ptr++)= '-'; + } + *ptr= 0; + field->store(str, 36,field->charset()); + } + + bool mariadb_to_cassandra(char **cass_data, int *cass_data_len) + { + String *uuid_str= field->val_str(&str_buf); + char *pstr= (char*)uuid_str->c_ptr(); + + if (uuid_str->length() != 36) + return true; + + int lower, upper; + for (uint i=0; i < 16; i++) + { + if ((upper= convert_hex_digit(pstr[0])) == -1 || + (lower= convert_hex_digit(pstr[1])) == -1) + { + return true; + } + buf[i]= lower | (upper << 4); + pstr += 2; + if (i == 3 || i == 5 || i == 7 || i == 9) + { + if (pstr[0] != '-') + return true; + pstr++; + } + } + + *cass_data= buf; + *cass_data_len= 16; + return false; + } + ~UuidDataConverter(){} +}; + const char * const validator_bigint= "org.apache.cassandra.db.marshal.LongType"; const char * const validator_int= "org.apache.cassandra.db.marshal.Int32Type"; const char * const validator_counter= "org.apache.cassandra.db.marshal.CounterColumnType"; @@ -609,6 +696,8 @@ const char * const validator_text= "org.apache.cassandra.db.marshal.UTF8Type" const char * const validator_timestamp="org.apache.cassandra.db.marshal.DateType"; +const char * const validator_uuid= "org.apache.cassandra.db.marshal.UUIDType"; + ColumnDataConverter *map_field_to_validator(Field *field, const char *validator_name) { ColumnDataConverter *res= NULL; @@ -617,8 +706,7 @@ ColumnDataConverter *map_field_to_validator(Field *field, const char *validator_ case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONGLONG: - if (!strcmp(validator_name, validator_bigint) || - 0/*!strcmp(validator_name, validator_timestamp)*/) + if (!strcmp(validator_name, validator_bigint)) res= new BigintDataConverter; break; @@ -637,9 +725,18 @@ ColumnDataConverter *map_field_to_validator(Field *field, const char *validator_ res= new TimestampDataConverter; break; - case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: // these are space padded CHAR(n) strings. + if (!strcmp(validator_name, validator_uuid) && + field->real_type() == MYSQL_TYPE_STRING && + field->field_length == 36) + { + // UUID maps to CHAR(36), its text representation + res= new UuidDataConverter; + break; + } + /* fall through: */ case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_STRING: // these are space padded strings. + case MYSQL_TYPE_VAR_STRING: if (!strcmp(validator_name, validator_blob) || !strcmp(validator_name, validator_ascii) || !strcmp(validator_name, validator_text)) @@ -698,7 +795,11 @@ bool ha_cassandra::setup_field_converters(Field **field_arg, uint n_fields) } if (n_mapped != n_fields - 1) + { + se->print_error("Some of SQL fields were not mapped to Cassandra's fields"); + my_error(ER_INTERNAL_ERROR, MYF(0), se->error_str()); return true; + } /* Setup type conversion for row_key. @@ -775,7 +876,11 @@ int ha_cassandra::index_read_map(uchar *buf, const uchar *key, old_map= dbug_tmp_use_all_columns(table, table->read_set); - rowkey_converter->mariadb_to_cassandra(&cass_key, &cass_key_len); + if (rowkey_converter->mariadb_to_cassandra(&cass_key, &cass_key_len)) + { + /* We get here when making lookups like uuid_column='not-an-uuid' */ + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } dbug_tmp_restore_column_map(table->read_set, old_map); @@ -858,10 +963,18 @@ int ha_cassandra::write_row(uchar *buf) old_map= dbug_tmp_use_all_columns(table, table->read_set); + insert_lineno++; + /* Convert the key */ char *cass_key; int cass_key_len; - rowkey_converter->mariadb_to_cassandra(&cass_key, &cass_key_len); + if (rowkey_converter->mariadb_to_cassandra(&cass_key, &cass_key_len)) + { + my_error(ER_WARN_DATA_OUT_OF_RANGE, MYF(0), + rowkey_converter->field->field_name, insert_lineno); + dbug_tmp_restore_column_map(table->read_set, old_map); + DBUG_RETURN(HA_ERR_AUTOINC_ERANGE); + } se->start_row_insert(cass_key, cass_key_len); /* Convert other fields */ @@ -869,7 +982,13 @@ int ha_cassandra::write_row(uchar *buf) { char *cass_data; int cass_data_len; - field_converters[i]->mariadb_to_cassandra(&cass_data, &cass_data_len); + if (field_converters[i]->mariadb_to_cassandra(&cass_data, &cass_data_len)) + { + my_error(ER_WARN_DATA_OUT_OF_RANGE, MYF(0), + field_converters[i]->field->field_name, insert_lineno); + dbug_tmp_restore_column_map(table->read_set, old_map); + DBUG_RETURN(HA_ERR_AUTOINC_ERANGE); + } se->add_insert_column(field_converters[i]->field->field_name, cass_data, cass_data_len); } @@ -892,7 +1011,7 @@ int ha_cassandra::write_row(uchar *buf) if (res) my_error(ER_INTERNAL_ERROR, MYF(0), se->error_str()); - + DBUG_RETURN(res? HA_ERR_INTERNAL_ERROR: 0); } @@ -1060,6 +1179,7 @@ int ha_cassandra::rnd_pos(uchar *buf, uchar *pos) int ha_cassandra::reset() { doing_insert_batch= false; + insert_lineno= 0; return 0; } diff --git a/storage/cassandra/ha_cassandra.h b/storage/cassandra/ha_cassandra.h index d5b542aae06..948bd8a6687 100644 --- a/storage/cassandra/ha_cassandra.h +++ b/storage/cassandra/ha_cassandra.h @@ -48,6 +48,9 @@ class ha_cassandra: public handler bool doing_insert_batch; ha_rows insert_rows_batched; + + /* Used to produce 'wrong column %s at row %lu' warnings */ + ha_rows insert_lineno; public: ha_cassandra(handlerton *hton, TABLE_SHARE *table_arg); ~ha_cassandra() |