408 lines
10 KiB
C++
408 lines
10 KiB
C++
/* Copyright (C) 1979-1996 TcX AB & Monty Program KB & Detron HB
|
|
|
|
This software is distributed with NO WARRANTY OF ANY KIND. No author or
|
|
distributor accepts any responsibility for the consequences of using it, or
|
|
for whether it serves any particular purpose or works at all, unless he or
|
|
she says so in writing. Refer to the Free Public License (the "License")
|
|
for full details.
|
|
|
|
Every copy of this file must include a copy of the License, normally in a
|
|
plain ASCII text file named PUBLIC. The License grants you the right to
|
|
copy, modify and redistribute this file, but only under certain conditions
|
|
described in the License. Among other things, the License requires that
|
|
the copyright notice and this notice be preserved on all copies. */
|
|
|
|
/* Insert of records */
|
|
|
|
#include "mysql_priv.h"
|
|
#include "sql_acl.h"
|
|
|
|
static int check_null_fields(THD *thd,TABLE *entry);
|
|
|
|
/*
|
|
Check if insert fields are correct
|
|
Resets form->time_stamp if a timestamp value is set
|
|
*/
|
|
|
|
static int
|
|
check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
|
|
List<Item> &values, ulong counter)
|
|
{
|
|
if ((fields.elements == 0))
|
|
{
|
|
if (values.elements != table->fields)
|
|
{
|
|
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
|
|
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
|
MYF(0),counter);
|
|
return -1;
|
|
}
|
|
if (grant_option &&
|
|
check_grant_all_columns(thd,INSERT_ACL,table))
|
|
return -1;
|
|
table->time_stamp=0; // This should be saved
|
|
}
|
|
else
|
|
{ // Part field list
|
|
if (fields.elements != values.elements)
|
|
{
|
|
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
|
|
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
|
MYF(0),counter);
|
|
return -1;
|
|
}
|
|
TABLE_LIST table_list;
|
|
bzero((char*) &table_list,sizeof(table_list));
|
|
table_list.name=table->table_name;
|
|
table_list.table=table;
|
|
table_list.grant=table->grant;
|
|
|
|
thd->dupp_field=0;
|
|
if (setup_fields(thd,&table_list,fields,1,0))
|
|
return -1;
|
|
if (thd->dupp_field)
|
|
{
|
|
my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name);
|
|
return -1;
|
|
}
|
|
if (table->timestamp_field && // Don't set timestamp if used
|
|
table->timestamp_field->query_id == thd->query_id)
|
|
table->time_stamp=0; // This should be saved
|
|
}
|
|
// For the values we need select_priv
|
|
table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields,
|
|
List<List_item> &values_list,enum_duplicates dup,
|
|
bool low_priority)
|
|
{
|
|
int error;
|
|
uint value_count;
|
|
ulong counter = 1;
|
|
COPY_INFO info;
|
|
ulonglong id;
|
|
TABLE *table;
|
|
uint save_time_stamp;
|
|
List_iterator<List_item> its(values_list);
|
|
List_item *values;
|
|
DBUG_ENTER("mysql_insert");
|
|
|
|
if (!(table = open_ltable(thd,table_list,
|
|
low_priority ? TL_WRITE_DELAYED : TL_WRITE)))
|
|
DBUG_RETURN(-1);
|
|
thd->proc_info="init";
|
|
save_time_stamp=table->time_stamp;
|
|
values= its++;
|
|
if (check_insert_fields(thd,table,fields,*values,1) ||
|
|
setup_fields(thd,table_list,*values,0,0))
|
|
{
|
|
table->time_stamp=save_time_stamp;
|
|
DBUG_RETURN(-1);
|
|
}
|
|
value_count= values->elements;
|
|
while (values = its++)
|
|
{
|
|
counter++;
|
|
if (values->elements != value_count)
|
|
{
|
|
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
|
|
ER(ER_WRONG_VALUE_COUNT_ON_ROW),
|
|
MYF(0),counter);
|
|
table->time_stamp=save_time_stamp;
|
|
DBUG_RETURN(-1);
|
|
}
|
|
if (setup_fields(thd,table_list,*values,0,0))
|
|
{
|
|
table->time_stamp=save_time_stamp;
|
|
DBUG_RETURN(-1);
|
|
}
|
|
}
|
|
its.rewind ();
|
|
/*
|
|
** Fill in the given fields and dump it to the table file
|
|
*/
|
|
|
|
info.records=info.deleted=info.copied=0;
|
|
info.handle_duplicates=dup;
|
|
// Don't count warnings for simple inserts
|
|
if (values_list.elements > 1 || (thd->options & OPTION_WARNINGS))
|
|
thd->count_cuted_fields = 1;
|
|
thd->cuted_fields = 0L;
|
|
table->next_number_field=table->found_next_number_field;
|
|
|
|
error=0;
|
|
id=0;
|
|
thd->proc_info="update";
|
|
while (values = its++)
|
|
{
|
|
if (fields.elements)
|
|
{
|
|
restore_record(table,2); // Get empty record
|
|
if (fill_record(fields,*values) || check_null_fields(thd,table))
|
|
{
|
|
if (values_list.elements != 1)
|
|
{
|
|
info.records++;
|
|
continue;
|
|
}
|
|
error=1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
table->record[0][0]=table->record[2][0]; // Fix delete marker
|
|
if (fill_record(table,*values))
|
|
{
|
|
if (values_list.elements != 1)
|
|
{
|
|
info.records++;
|
|
continue;
|
|
}
|
|
error=1;
|
|
break;
|
|
}
|
|
}
|
|
if ((error=write_record(thd,table,&info)))
|
|
break;
|
|
/*
|
|
If auto_increment values are used, save the first one
|
|
for LAST_INSERT_ID() and for the update log.
|
|
*/
|
|
if (! id && thd->insert_id_used)
|
|
{ // Get auto increment value
|
|
id= thd->insert_id();
|
|
}
|
|
}
|
|
thd->proc_info="end";
|
|
if (id && values_list.elements != 1)
|
|
thd->insert_id(id); // For update log
|
|
else if (table->next_number_field)
|
|
id=table->next_number_field->val_int(); // Return auto_increment value
|
|
if (info.copied || info.deleted)
|
|
mysql_update_log.write(thd->query);
|
|
|
|
table->time_stamp=save_time_stamp; // Restore auto timestamp pointer
|
|
table->next_number_field=0;
|
|
thd->count_cuted_fields=0;
|
|
thd->next_insert_id=0; // Reset this if wrongly used
|
|
if (thd->lock)
|
|
{
|
|
mysql_unlock_tables(thd->lock);
|
|
thd->lock=0;
|
|
}
|
|
if (error)
|
|
DBUG_RETURN(-1);
|
|
|
|
if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) ||
|
|
!thd->cuted_fields))
|
|
send_ok(&thd->net,info.copied+info.deleted,id);
|
|
else {
|
|
char buff[160];
|
|
if (dup == DUP_IGNORE)
|
|
sprintf(buff,ER(ER_INSERT_INFO),info.records,info.records-info.copied,
|
|
thd->cuted_fields);
|
|
else
|
|
sprintf(buff,ER(ER_INSERT_INFO),info.records,info.deleted,
|
|
thd->cuted_fields);
|
|
::send_ok(&thd->net,info.copied+info.deleted,0L,buff);
|
|
}
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
|
|
/* Check if there is more uniq keys after field */
|
|
|
|
static int last_uniq_key(TABLE *table,uint keynr)
|
|
{
|
|
while (++keynr < table->keys)
|
|
if (!table->key_info[keynr].dupp_key)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Write a record to table with optional deleting of conflicting records
|
|
*/
|
|
|
|
|
|
int write_record(THD *thd,TABLE *table,COPY_INFO *info)
|
|
{
|
|
int error,key_nr;
|
|
char *key=0;
|
|
|
|
info->records++;
|
|
if (info->handle_duplicates == DUP_REPLACE)
|
|
{
|
|
while ((error=ha_write(table,table->record[0])))
|
|
{
|
|
if (error != HA_WRITE_SKIPP)
|
|
goto err;
|
|
if ((key_nr = ha_keyerror(table,error)) < 0)
|
|
{
|
|
error=HA_WRITE_SKIPP; /* Database can't find key */
|
|
goto err;
|
|
}
|
|
if (ha_extra(table,HA_EXTRA_FLUSH_CACHE)) /* Not neaded with NISAM */
|
|
{
|
|
error=my_errno;
|
|
goto err;
|
|
}
|
|
if (!key)
|
|
key=(char*) my_alloca(ha_max_key_length[table->db_type]);
|
|
key_copy((byte*) key,table,(uint) key_nr,0);
|
|
if ((error=ha_rkey(table,table->record[1],key_nr,(byte*) key,0,
|
|
HA_READ_KEY_EXACT)))
|
|
goto err;
|
|
if (last_uniq_key(table,key_nr))
|
|
{
|
|
if ((error=ha_update(table,table->record[1],table->record[0])))
|
|
goto err;
|
|
info->deleted++;
|
|
break; /* Update logfile and count */
|
|
}
|
|
else if ((error=ha_delete(table,table->record[1])))
|
|
goto err;
|
|
info->deleted++;
|
|
}
|
|
info->copied++;
|
|
}
|
|
else if ((error=ha_write(table,table->record[0])))
|
|
{
|
|
if (info->handle_duplicates != DUP_IGNORE ||
|
|
error != HA_ERR_FOUND_DUPP_KEY)
|
|
goto err;
|
|
}
|
|
else
|
|
info->copied++;
|
|
if (key)
|
|
my_afree(key);
|
|
return 0;
|
|
|
|
err:
|
|
my_afree(key);
|
|
ha_error(table,error,MYF(0));
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Check that all fields with arn't null_fields are used
|
|
if DONT_USE_DEFAULT_FIELDS isn't defined use default value for not
|
|
set fields.
|
|
******************************************************************************/
|
|
|
|
static int check_null_fields(THD *thd __attribute__((unused)),
|
|
TABLE *entry __attribute__((unused)))
|
|
{
|
|
#ifdef DONT_USE_DEFAULT_FIELDS
|
|
for (Field **field=entry->field ; *field ; field++)
|
|
{
|
|
if ((*field)->query_id != thd->query_id && !(*field)->maybe_null() &&
|
|
*field != entry->timestamp_field &&
|
|
*field != entry->next_number_field)
|
|
{
|
|
my_printf_error(ER_BAD_NULL_ERROR, ER(ER_BAD_NULL_ERROR),MYF(0),
|
|
(*field)->field_name);
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
** store records in INSERT ... SELECT *
|
|
***************************************************************************/
|
|
|
|
int
|
|
select_insert::prepare(List<Item> &values)
|
|
{
|
|
TABLE *form=table;
|
|
DBUG_ENTER("select_insert::prepare");
|
|
|
|
if (check_insert_fields(thd,table,*fields,values,1))
|
|
DBUG_RETURN(1);
|
|
|
|
if (fields->elements)
|
|
{
|
|
restore_record(form,2); // Get empty record
|
|
}
|
|
else
|
|
form->record[0][0]=form->record[2][0]; // Fix delete marker
|
|
form->next_number_field=form->found_next_number_field;
|
|
thd->count_cuted_fields=1; /* calc cuted fields */
|
|
thd->cuted_fields=0;
|
|
ha_extra(form,HA_EXTRA_WRITE_CACHE);
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
select_insert::~select_insert()
|
|
{
|
|
table->time_stamp=save_time_stamp;
|
|
table->next_number_field=0;
|
|
thd->count_cuted_fields=0;
|
|
}
|
|
|
|
|
|
bool select_insert::send_data(List<Item> &values)
|
|
{
|
|
if (thd->offset_limit)
|
|
{ // using limit offset,count
|
|
thd->offset_limit--;
|
|
return 0;
|
|
}
|
|
if (fields->elements)
|
|
fill_record(*fields,values);
|
|
else
|
|
fill_record(table,values);
|
|
if (write_record(thd,table,&info))
|
|
return 1;
|
|
if (table->next_number_field) // Clear for next record
|
|
table->next_number_field->reset();
|
|
return 0;
|
|
}
|
|
|
|
|
|
void select_insert::send_error(uint errcode,char *err)
|
|
{
|
|
::send_error(&thd->net,errcode,err);
|
|
VOID(ha_extra(table,HA_EXTRA_NO_CACHE));
|
|
}
|
|
|
|
|
|
void select_insert::send_eof()
|
|
{
|
|
int error;
|
|
if ((error=ha_extra(table,HA_EXTRA_NO_CACHE)))
|
|
{
|
|
ha_error(table,error,MYF(0));
|
|
::send_error(&thd->net);
|
|
}
|
|
else
|
|
{
|
|
char buff[160];
|
|
if (info.handle_duplicates == DUP_IGNORE)
|
|
sprintf(buff,ER(ER_INSERT_INFO),info.records,info.records-info.copied,
|
|
thd->cuted_fields);
|
|
else
|
|
sprintf(buff,ER(ER_INSERT_INFO),info.records,info.deleted,
|
|
thd->cuted_fields);
|
|
::send_ok(&thd->net,info.copied,0L,buff);
|
|
mysql_update_log.write(thd->query);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
** Instansiate templates
|
|
*****************************************************************************/
|
|
|
|
#ifdef __GNUC__
|
|
template class List_iterator<List_item>;
|
|
#endif
|