--- parser3/src/classes/table.C 2007/08/20 10:07:36 1.232 +++ parser3/src/classes/table.C 2009/07/29 05:01:33 1.263 @@ -1,14 +1,16 @@ /** @file Parser: @b table parser class. - Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char * const IDENT_TABLE_C="$Date: 2007/08/20 10:07:36 $"; +static const char * const IDENT_TABLE_C="$Date: 2009/07/29 05:01:33 $"; +#if (!defined(NO_STRINGSTREAM) && !defined(FREEBSD4)) #include using namespace std; +#endif #include "classes.h" #include "pa_vmethod_frame.h" @@ -24,7 +26,7 @@ using namespace std; class MTable: public Methoded { public: // VStateless_class - Value* create_new_value(Pool&, HashStringValue&) { return new VTable(); } + Value* create_new_value(Pool&, HashStringValue*) { return new VTable(); } public: MTable(); @@ -37,19 +39,8 @@ public: // Methoded DECLARE_CLASS_VAR(table, new MTable, 0); -// externs - -extern String cycle_data_name; - -// defines for globals - -#define SQL_BIND_NAME "bind" -#define SQL_DEFAULT_NAME "default" -#define SQL_DISTINCT_NAME "distinct" -#define SQL_VALUE_TYPE_NAME "type" #define TABLE_REVERSE_NAME "reverse" - // globals String sql_bind_name(SQL_BIND_NAME); @@ -130,6 +121,38 @@ static void check_option_param(bool opti "%s", msg); } +struct TableSeparators { + char column; const String* scolumn; + char encloser; const String* sencloser; + + TableSeparators(): + column('\t'), scolumn(new String("\t")), + encloser(0), sencloser(0) + {} + int load( HashStringValue& options ) { + int result=0; + if(Value* vseparator=options.get(PA_COLUMN_SEPARATOR_NAME)) { + scolumn=&vseparator->as_string(); + if(scolumn->length()!=1) + throw Exception(PARSER_RUNTIME, + scolumn, + "separator must be one character long"); + column=scolumn->first_char(); + result++; + } + if(Value* vencloser=options.get(PA_COLUMN_ENCLOSER_NAME)) { + sencloser=&vencloser->as_string(); + if(sencloser->length()!=1) + throw Exception(PARSER_RUNTIME, + sencloser, + "encloser must be one character long"); + encloser=sencloser->first_char(); + result++; + } + return result; + } +}; + static void _create(Request& r, MethodParams& params) { // clone/copy part? if(Table *source=params[0].get_table()) { @@ -140,20 +163,49 @@ static void _create(Request& r, MethodPa return; } - // data is last parameter + size_t data_param_index=0; + bool nameless=false; + + if(params.count()>1) { + if(params[0].is_string()){ // can be nameless only + const String& snameless=params.as_string(0, "called with more then 1 param, first param may be only string 'nameless' or junction"); + if(snameless!="nameless") + throw Exception(PARSER_RUNTIME, + &snameless, + "table::create called with more then 1 param, first param may be only 'nameless'"); + nameless=true; + data_param_index++; + } + } + + HashStringValue *options=0; + TableSeparators separators; + + size_t options_param_index=data_param_index+1; + if( + options_param_indexsplit(*columns, col_pos_after, "\t", String::L_AS_IS); + head[0]->split(*columns, col_pos_after, *separators.scolumn, String::L_AS_IS); } } @@ -174,13 +226,13 @@ static void _create(Request& r, MethodPa Array_iterator i(rows); while(i.has_next()) { Table::element_type row(new ArrayString); - const String& string=*i.next(); + const String& string=*i.next(); // remove comment lines - if(!string.length()) + if(string.is_empty()) continue; size_t col_pos_after=0; - string.split(*row, col_pos_after, "\t", String::L_AS_IS); + string.split(*row, col_pos_after, *separators.scolumn, String::L_AS_IS); table+=row; } @@ -270,38 +322,6 @@ static void skip_empty_and_comment_lines } } -struct TableSeparators { - char column; const String* scolumn; - char encloser; const String* sencloser; - - TableSeparators(): - column('\t'), scolumn(new String("\t", false)), - encloser(0), sencloser(0) - {} - int load( HashStringValue& options ) { - int result=0; - if(Value* vseparator=options.get(PA_COLUMN_SEPARATOR_NAME)) { - scolumn=&vseparator->as_string(); - if(scolumn->length()!=1) - throw Exception(PARSER_RUNTIME, - scolumn, - "separator must be one character long"); - column=scolumn->first_char(); - result++; - } - if(Value* vencloser=options.get(PA_COLUMN_ENCLOSER_NAME)) { - sencloser=&vencloser->as_string(); - if(sencloser->length()!=1) - throw Exception(PARSER_RUNTIME, - sencloser, - "encloser must be one character long"); - encloser=sencloser->first_char(); - result++; - } - return result; - } -}; - static void _load(Request& r, MethodParams& params) { const String& first_param=params.as_string(0, FILE_NAME_MUST_BE_STRING); int filename_param_index=0; @@ -320,7 +340,7 @@ static void _load(Request& r, MethodPara } // loading text - char *data=file_read_text(r.charsets, + char *data=file_load_text(r, r.absolute(params.as_string(filename_param_index, FILE_NAME_MUST_BE_STRING)), true, options @@ -335,7 +355,7 @@ static void _load(Request& r, MethodPara skip_empty_and_comment_lines(&data); while( lsplit_result sr=lsplit(&data, separators.column, '\n', separators.encloser) ) { - *columns+=new String(sr.piece, 0, true); + *columns+=new String(sr.piece, String::L_TAINTED); if(sr.delim=='\n') break; } @@ -350,7 +370,7 @@ static void _load(Request& r, MethodPara while( lsplit_result sr=lsplit(&data, separators.column, '\n', separators.encloser) ) { if(!*sr.piece && !sr.delim && !row->count()) // append last empty column [if without \n] break; - *row+=new String(sr.piece, 0, true); + *row+=new String(sr.piece, String::L_TAINTED); if(sr.delim=='\n') { table+=row; row=new ArrayString(columns_count); @@ -365,49 +385,54 @@ static void _load(Request& r, MethodPara GET_SELF(r, VTable).set_table(table); } -void maybe_enclose( String& to, const String& from, char encloser, const String* sencloser ) { +#if (!defined(NO_STRINGSTREAM) && !defined(FREEBSD4)) + +void maybe_enclose( ostringstream& to, const String& from, char encloser ) { if(encloser) { - to<<*sencloser; + to< i(*table.columns()); i.has_next(); ) { - maybe_enclose( sdata, *i.next(), separators.encloser, separators.sencloser ); - if(i.has_next()) - sdata<<*separators.scolumn; + maybe_enclose( ost, *i.next(), separators.encloser ); + if(i.has_next()){ + ost<count():0) for(int column=0; column i(table); while(i.has_next()) { for(Array_iterator c(*i.next()); c.has_next(); ) { - maybe_enclose( sdata, *c.next(), separators.encloser, separators.sencloser ); + maybe_enclose( ost, *c.next(), separators.encloser ); if(c.has_next()) - sdata<<*separators.scolumn; + ost<count()) - throw Exception(PARSER_RUNTIME, - 0, - "invalid option passed"); - } else { - throw Exception(PARSER_RUNTIME, - 0, - "additional params must be hash (did you spell mode parameter correctly?)"); - } - } + file_write(file_spec, data_cstr, data.length(), true /* as text */, do_append); } - if(param_index i(*table.columns()); i.has_next(); ) { - maybe_enclose( ost, *i.next(), separators.encloser ); - if(i.has_next()){ - ost<count():0) for(int column=0; column i(table); while(i.has_next()) { for(Array_iterator c(*i.next()); c.has_next(); ) { - maybe_enclose( ost, *c.next(), separators.encloser ); + maybe_enclose( sdata, *c.next(), separators.encloser, separators.sencloser ); if(c.has_next()) - ost< - cycle_data_setter(r.classes_conf, cycle_data_name, /*any not null flag*/&r); + InCycle temp(r); Value& body_code=params.as_junction(0, "body must be code"); Value* delim_maybe_code=params.count()>1?¶ms[1]:0; Table& table=GET_SELF(r, VTable).table(); - bool need_delim=false; int saved_current=table.current(); int size=table.count(); - for(int row=0; rowlength()) { // delimiter set and we have body - if(need_delim) // need delim & iteration produced string? - r.write_pass_lang(r.process(*delim_maybe_code)); - need_delim=true; - } - r.write_pass_lang(sv_processed); + if(delim_maybe_code) { // delimiter set + bool need_delim=false; + for(int row=0; rowis_empty()) { // we have body + if(need_delim) // need delim & iteration produced string? + r.write_pass_lang(r.process(*delim_maybe_code)); + else + need_delim=true; + } - if(lskip==Request::SKIP_BREAK) - break; + r.write_pass_lang(sv_processed); + + if(lskip==Request::SKIP_BREAK) + break; + } + } else { + for(int row=0; rowvalue_type) { - case C_STRING: - { + bool exist=false; + switch(info->value_type) { + case C_STRING: { exist=info->hash->put_dont_replace(*key, new VString(*row->get(info->value_fields->get(0)))); + break; } - break; - case C_HASH: - { + case C_HASH: { VHash* vhash=new VHash; HashStringValue& hash=vhash->hash(); for(Array_iterator i(*info->value_fields); i.has_next(); ) { @@ -692,18 +696,16 @@ static void table_row_to_hash(Table::ele } exist=info->hash->put_dont_replace(*key, vhash); + break; } - break; - case C_TABLE: - { - VTable* vtable=(VTable*)info->hash->get(*key); // put. table existed? - if( info->distinct==D_ILLEGAL ){ - exist=true; - break; - } - + case C_TABLE: { + VTable* vtable=(VTable*)info->hash->get(*key); // table exist? Table* table; if(vtable) { + if(info->distinct==D_ILLEGAL) { + exist=true; + break; + } table=vtable->get_table(); } else { // no? creating table of same structure as source @@ -712,29 +714,49 @@ static void table_row_to_hash(Table::ele info->hash->put(*key, new VTable(table)); } *table+=row; + break; } - break; } if(exist && info->distinct==D_ILLEGAL) throw Exception(PARSER_RUNTIME, key, "duplicate key"); } + +Table2hash_value_type get_value_type(Value& vvalue_type){ + if(vvalue_type.is_string()) { + const String& svalue_type=*vvalue_type.get_string(); + if(svalue_type == "table"){ + return C_TABLE; + } else if (svalue_type == "string") { + return C_STRING; + } else if (svalue_type == "hash") { + return C_HASH; + } else { + throw Exception(PARSER_RUNTIME, + &svalue_type, + "must be 'hash', 'table' or 'string'"); + } + } else { + throw Exception(PARSER_RUNTIME, + 0, + "'type' must be hash"); + } +} + static void _hash(Request& r, MethodParams& params) { Table& self_table=GET_SELF(r, VTable).table(); VHash& result=*new VHash; - if(Table::columns_type columns=self_table.columns()) + if(Table::columns_type columns=self_table.columns()){ if(columns->count()>0) { Table2hash_distint distinct=D_ILLEGAL; Table2hash_value_type value_type=C_HASH; int param_index=params.count()-1; if(param_index>0) { - if(HashStringValue* options= - params.as_no_junction(param_index, "param must not be code").get_hash() - ){ + if(HashStringValue* options=params.as_no_junction(param_index, PARAM_MUST_NOT_BE_CODE).get_hash()){ // options where specified --param_index; int valid_options=0; - if(Value* vdistinct_code=options->get(sql_distinct_name)) { + if(Value* vdistinct_code=options->get(sql_distinct_name)) { // $.distinct ? valid_options++; Value& vdistinct_value=r.process_to_value(*vdistinct_code); if(vdistinct_value.is_string()) { @@ -742,37 +764,24 @@ static void _hash(Request& r, MethodPara if(sdistinct=="tables") { value_type=C_TABLE; distinct=D_FIRST; - } else + } else { throw Exception(PARSER_RUNTIME, &sdistinct, "must be 'tables' or true/false"); - } else + } + } else { distinct=vdistinct_value.as_bool()?D_FIRST:D_ILLEGAL; + } } - if(Value* vvalue_type_code=options->get(sql_value_type_name)) { - if(value_type==C_TABLE){ // $.distinct[tables] was specified + if(Value* vvalue_type_code=options->get(sql_value_type_name)) { // $.type ? + if(value_type==C_TABLE) // $.distinct[tables] already was specified throw Exception(PARSER_RUNTIME, 0, - "you can't specify $.distinct[tables] and $.values[] together."); - } else { - valid_options++; - Value& vvalue_type_value=r.process_to_value(*vvalue_type_code); - if(vvalue_type_value.is_string()) { - const String& svalue_type=*vvalue_type_value.get_string(); - if(svalue_type == "table"){ - value_type=C_TABLE; - } else if (svalue_type == "string") { - value_type=C_STRING; - } else if (svalue_type == "hash") { - value_type=C_HASH; - } else { - throw Exception(PARSER_RUNTIME, - &svalue_type, - "must be 'hash', 'table' or 'string'"); - } - } - } - } + "you can't specify $.distinct[tables] and $.type[] together"); + + valid_options++; + value_type=get_value_type(r.process_to_value(*vvalue_type_code)); + } if(valid_options!=options->count()) throw Exception(PARSER_RUNTIME, @@ -780,48 +789,46 @@ static void _hash(Request& r, MethodPara "called with invalid option"); } } - if(param_index==2) // bad options param type + + if(param_index==2) // options was specified but not as hash throw Exception(PARSER_RUNTIME, 0, "options must be hash"); Array value_fields; - if(param_index>0) { + if(param_index==0){ // list of columns wasn't specified + if(value_type==C_STRING) // $.type[string] + throw Exception(PARSER_RUNTIME, + 0, + "you must specify one value field with option $.type[string]"); + + for(size_t i=0; icount(); i++) // by all columns, including key + value_fields+=i; + + } else { // list of columns was specified if(value_type==C_TABLE) throw Exception(PARSER_RUNTIME, 0, - "in distinct[tables] mode you may not specify value field(s)"); + "you can't specify value field(s) with option $.distinct[tables] or $.type[tables]"); + Value& value_fields_param=params.as_no_junction(param_index, "value field(s) must not be code"); - if(value_fields_param.is_string()) { - value_fields+=self_table.column_name2index( - *value_fields_param.get_string(), true); - } else if(Table* value_fields_table=value_fields_param.get_table()) { - for(Array_iterator i(*value_fields_table); - i.has_next(); ) { - const String& value_field_name - =*i.next()->get(0); - value_fields - +=self_table.column_name2index(value_field_name, true); + if(value_fields_param.is_string()) { // one column as string was specified + value_fields+=self_table.column_name2index(*value_fields_param.get_string(), true); + } else if(Table* value_fields_table=value_fields_param.get_table()) { // list of columns were specified in table + for(Array_iterator i(*value_fields_table); i.has_next(); ) { + const String& value_field_name =*i.next()->get(0); + value_fields +=self_table.column_name2index(value_field_name, true); } } else throw Exception(PARSER_RUNTIME, 0, "value field(s) must be string or table"); - - } else { // by all columns, including key - if(value_type==C_STRING) - throw Exception(PARSER_RUNTIME, - 0, - "with $.values[string] you must specify one value field(s)"); - // if(!(distinct!=D_ILLEGAL && distinct!=D_FIRST)) - for(size_t i=0; icount(); i++) - value_fields+=i; } if(value_type==C_STRING && value_fields.count()!=1) throw Exception(PARSER_RUNTIME, 0, - "you can specify one value field with this $.type[]."); + "you can specify only one value field with option $.type[string]"); { Value* key_param=¶ms[0]; @@ -846,6 +853,7 @@ static void _hash(Request& r, MethodPara result.extract_default(); } } + } r.write_no_lang(result); } @@ -951,7 +959,7 @@ static void _locate(Request& r, MethodPa bool result=params[0].get_junction()? _locate_expression(table, o, r, params) : _locate_name_value(table, o, r, params); - r.write_no_lang(*new VBool(result)); + r.write_no_lang(VBool::get(result)); } @@ -1036,7 +1044,7 @@ public: bool add_column(SQL_Error& error, const char *str, size_t length) { try { - columns+=new String(str, length, true); + columns+=new String(str, String::L_TAINTED, length); return false; } catch(...) { error=SQL_Error("exception occured in Table_sql_event_handlers::add_column"); @@ -1064,10 +1072,7 @@ public: } bool add_row_cell(SQL_Error& error, const char* str, size_t length) { try { - String& cell=*new String; - if(length) - cell.append_know_length(str, length, String::L_TAINTED); - *row+=&cell; + *row+=new String(str, String::L_TAINTED, length); return false; } catch(...) { error=SQL_Error("exception occured in Table_sql_event_handlers::add_row_cell"); @@ -1084,7 +1089,7 @@ static void marshal_bind( { SQL_Driver::Placeholder& ph=**pptr; ph.name=aname.cstr(); - ph.value=avalue->as_string().cstr(String::L_UNSPECIFIED); + ph.value=avalue->as_string().untaint_cstr(String::L_AS_IS); ph.is_null=avalue->get_class()==void_class; ph.were_updated=false; @@ -1107,12 +1112,9 @@ void unmarshal_bind_updates(HashStringVa if(ph->were_updated) { Value* value; if(ph->is_null) - value=new VVoid(); + value=VVoid::get(); else - if(ph->value) - value=new VString(*new String(ph->value, 0, true/*tainted*/)); - else - value=new VString(*new String()); + value=new VString(*new String(ph->value, String::L_TAINTED)); hash.put(ph->name, value); } } @@ -1121,7 +1123,7 @@ static void _sql(Request& r, MethodParam Value& statement=params.as_junction(0, "statement must be code"); HashStringValue* bind=0; - ulong limit=0; + ulong limit=SQL_NO_LIMIT; ulong offset=0; if(params.count()>1) { Value& voptions=params.as_no_junction(1, "options must be hash, not code"); @@ -1157,8 +1159,8 @@ static void _sql(Request& r, MethodParam Temp_lang temp_lang(r, String::L_SQL); const String& statement_string=r.process_to_string(statement); - const char* statement_cstr= - statement_string.cstr(String::L_UNSPECIFIED, r.connection()); + const char* statement_cstr=statement_string.untaint_cstr(r.flang, r.connection()); + Table_sql_event_handlers handlers; #ifdef RESOURCES_DEBUG struct timeval mt[2]; @@ -1194,10 +1196,15 @@ static void _sql(Request& r, MethodParam GET_SELF(r, VTable).set_table(result); } -static void _columns(Request& r, MethodParams&) { +static void _columns(Request& r, MethodParams& params) { + const String* column_column_name; + if(params.count()>0) + column_column_name=¶ms.as_string(0, COLUMN_NAME_MUST_BE_STRING); + else + column_column_name=new String("column"); Table::columns_type result_columns(new ArrayString); - *result_columns+=new String("column"); + *result_columns+=column_column_name; Table& result_table=*new Table(result_columns); Table& source_table=GET_SELF(r, VTable).table(); @@ -1240,9 +1247,9 @@ MTable::MTable(): Methoded("table") { // ^table::create{data} // ^table::create[nameless]{data} // ^table::create[table] - add_native_method("create", Method::CT_DYNAMIC, _create, 1, 2); + add_native_method("create", Method::CT_DYNAMIC, _create, 1, 3); // old name for compatibility with <= v 1.141 2002/01/25 11:33:45 paf - add_native_method("set", Method::CT_DYNAMIC, _create, 1, 2); + add_native_method("set", Method::CT_DYNAMIC, _create, 1, 3); // ^table::load[file] // ^table::load[nameless;file] @@ -1269,8 +1276,8 @@ MTable::MTable(): Methoded("table") { // ^table.menu{code}[delim] add_native_method("menu", Method::CT_DYNAMIC, _menu, 1, 2); - // ^table:hash[key field name] - // ^table:hash[key field name][value field name(s) string/table] + // ^table.hash[key field name] + // ^table.hash[key field name][value field name(s) string/table] add_native_method("hash", Method::CT_DYNAMIC, _hash, 1, 3); // ^table.sort{string-key-maker} ^table.sort{string-key-maker}[desc|asc] @@ -1290,12 +1297,12 @@ MTable::MTable(): Methoded("table") { add_native_method("join", Method::CT_DYNAMIC, _join, 1, 2); - // ^table:sql[query] - // ^table:sql[query][$.limit(1) $.offset(2)] + // ^table::sql[query] + // ^table::sql[query][$.limit(1) $.offset(2)] add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2); - // ^table:columns[] - add_native_method("columns", Method::CT_DYNAMIC, _columns, 0, 0); + // ^table.columns[[column name]] + add_native_method("columns", Method::CT_DYNAMIC, _columns, 0, 1); // ^table.select(expression) = table add_native_method("select", Method::CT_DYNAMIC, _select, 1, 1);