@@ -400,7 +400,7 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file,
400400 num_parts= 2 ;
401401 use_default_num_partitions= false ;
402402 }
403- else if (part_type != HASH_PARTITION)
403+ else if (part_type != HASH_PARTITION && ! is_range_interval () )
404404 {
405405 const char *error_string;
406406 if (part_type == RANGE_PARTITION)
@@ -891,6 +891,39 @@ bool partition_info::vers_set_hist_part(THD *thd, uint *create_count)
891891 return false ;
892892}
893893
894+ /*
895+ Determine the number of range interval partitions to create, like
896+ partition_info::vers_set_hist_part.
897+
898+ TODO(MDEV-15621): consider making this more efficient by using a
899+ hash, possibly like Partition_share::partition_name_hash
900+ */
901+ bool partition_info::range_interval_set_count (THD* thd, uint *create_count)
902+ {
903+ /* At least one range partition is defined */
904+ DBUG_ASSERT (partitions.elements > 0 );
905+ partition_element *el= partitions.elem (partitions.elements - 1 );
906+ Item *item= el->get_col_val (0 ).item_expression ;
907+ MYSQL_TIME cur_time, end_time;
908+ thd->variables .time_zone ->gmt_sec_to_TIME (&end_time, thd->query_start ());
909+ longlong cur= item->val_datetime_packed (thd);
910+ unpack_time (cur, &cur_time, MYSQL_TIMESTAMP_DATETIME);
911+ longlong end= pack_time (&end_time);
912+ *create_count= 0 ;
913+ while (cur <= end)
914+ {
915+ if (date_add_interval (thd, &cur_time, int_type, interval))
916+ return true ;
917+ cur= pack_time (&cur_time);
918+ ++*create_count;
919+ if (partitions.elements + *create_count > MAX_PARTITIONS)
920+ {
921+ my_error (ER_TOO_MANY_PARTITIONS_ERROR, MYF (0 ));
922+ return true ;
923+ }
924+ }
925+ return false ;
926+ }
894927
895928/* *
896929 @brief Run fast_alter_partition_table() to add new history partitions
@@ -1014,6 +1047,103 @@ bool vers_create_partitions(THD *thd, TABLE_LIST* tl, uint num_parts)
10141047 return result;
10151048}
10161049
1050+ /*
1051+ similar to vers_create_partitions, create range interval partitions
1052+ */
1053+ bool range_interval_create_partitions (THD* thd, TABLE_LIST* tl, uint num_parts)
1054+ {
1055+ bool result= true ;
1056+ Table_specification_st create_info;
1057+ Alter_info alter_info;
1058+ TABLE *table= tl->table ;
1059+ partition_info *save_part_info= thd->work_part_info ;
1060+ /* TODO: this may still trigger MSAN unitialised? */
1061+ bool save_no_write_to_binlog= thd->lex ->no_write_to_binlog ;
1062+ thd->lex ->no_write_to_binlog = true ;
1063+
1064+ DBUG_ASSERT (!thd->is_error ());
1065+ DBUG_ASSERT (num_parts);
1066+
1067+ {
1068+ alter_info.reset ();
1069+ alter_info.partition_flags = ALTER_PARTITION_ADD;
1070+ create_info.init ();
1071+ create_info.alter_info = &alter_info;
1072+ Alter_table_ctx alter_ctx (thd, tl, 1 , &table->s ->db , &table->s ->table_name );
1073+
1074+ MDL_REQUEST_INIT (&tl->mdl_request , MDL_key::TABLE, tl->db .str ,
1075+ tl->table_name .str , MDL_SHARED_NO_WRITE, MDL_TRANSACTION);
1076+ if (thd->mdl_context .acquire_lock (&tl->mdl_request ,
1077+ thd->variables .lock_wait_timeout ))
1078+ goto exit;
1079+ table->mdl_ticket = tl->mdl_request .ticket ;
1080+
1081+ create_info.db_type = table->s ->db_type ();
1082+ DBUG_ASSERT (create_info.db_type );
1083+
1084+ partition_info *part_info= new partition_info ();
1085+ if (unlikely (!part_info))
1086+ {
1087+ my_error (ER_OUT_OF_RESOURCES, MYF (0 ));
1088+ goto exit;
1089+ }
1090+ part_info->use_default_num_partitions = false ;
1091+ part_info->use_default_num_subpartitions = false ;
1092+ part_info->num_parts = num_parts;
1093+ part_info->num_subparts = table->part_info ->num_subparts ;
1094+ part_info->subpart_type = table->part_info ->subpart_type ;
1095+ part_info->num_columns = table->part_info ->num_columns ;
1096+ part_info->part_type = RANGE_PARTITION;
1097+ /* for partition_info::fix_parser_data to exit early */
1098+ part_info->int_type = table->part_info ->int_type ;
1099+
1100+ thd->work_part_info = part_info;
1101+ bool partition_changed= false ;
1102+ bool fast_alter_partition= false ;
1103+ if (prep_alter_part_table (thd, table, &alter_info, &create_info,
1104+ &partition_changed, &fast_alter_partition))
1105+ {
1106+ my_error (ER_INTERNAL_ERROR, MYF (ME_WARNING),
1107+ tl->db .str , tl->table_name .str );
1108+ goto exit;
1109+ }
1110+ if (!fast_alter_partition)
1111+ {
1112+ my_error (ER_INTERNAL_ERROR, MYF (ME_WARNING),
1113+ tl->db .str , tl->table_name .str );
1114+ goto exit;
1115+ }
1116+ DBUG_ASSERT (partition_changed);
1117+ if (mysql_prepare_alter_table (thd, table, &create_info, &alter_info,
1118+ &alter_ctx))
1119+ {
1120+ my_error (ER_INTERNAL_ERROR, MYF (ME_WARNING),
1121+ tl->db .str , tl->table_name .str );
1122+ goto exit;
1123+ }
1124+
1125+ alter_info.db = alter_ctx.db ;
1126+ alter_info.table_name = alter_ctx.table_name ;
1127+ if (fast_alter_partition_table (thd, table, &alter_info, &alter_ctx,
1128+ &create_info, tl))
1129+ {
1130+ my_error (ER_INTERNAL_ERROR, MYF (ME_WARNING),
1131+ tl->db .str , tl->table_name .str );
1132+ goto exit;
1133+ }
1134+ }
1135+
1136+ result= false ;
1137+ // NOTE: we have to return DA_EMPTY for new command
1138+ DBUG_ASSERT (thd->get_stmt_da ()->is_ok ());
1139+ thd->get_stmt_da ()->reset_diagnostics_area ();
1140+ thd->variables .option_bits |= OPTION_BINLOG_THIS;
1141+
1142+ exit:
1143+ thd->lex ->no_write_to_binlog = save_no_write_to_binlog;
1144+ thd->work_part_info = save_part_info;
1145+ return result;
1146+ }
10171147
10181148/* *
10191149 Warn at the end of DML command if the last history partition is out of LIMIT.
@@ -2320,6 +2450,7 @@ bool partition_info::fix_parser_data(THD *thd)
23202450 partition_element *part_elem;
23212451 uint num_elements;
23222452 uint i= 0 , j, k;
2453+ int sql_command= thd_sql_command (thd);
23232454 DBUG_ENTER (" partition_info::fix_parser_data" );
23242455
23252456 if (!(part_type == RANGE_PARTITION ||
@@ -2334,13 +2465,25 @@ bool partition_info::fix_parser_data(THD *thd)
23342465 DBUG_RETURN (true );
23352466 }
23362467 /* If not set, use DEFAULT = 2 for CREATE and ALTER! */
2337- if ((thd_sql_command (thd) == SQLCOM_CREATE_TABLE ||
2338- thd_sql_command (thd) == SQLCOM_ALTER_TABLE) &&
2468+ if ((sql_command == SQLCOM_CREATE_TABLE ||
2469+ sql_command == SQLCOM_ALTER_TABLE) &&
23392470 key_algorithm == KEY_ALGORITHM_NONE)
23402471 key_algorithm= KEY_ALGORITHM_55;
23412472 }
23422473 DBUG_RETURN (FALSE );
23432474 }
2475+ /*
2476+ TODO(MDEV-15621): we exit here for range interval partitions
2477+ because if this is called from prep_alter_part_table then
2478+ partition data is not calculated (in
2479+ check_range_interval_constants) yet. But maybe we shouldn't exit
2480+ here in user CREATE/ALTER TABLE statements for other checks.
2481+ */
2482+ if (sql_command != SQLCOM_CREATE_TABLE &&
2483+ sql_command != SQLCOM_ALTER_TABLE && is_range_interval ())
2484+ {
2485+ DBUG_RETURN (FALSE );
2486+ }
23442487 if (is_sub_partitioned () && list_of_subpart_fields)
23452488 {
23462489 /* KEY subpartitioning, check ALGORITHM = N. Should not pass the parser! */
@@ -2785,6 +2928,76 @@ bool partition_info::vers_init_info(THD * thd)
27852928 return false ;
27862929}
27872930
2931+ bool partition_info::set_interval (THD* thd, Item* ival, interval_type type,
2932+ const char *table_name)
2933+ {
2934+ bool error= get_interval_value (thd, ival, type, &interval) ||
2935+ interval.neg || interval.second_part ||
2936+ !(interval.year || interval.month || interval.day || interval.hour ||
2937+ interval.minute || interval.second );
2938+ if (error)
2939+ {
2940+ my_error (ER_PART_WRONG_VALUE, MYF (0 ), table_name, " INTERVAL" );
2941+ return true ;
2942+ }
2943+ int_type= type;
2944+ return false ;
2945+ }
2946+
2947+ bool partition_info::set_interval (int num, LEX_CSTRING &type, bool is_ds,
2948+ const char *table_name)
2949+ {
2950+ if (num < 0 )
2951+ goto end;
2952+ if (is_ds)
2953+ {
2954+ if (type.length == 3 && !strncasecmp (type.str , " DAY" , 3 ))
2955+ {
2956+ interval.day = num;
2957+ int_type= INTERVAL_DAY;
2958+ return false ;
2959+ }
2960+ else if (type.length == 4 && !strncasecmp (type.str , " HOUR" , 4 ))
2961+ {
2962+ interval.hour = num;
2963+ int_type= INTERVAL_HOUR;
2964+ return false ;
2965+ }
2966+ else if (type.length == 6 )
2967+ {
2968+ if (!strncasecmp (type.str , " MINUTE" , 6 ))
2969+ {
2970+ interval.minute = num;
2971+ int_type= INTERVAL_MINUTE;
2972+ return false ;
2973+ }
2974+ else if (!strncasecmp (type.str , " SECOND" , 6 ))
2975+ {
2976+ interval.second = num;
2977+ int_type= INTERVAL_SECOND;
2978+ return false ;
2979+ }
2980+ }
2981+ }
2982+ else
2983+ {
2984+ if (type.length == 4 && !strncasecmp (type.str , " YEAR" , 4 ))
2985+ {
2986+ interval.year = num;
2987+ int_type= INTERVAL_YEAR;
2988+ return false ;
2989+ }
2990+ else if (type.length == 5 && !strncasecmp (type.str , " MONTH" , 5 ))
2991+ {
2992+ interval.month = num;
2993+ int_type= INTERVAL_MONTH;
2994+ return false ;
2995+ }
2996+ }
2997+ end:
2998+ my_error (ER_PART_WRONG_VALUE, MYF (0 ), table_name, " INTERVAL" );
2999+ return true ;
3000+ }
27883001
27893002/* *
27903003 Assign INTERVAL and STARTS for SYSTEM_TIME partitions.
0 commit comments