Commit 741a1df4 authored by Alexander Barkov's avatar Alexander Barkov

WL#1397 convert XML -> SQL

parent 946dc457
drop table if exists t1, t2;
create table t1 (a int, b varchar(64));
-- Load a static XML file
load xml infile '../../std_data/loadxml.dat' into table t1
rows identified by '<row>';
select * from t1 order by a;
a b
1 b1
2 b2
3 b3
11 b11
111 b111
112 b112 & < > " ' &unknown; -- check entities
212 b212
213 b213
214 b214
215 b215
216 &bb b;
delete from t1;
-- Load a static XML file with 'IGNORE num ROWS'
load xml infile '../../std_data/loadxml.dat' into table t1
rows identified by '<row>' ignore 4 rows;
select * from t1 order by a;
a b
111 b111
112 b112 & < > " ' &unknown; -- check entities
212 b212
213 b213
214 b214
215 b215
216 &bb b;
-- Check 'mysqldump --xml' + 'LOAD XML' round trip
delete from t1;
load xml infile 'MYSQLTEST_VARDIR/tmp/loadxml-dump.xml' into table t1 rows identified by '<row>';;
select * from t1 order by a;
a b
111 b111
112 b112 & < > " ' &unknown; -- check entities
212 b212
213 b213
214 b214
215 b215
216 &bb b;
--Check that default row tag is '<row>
delete from t1;
load xml infile 'MYSQLTEST_VARDIR/tmp/loadxml-dump.xml' into table t1;;
select * from t1 order by a;
a b
111 b111
112 b112 & < > " ' &unknown; -- check entities
212 b212
213 b213
214 b214
215 b215
216 &bb b;
-- Check that 'xml' is not a keyword
select 1 as xml;
xml
1
create table t2(fl text);
LOAD XML LOCAL INFILE "$MYSQLTEST_VARDIR/tmp/loadxml-dump.xml" INTO TABLE t2 ROWS IDENTIFIED BY '<person>';;
show processlist;
Id User Host db Command Time State Info
2 root localhost test Query 0 NULL show processlist
5 root localhost test Query 3 Reading from net LOAD XML LOCAL INFILE "$MYSQLTEST_VARDIR/tmp/loadxml-dump.xml" INTO TABLE t2 ROWS IDENTIFIED BY '<p
drop table t1;
drop table t2;
create table t1 (
id int(11) not null,
text text,
primary key (id)
) engine=MyISAM default charset=latin1;
load xml infile '../../std_data/loadxml2.dat' into table t1;
select * from t1;
id text
1 line1
line2
line3
drop table t1;
<?xml version="1.0"?>
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<database name="test">
<table_structure name="t1">
<field Field="a" Type="int(11)" Null="YES" Key="" Extra="" />
<field Field="b" Type="varchar(128)" Null="YES" Key="" Extra="" />
<options Name="t1" Engine="MyISAM" Version="10" Row_format="Dynamic" Rows="3" Avg_row_length="20" Data_length="60" Max_data_length="281474976710655" Index_length="1024" Data_free="0" Create_time="2007-02-09 09:08:36" Update_time="2007-02-09 09:08:54" Collation="latin1_swedish_ci" Create_options="" Comment="" />
</table_structure>
<table_data name="t1">
<row>
<field name="a">1</field>
<field name="b">b1</field>
</row>
<row>
<field name="a">2</field>
<field name="b">b2</field>
</row>
<row>
<field name="a">3</field>
<field name="b">b3</field>
</row>
<row>
<field name="a">11</field>
<field name="b">b11</field>
</row>
<!-- Check field values as tags -->
<row>
<a>111</a>
<b>b111</b>
</row>
<row>
<a>112</a>
<b>b112 &amp; &lt; &gt; &quot; &apos; &unknown; -- check entities</b>
</row>
<!-- Check field values in attributes -->
<row a=212 b="b212"></row>
<!-- Bug#29752 Linefeeds break LOAD XML INFILE -->
<!-- Check varios combinations of TAB and NL -->
<row
a=213 b="b213">
</row>
<row
a=214
b="b214">
</row>
<row a=215 b="b215"></row>
<row a=216 b="&bb
b;"></row>
<!-- End of bug#29752 -->
</table_data>
</database>
</mysqldump>
<?xml version="1.0"?>
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<database name="test_of_xml_import">
<table_structure name="t1">
<field Field="id" Type="int(11)" Null="NO" Key="PRI" Extra="" />
<field Field="text" Type="text" Null="YES" Key="" Extra="" />
<key Table="t1" Non_unique="0" Key_name="PRIMARY" Seq_in_index="1" Column_name="id" Collation="A" Cardinality="1" Null="" Index_type="BTREE" Comment="" Index_Comment="" />
<options Name="t1" Engine="MyISAM" Version="10" Row_format="Dynamic" Rows="1" Avg_row_length="32" Data_length="32" Max_data_length="281474976710655" Index_length="2048" Data_free="0" Create_time="2009-06-18 10:02:37" Update_time="2009-06-18 10:02:43" Collation="latin1_swedish_ci" Create_options="" Comment="" />
</table_structure>
<table_data name="t1">
<row>
<field name="id">1</field>
<field name="text">line1
line2
line3</field>
</row>
</table_data>
</database>
</mysqldump>
#
# Tests for "LOAD XML" - a contributed patch from Erik Wetterberg.
#
# Running the $MYSQL_DUMP tool against an embedded server does not work.
--source include/not_embedded.inc
--disable_warnings
drop table if exists t1, t2;
--enable_warnings
create table t1 (a int, b varchar(64));
--echo -- Load a static XML file
load xml infile '../../std_data/loadxml.dat' into table t1
rows identified by '<row>';
select * from t1 order by a;
delete from t1;
--echo -- Load a static XML file with 'IGNORE num ROWS'
load xml infile '../../std_data/loadxml.dat' into table t1
rows identified by '<row>' ignore 4 rows;
select * from t1 order by a;
--echo -- Check 'mysqldump --xml' + 'LOAD XML' round trip
--exec $MYSQL_DUMP --xml test t1 > "$MYSQLTEST_VARDIR/tmp/loadxml-dump.xml" 2>&1
delete from t1;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval load xml infile '$MYSQLTEST_VARDIR/tmp/loadxml-dump.xml' into table t1 rows identified by '<row>';
select * from t1 order by a;
--echo --Check that default row tag is '<row>
delete from t1;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval load xml infile '$MYSQLTEST_VARDIR/tmp/loadxml-dump.xml' into table t1;
select * from t1 order by a;
--echo -- Check that 'xml' is not a keyword
select 1 as xml;
#
# Bug #42520 killing load .. infile Assertion failed: ! is_set(), file .\sql_error.cc, line 8
#
--disable_query_log
delete from t1;
insert into t1 values (1, '12345678900987654321'), (2, 'asdfghjkl;asdfghjkl;');
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
--exec $MYSQL_DUMP --xml test t1 > "$MYSQLTEST_VARDIR/tmp/loadxml-dump.xml" 2>&1
--enable_query_log
connect (addconroot, localhost, root,,);
connection addconroot;
create table t2(fl text);
--let $PSEUDO_THREAD_ID=`select @@pseudo_thread_id `
--send LOAD XML LOCAL INFILE "$MYSQLTEST_VARDIR/tmp/loadxml-dump.xml" INTO TABLE t2 ROWS IDENTIFIED BY '<person>';
sleep 3;
connection default;
show processlist;
--disable_query_log
--eval kill $PSEUDO_THREAD_ID
--enable_query_log
disconnect addconroot;
#
# Clean up
#
remove_file $MYSQLTEST_VARDIR/tmp/loadxml-dump.xml;
drop table t1;
drop table t2;
#
# Bug #36750 LOAD XML doesn't understand new line (feed) characters in multi line text fields
#
create table t1 (
id int(11) not null,
text text,
primary key (id)
) engine=MyISAM default charset=latin1;
load xml infile '../../std_data/loadxml2.dat' into table t1;
select * from t1;
drop table t1;
...@@ -607,6 +607,7 @@ static SYMBOL symbols[] = { ...@@ -607,6 +607,7 @@ static SYMBOL symbols[] = {
{ "X509", SYM(X509_SYM)}, { "X509", SYM(X509_SYM)},
{ "XOR", SYM(XOR)}, { "XOR", SYM(XOR)},
{ "XA", SYM(XA_SYM)}, { "XA", SYM(XA_SYM)},
{ "XML", SYM(XML_SYM)}, /* LOAD XML Arnold/Erik */
{ "YEAR", SYM(YEAR_SYM)}, { "YEAR", SYM(YEAR_SYM)},
{ "YEAR_MONTH", SYM(YEAR_MONTH_SYM)}, { "YEAR_MONTH", SYM(YEAR_MONTH_SYM)},
{ "ZEROFILL", SYM(ZEROFILL)}, { "ZEROFILL", SYM(ZEROFILL)},
......
...@@ -1602,13 +1602,17 @@ bool select_result::check_simple_select() const ...@@ -1602,13 +1602,17 @@ bool select_result::check_simple_select() const
static String default_line_term("\n",default_charset_info); static String default_line_term("\n",default_charset_info);
static String default_escaped("\\",default_charset_info); static String default_escaped("\\",default_charset_info);
static String default_field_term("\t",default_charset_info); static String default_field_term("\t",default_charset_info);
static String default_xml_row_term("<row>", default_charset_info);
sql_exchange::sql_exchange(char *name,bool flag) sql_exchange::sql_exchange(char *name, bool flag,
enum enum_filetype filetype_arg)
:file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0) :file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0)
{ {
filetype= filetype_arg;
field_term= &default_field_term; field_term= &default_field_term;
enclosed= line_start= &my_empty_string; enclosed= line_start= &my_empty_string;
line_term= &default_line_term; line_term= filetype == FILETYPE_CSV ?
&default_line_term : &default_xml_row_term;
escaped= &default_escaped; escaped= &default_escaped;
cs= NULL; cs= NULL;
} }
......
...@@ -89,6 +89,7 @@ enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT, ...@@ -89,6 +89,7 @@ enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
SLAVE_EXEC_MODE_LAST_BIT}; SLAVE_EXEC_MODE_LAST_BIT};
enum enum_mark_columns enum enum_mark_columns
{ MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE}; { MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE};
enum enum_filetype { FILETYPE_CSV, FILETYPE_XML };
extern char internal_table_name[2]; extern char internal_table_name[2];
extern char empty_c_string[1]; extern char empty_c_string[1];
...@@ -2364,13 +2365,15 @@ my_eof(THD *thd) ...@@ -2364,13 +2365,15 @@ my_eof(THD *thd)
class sql_exchange :public Sql_alloc class sql_exchange :public Sql_alloc
{ {
public: public:
enum enum_filetype filetype; /* load XML, Added by Arnold & Erik */
char *file_name; char *file_name;
String *field_term,*enclosed,*line_term,*line_start,*escaped; String *field_term,*enclosed,*line_term,*line_start,*escaped;
bool opt_enclosed; bool opt_enclosed;
bool dumpfile; bool dumpfile;
ulong skip_lines; ulong skip_lines;
CHARSET_INFO *cs; CHARSET_INFO *cs;
sql_exchange(char *name,bool dumpfile_flag); sql_exchange(char *name, bool dumpfile_flag,
enum_filetype filetype_arg= FILETYPE_CSV);
bool escaped_given(void); bool escaped_given(void);
}; };
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
/* Copy data from a textfile to table */ /* Copy data from a textfile to table */
/* 2006-12 Erik Wetterberg : LOAD XML added */
#include "mysql_priv.h" #include "mysql_priv.h"
#include <my_dir.h> #include <my_dir.h>
#include <m_ctype.h> #include <m_ctype.h>
...@@ -23,6 +25,23 @@ ...@@ -23,6 +25,23 @@
#include "sp_head.h" #include "sp_head.h"
#include "sql_trigger.h" #include "sql_trigger.h"
class XML_TAG {
public:
int level;
String field;
String value;
XML_TAG(int l, String f, String v);
};
XML_TAG::XML_TAG(int l, String f, String v)
{
level= l;
field.append(f);
value.append(v);
}
class READ_INFO { class READ_INFO {
File file; File file;
uchar *buffer, /* Buffer for read text */ uchar *buffer, /* Buffer for read text */
...@@ -37,6 +56,7 @@ class READ_INFO { ...@@ -37,6 +56,7 @@ class READ_INFO {
bool need_end_io_cache; bool need_end_io_cache;
IO_CACHE cache; IO_CACHE cache;
NET *io_net; NET *io_net;
int level; /* for load xml */
public: public:
bool error,line_cuted,found_null,enclosed; bool error,line_cuted,found_null,enclosed;
...@@ -54,6 +74,12 @@ public: ...@@ -54,6 +74,12 @@ public:
char unescape(char chr); char unescape(char chr);
int terminator(char *ptr,uint length); int terminator(char *ptr,uint length);
bool find_start_of_fields(); bool find_start_of_fields();
/* load xml */
List<XML_TAG> taglist;
int read_value(int delim, String *val);
int read_xml();
int clear_level(int level);
/* /*
We need to force cache close before destructor is invoked to log We need to force cache close before destructor is invoked to log
the last read block the last read block
...@@ -82,6 +108,13 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -82,6 +108,13 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
List<Item> &set_values, READ_INFO &read_info, List<Item> &set_values, READ_INFO &read_info,
String &enclosed, ulong skip_lines, String &enclosed, ulong skip_lines,
bool ignore_check_option_errors); bool ignore_check_option_errors);
static int read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
List<Item> &fields_vars, List<Item> &set_fields,
List<Item> &set_values, READ_INFO &read_info,
String &enclosed, ulong skip_lines,
bool ignore_check_option_errors);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
static bool write_execute_load_query_log_event(THD *thd, static bool write_execute_load_query_log_event(THD *thd,
bool duplicates, bool ignore, bool duplicates, bool ignore,
...@@ -390,7 +423,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -390,7 +423,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */ thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */
thd->cuted_fields=0L; thd->cuted_fields=0L;
/* Skip lines if there is a line terminator */ /* Skip lines if there is a line terminator */
if (ex->line_term->length()) if (ex->line_term->length() && ex->filetype != FILETYPE_XML)
{ {
/* ex->skip_lines needs to be preserved for logging */ /* ex->skip_lines needs to be preserved for logging */
while (skip_lines > 0) while (skip_lines > 0)
...@@ -421,7 +454,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -421,7 +454,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
(MODE_STRICT_TRANS_TABLES | (MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES))); MODE_STRICT_ALL_TABLES)));
if (!field_term->length() && !enclosed->length()) if (ex->filetype == FILETYPE_XML) /* load xml */
error= read_xml_field(thd, info, table_list, fields_vars,
set_fields, set_values, read_info,
*(ex->line_term), skip_lines, ignore);
else if (!field_term->length() && !enclosed->length())
error= read_fixed_length(thd, info, table_list, fields_vars, error= read_fixed_length(thd, info, table_list, fields_vars,
set_fields, set_values, read_info, set_fields, set_values, read_info,
skip_lines, ignore); skip_lines, ignore);
...@@ -917,6 +954,171 @@ continue_loop:; ...@@ -917,6 +954,171 @@ continue_loop:;
} }
/****************************************************************************
** Read rows in xml format
****************************************************************************/
static int
read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
List<Item> &fields_vars, List<Item> &set_fields,
List<Item> &set_values, READ_INFO &read_info,
String &row_tag, ulong skip_lines,
bool ignore_check_option_errors)
{
List_iterator_fast<Item> it(fields_vars);
Item *item;
TABLE *table= table_list->table;
bool no_trans_update_stmt;
CHARSET_INFO *cs= read_info.read_charset;
DBUG_ENTER("read_xml_field");
no_trans_update_stmt= !table->file->has_transactions();
for ( ; ; it.rewind())
{
if (thd->killed)
{
thd->send_kill_message();
DBUG_RETURN(1);
}
// read row tag and save values into tag list
if (read_info.read_xml())
break;
List_iterator_fast<XML_TAG> xmlit(read_info.taglist);
xmlit.rewind();
XML_TAG *tag= NULL;
#ifndef DBUG_OFF
DBUG_PRINT("read_xml_field", ("skip_lines=%d", (int) skip_lines));
while ((tag= xmlit++))
{
DBUG_PRINT("read_xml_field", ("got tag:%i '%s' '%s'",
tag->level, tag->field.c_ptr(),
tag->value.c_ptr()));
}
#endif
restore_record(table, s->default_values);
while ((item= it++))
{
/* If this line is to be skipped we don't want to fill field or var */
if (skip_lines)
continue;
/* find field in tag list */
xmlit.rewind();
tag= xmlit++;
while(tag && strcmp(tag->field.c_ptr(), item->name) != 0)
tag= xmlit++;
if (!tag) // found null
{
if (item->type() == Item::FIELD_ITEM)
{
Field *field= ((Item_field *) item)->field;
field->reset();
field->set_null();
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
if (!field->maybe_null())
{
if (field->type() == FIELD_TYPE_TIMESTAMP)
((Field_timestamp *) field)->set_time();
else if (field != table->next_number_field)
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1);
}
}
else
((Item_user_var_as_out_param *) item)->set_null_value(cs);
continue;
}
if (item->type() == Item::FIELD_ITEM)
{
Field *field= ((Item_field *)item)->field;
field->set_notnull();
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
field->store((char *) tag->value.ptr(), tag->value.length(), cs);
}
else
((Item_user_var_as_out_param *) item)->set_value(
(char *) tag->value.ptr(),
tag->value.length(), cs);
}
if (read_info.error)
break;
if (skip_lines)
{
skip_lines--;
continue;
}
if (item)
{
/* Have not read any field, thus input file is simply ended */
if (item == fields_vars.head())
break;
for ( ; item; item= it++)
{
if (item->type() == Item::FIELD_ITEM)
{
/*
QQ: We probably should not throw warning for each field.
But how about intention to always have the same number
of warnings in THD::cuted_fields (and get rid of cuted_fields
in the end ?)
*/
thd->cuted_fields++;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
thd->warning_info->current_row_for_warning());
}
else
((Item_user_var_as_out_param *)item)->set_null_value(cs);
}
}
if (thd->killed ||
fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
ignore_check_option_errors,
table->triggers,
TRG_EVENT_INSERT))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
ignore_check_option_errors)) {
case VIEW_CHECK_SKIP:
read_info.next_line();
goto continue_loop;
case VIEW_CHECK_ERROR:
DBUG_RETURN(-1);
}
if (write_record(thd, table, &info))
DBUG_RETURN(1);
/*
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
*/
thd->transaction.stmt.modified_non_trans_table= no_trans_update_stmt;
thd->warning_info->inc_current_row_for_warning();
continue_loop:;
}
DBUG_RETURN(test(read_info.error) || thd->is_error());
} /* load xml end */
/* Unescape all escape characters, mark \N as null */ /* Unescape all escape characters, mark \N as null */
char char
...@@ -955,6 +1157,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, ...@@ -955,6 +1157,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
field_term_length= field_term.length(); field_term_length= field_term.length();
line_term_ptr=(char*) line_term.ptr(); line_term_ptr=(char*) line_term.ptr();
line_term_length= line_term.length(); line_term_length= line_term.length();
level= 0; /* for load xml */
if (line_start.length() == 0) if (line_start.length() == 0)
{ {
line_start_ptr=0; line_start_ptr=0;
...@@ -1030,6 +1233,10 @@ READ_INFO::~READ_INFO() ...@@ -1030,6 +1233,10 @@ READ_INFO::~READ_INFO()
my_free((uchar*) buffer,MYF(0)); my_free((uchar*) buffer,MYF(0));
error=1; error=1;
} }
List_iterator<XML_TAG> xmlit(taglist);
XML_TAG *t;
while ((t= xmlit++))
delete(t);
} }
...@@ -1362,3 +1569,319 @@ bool READ_INFO::find_start_of_fields() ...@@ -1362,3 +1569,319 @@ bool READ_INFO::find_start_of_fields()
} }
return 0; return 0;
} }
/*
Clear taglist from tags with a specified level
*/
int READ_INFO::clear_level(int level)
{
DBUG_ENTER("READ_INFO::read_xml clear_level");
List_iterator<XML_TAG> xmlit(taglist);
xmlit.rewind();
XML_TAG *tag;
while ((tag= xmlit++))
{
if(tag->level >= level)
{
xmlit.remove();
delete tag;
}
}
DBUG_RETURN(0);
}
/*
Convert an XML entity to Unicode value.
Return -1 on error;
*/
static int
my_xml_entity_to_char(const char *name, uint length)
{
if (length == 2)
{
if (!memcmp(name, "gt", length))
return '>';
if (!memcmp(name, "lt", length))
return '<';
}
else if (length == 3)
{
if (!memcmp(name, "amp", length))
return '&';
}
else if (length == 4)
{
if (!memcmp(name, "quot", length))
return '"';
if (!memcmp(name, "apos", length))
return '\'';
}
return -1;
}
/**
@brief Convert newline, linefeed, tab to space
@param chr character
@details According to the "XML 1.0" standard,
only space (#x20) characters, carriage returns,
line feeds or tabs are considered as spaces.
Convert all of them to space (#x20) for parsing simplicity.
*/
static int
my_tospace(int chr)
{
return (chr == '\t' || chr == '\r' || chr == '\n') ? ' ' : chr;
}
/*
Read an xml value: handle multibyte and xml escape
*/
int READ_INFO::read_value(int delim, String *val)
{
int chr;
String tmp;
for (chr= GET; my_tospace(chr) != delim && chr != my_b_EOF;)
{
#ifdef USE_MB
if (my_mbcharlen(read_charset, chr) > 1)
{
DBUG_PRINT("read_xml",("multi byte"));
int i, ml= my_mbcharlen(read_charset, chr);
for (i= 1; i < ml; i++)
{
val->append(chr);
/*
Don't use my_tospace() in the middle of a multi-byte character
TODO: check that the multi-byte sequence is valid.
*/
chr= GET;
if (chr == my_b_EOF)
return chr;
}
}
#endif
if(chr == '&')
{
tmp.length(0);
for (chr= my_tospace(GET) ; chr != ';' ; chr= my_tospace(GET))
{
if (chr == my_b_EOF)
return chr;
tmp.append(chr);
}
if ((chr= my_xml_entity_to_char(tmp.ptr(), tmp.length())) >= 0)
val->append(chr);
else
{
val->append('&');
val->append(tmp);
val->append(';');
}
}
else
val->append(chr);
chr= GET;
}
return my_tospace(chr);
}
/*
Read a record in xml format
tags and attributes are stored in taglist
when tag set in ROWS IDENTIFIED BY is closed, we are ready and return
*/
int READ_INFO::read_xml()
{
DBUG_ENTER("READ_INFO::read_xml");
int chr, chr2, chr3;
int delim= 0;
String tag, attribute, value;
bool in_tag= false;
tag.length(0);
attribute.length(0);
value.length(0);
for (chr= my_tospace(GET); chr != my_b_EOF ; )
{
switch(chr){
case '<': /* read tag */
/* TODO: check if this is a comment <!-- comment --> */
chr= my_tospace(GET);
if(chr == '!')
{
chr2= GET;
chr3= GET;
if(chr2 == '-' && chr3 == '-')
{
chr2= 0;
chr3= 0;
chr= my_tospace(GET);
while(chr != '>' || chr2 != '-' || chr3 != '-')
{
if(chr == '-')
{
chr3= chr2;
chr2= chr;
}
else if (chr2 == '-')
{
chr2= 0;
chr3= 0;
}
chr= my_tospace(GET);
if (chr == my_b_EOF)
goto found_eof;
}
break;
}
}
tag.length(0);
while(chr != '>' && chr != ' ' && chr != '/' && chr != my_b_EOF)
{
if(chr != delim) /* fix for the '<field name =' format */
tag.append(chr);
chr= my_tospace(GET);
}
// row tag should be in ROWS IDENTIFIED BY '<row>' - stored in line_term
if((tag.length() == line_term_length -2) &&
(strncmp(tag.c_ptr_safe(), line_term_ptr + 1, tag.length()) == 0))
{
DBUG_PRINT("read_xml", ("start-of-row: %i %s %s",
level,tag.c_ptr_safe(), line_term_ptr));
}
if(chr == ' ' || chr == '>')
{
level++;
clear_level(level + 1);
}
if (chr == ' ')
in_tag= true;
else
in_tag= false;
break;
case ' ': /* read attribute */
while(chr == ' ') /* skip blanks */
chr= my_tospace(GET);
if(!in_tag)
break;
while(chr != '=' && chr != '/' && chr != '>' && chr != my_b_EOF)
{
attribute.append(chr);
chr= my_tospace(GET);
}
break;
case '>': /* end tag - read tag value */
in_tag= false;
chr= read_value('<', &value);
if(chr == my_b_EOF)
goto found_eof;
/* save value to list */
if(tag.length() > 0 && value.length() > 0)
{
DBUG_PRINT("read_xml", ("lev:%i tag:%s val:%s",
level,tag.c_ptr_safe(), value.c_ptr_safe()));
taglist.push_front( new XML_TAG(level, tag, value));
}
tag.length(0);
value.length(0);
attribute.length(0);
break;
case '/': /* close tag */
level--;
chr= my_tospace(GET);
if(chr != '>') /* if this is an empty tag <tag /> */
tag.length(0); /* we should keep tag value */
while(chr != '>' && chr != my_b_EOF)
{
tag.append(chr);
chr= my_tospace(GET);
}
if((tag.length() == line_term_length -2) &&
(strncmp(tag.c_ptr_safe(), line_term_ptr + 1, tag.length()) == 0))
{
DBUG_PRINT("read_xml", ("found end-of-row %i %s",
level, tag.c_ptr_safe()));
DBUG_RETURN(0); //normal return
}
chr= my_tospace(GET);
break;
case '=': /* attribute name end - read the value */
//check for tag field and attribute name
if(!memcmp(tag.c_ptr_safe(), STRING_WITH_LEN("field")) &&
!memcmp(attribute.c_ptr_safe(), STRING_WITH_LEN("name")))
{
/*
this is format <field name="xx">xx</field>
where actual fieldname is in attribute
*/
delim= my_tospace(GET);
tag.length(0);
attribute.length(0);
chr= '<'; /* we pretend that it is a tag */
level--;
break;
}
//check for " or '
chr= GET;
if (chr == my_b_EOF)
goto found_eof;
if(chr == '"' || chr == '\'')
{
delim= chr;
}
else
{
delim= ' '; /* no delimiter, use space */
PUSH(chr);
}
chr= read_value(delim, &value);
if(attribute.length() > 0 && value.length() > 0)
{
DBUG_PRINT("read_xml", ("lev:%i att:%s val:%s\n",
level + 1,
attribute.c_ptr_safe(),
value.c_ptr_safe()));
taglist.push_front(new XML_TAG(level + 1, attribute, value));
}
attribute.length(0);
value.length(0);
if (chr != ' ')
chr= my_tospace(GET);
break;
default:
chr= my_tospace(GET);
} /* end switch */
} /* end while */
found_eof:
DBUG_PRINT("read_xml",("Found eof"));
eof= 1;
DBUG_RETURN(1);
}
...@@ -508,6 +508,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, ...@@ -508,6 +508,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
sp_head *sphead; sp_head *sphead;
struct p_elem_val *p_elem_value; struct p_elem_val *p_elem_value;
enum index_hint_type index_hint; enum index_hint_type index_hint;
enum enum_filetype filetype;
Diag_condition_item_name diag_condition_item_name; Diag_condition_item_name diag_condition_item_name;
} }
...@@ -1116,6 +1117,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1116,6 +1117,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token WRITE_SYM /* SQL-2003-N */ %token WRITE_SYM /* SQL-2003-N */
%token X509_SYM %token X509_SYM
%token XA_SYM %token XA_SYM
%token XML_SYM
%token XOR %token XOR
%token YEAR_MONTH_SYM %token YEAR_MONTH_SYM
%token YEAR_SYM /* SQL-2003-R */ %token YEAR_SYM /* SQL-2003-R */
...@@ -1309,7 +1311,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1309,7 +1311,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
prepare prepare_src execute deallocate prepare prepare_src execute deallocate
statement sp_suid statement sp_suid
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
view_replace_or_algorithm view_replace view_replace_or_algorithm view_replace
view_algorithm view_or_trigger_or_sp_or_event view_algorithm view_or_trigger_or_sp_or_event
definer_tail no_definer_tail definer_tail no_definer_tail
...@@ -1338,6 +1340,7 @@ END_OF_INPUT ...@@ -1338,6 +1340,7 @@ END_OF_INPUT
%type <spname> sp_name %type <spname> sp_name
%type <index_hint> index_hint_type %type <index_hint> index_hint_type
%type <num> index_hint_clause %type <num> index_hint_clause
%type <filetype> data_or_xml
%type <NONE> signal_stmt resignal_stmt %type <NONE> signal_stmt resignal_stmt
%type <diag_condition_item_name> signal_condition_information_item_name %type <diag_condition_item_name> signal_condition_information_item_name
...@@ -10663,7 +10666,7 @@ use: ...@@ -10663,7 +10666,7 @@ use:
/* import, export of files */ /* import, export of files */
load: load:
LOAD DATA_SYM LOAD data_or_xml
{ {
THD *thd= YYTHD; THD *thd= YYTHD;
LEX *lex= thd->lex; LEX *lex= thd->lex;
...@@ -10671,39 +10674,21 @@ load: ...@@ -10671,39 +10674,21 @@ load:
if (lex->sphead) if (lex->sphead)
{ {
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); my_error(ER_SP_BADSTATEMENT, MYF(0),
$2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML");
MYSQL_YYABORT; MYSQL_YYABORT;
} }
lex->fname_start= lip->get_ptr(); lex->fname_start= lip->get_ptr();
} }
load_data
{}
| LOAD TABLE_SYM table_ident FROM MASTER_SYM
{
LEX *lex=Lex;
WARN_DEPRECATED(yythd, "6.0", "LOAD TABLE FROM MASTER",
"MySQL Administrator (mysqldump, mysql)");
if (lex->sphead)
{
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE");
MYSQL_YYABORT;
}
lex->sql_command = SQLCOM_LOAD_MASTER_TABLE;
if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING))
MYSQL_YYABORT;
}
;
load_data:
load_data_lock opt_local INFILE TEXT_STRING_filesystem load_data_lock opt_local INFILE TEXT_STRING_filesystem
{ {
LEX *lex=Lex; LEX *lex=Lex;
lex->sql_command= SQLCOM_LOAD; lex->sql_command= SQLCOM_LOAD;
lex->lock_option= $1; lex->lock_option= $4;
lex->local_file= $2; lex->local_file= $5;
lex->duplicates= DUP_ERROR; lex->duplicates= DUP_ERROR;
lex->ignore= 0; lex->ignore= 0;
if (!(lex->exchange= new sql_exchange($4.str, 0))) if (!(lex->exchange= new sql_exchange($7.str, 0, $2)))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
opt_duplicate INTO opt_duplicate INTO
...@@ -10713,7 +10698,7 @@ load_data: ...@@ -10713,7 +10698,7 @@ load_data:
TABLE_SYM table_ident TABLE_SYM table_ident
{ {
LEX *lex=Lex; LEX *lex=Lex;
if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING, if (!Select->add_table_to_list(YYTHD, $13, NULL, TL_OPTION_UPDATING,
lex->lock_option)) lex->lock_option))
MYSQL_YYABORT; MYSQL_YYABORT;
lex->field_list.empty(); lex->field_list.empty();
...@@ -10721,12 +10706,37 @@ load_data: ...@@ -10721,12 +10706,37 @@ load_data:
lex->value_list.empty(); lex->value_list.empty();
} }
opt_load_data_charset opt_load_data_charset
{ Lex->exchange->cs= $12; } { Lex->exchange->cs= $15; }
opt_xml_rows_identified_by
opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec
opt_load_data_set_spec opt_load_data_set_spec
{} {}
| FROM MASTER_SYM | LOAD TABLE_SYM table_ident FROM MASTER_SYM
{
LEX *lex=Lex;
WARN_DEPRECATED(yythd, "6.0", "LOAD TABLE FROM MASTER",
"MySQL Administrator (mysqldump, mysql)");
if (lex->sphead)
{ {
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE");
MYSQL_YYABORT;
}
lex->sql_command = SQLCOM_LOAD_MASTER_TABLE;
if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING))
MYSQL_YYABORT;
}
| LOAD DATA_SYM FROM MASTER_SYM
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
if (lex->sphead)
{
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
MYSQL_YYABORT;
}
lex->fname_start= lip->get_ptr();
Lex->sql_command = SQLCOM_LOAD_MASTER_DATA; Lex->sql_command = SQLCOM_LOAD_MASTER_DATA;
WARN_DEPRECATED(yythd, "6.0", "LOAD DATA FROM MASTER", WARN_DEPRECATED(yythd, "6.0", "LOAD DATA FROM MASTER",
"mysqldump or future " "mysqldump or future "
...@@ -10734,6 +10744,11 @@ load_data: ...@@ -10734,6 +10744,11 @@ load_data:
} }
; ;
data_or_xml:
DATA_SYM { $$= FILETYPE_CSV; }
| XML_SYM { $$= FILETYPE_XML; }
;
opt_local: opt_local:
/* empty */ { $$=0;} /* empty */ { $$=0;}
| LOCAL_SYM { $$=1;} | LOCAL_SYM { $$=1;}
...@@ -10820,15 +10835,25 @@ line_term: ...@@ -10820,15 +10835,25 @@ line_term:
} }
; ;
opt_xml_rows_identified_by:
/* empty */ { }
| ROWS_SYM IDENTIFIED_SYM BY text_string
{ Lex->exchange->line_term = $4; };
opt_ignore_lines: opt_ignore_lines:
/* empty */ /* empty */
| IGNORE_SYM NUM LINES | IGNORE_SYM NUM lines_or_rows
{ {
DBUG_ASSERT(Lex->exchange != 0); DBUG_ASSERT(Lex->exchange != 0);
Lex->exchange->skip_lines= atol($2.str); Lex->exchange->skip_lines= atol($2.str);
} }
; ;
lines_or_rows:
LINES { }
| ROWS_SYM { }
;
opt_field_or_var_spec: opt_field_or_var_spec:
/* empty */ {} /* empty */ {}
| '(' fields_or_vars ')' {} | '(' fields_or_vars ')' {}
...@@ -11924,6 +11949,7 @@ keyword_sp: ...@@ -11924,6 +11949,7 @@ keyword_sp:
| WEEK_SYM {} | WEEK_SYM {}
| WORK_SYM {} | WORK_SYM {}
| X509_SYM {} | X509_SYM {}
| XML_SYM {}
| YEAR_SYM {} | YEAR_SYM {}
; ;
......
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