--- parser3/src/classes/hash.C 2024/09/27 23:38:52 1.161 +++ parser3/src/classes/hash.C 2024/10/27 17:50:59 1.166 @@ -11,13 +11,14 @@ #include "pa_request.h" #include "pa_charsets.h" #include "pa_vhash.h" +#include "pa_varray.h" #include "pa_vvoid.h" #include "pa_sql_connection.h" #include "pa_vtable.h" #include "pa_vbool.h" #include "pa_vmethod_frame.h" -volatile const char * IDENT_HASH_C="$Id: hash.C,v 1.161 2024/09/27 23:38:52 moko Exp $"; +volatile const char * IDENT_HASH_C="$Id: hash.C,v 1.166 2024/10/27 17:50:59 moko Exp $"; // class @@ -41,7 +42,7 @@ class Hash_sql_event_handlers: public SQ HashStringValue& result; Value* row_value; int column_index; - ArrayString& columns; + ArrayString* columns; bool one_bool_column; Table2hash_value_type value_type; int columns_count; @@ -53,7 +54,7 @@ public: result(aresult), row_value(0), column_index(0), - columns(*new ArrayString), + columns(new ArrayString), one_bool_column(false), value_type(avalue_type), empty(0) { @@ -61,7 +62,12 @@ public: bool add_column(SQL_Error& error, const char* str, size_t ) { try { - columns+=new String(str, String::L_TAINTED /* no length as 0x00 can be inside */); + if(columns_count){ + // another query in multi_statements mode + columns=new ArrayString; + columns_count=0; + } + *columns+=new String(str, String::L_TAINTED /* no length as 0x00 can be inside */); return false; } catch(...) { error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_column"); @@ -70,24 +76,27 @@ public: } bool before_rows(SQL_Error& error) { - if(columns.count()<1) { + columns_count=columns->count(); + if(columns_count<1) { error=SQL_Error("no columns"); return true; } - switch(value_type){ - case C_STRING: { - if(columns.count()>2){ - error=SQL_Error("only 2 columns allowed for $.type[string]."); - return true; + if(columns_count==1) { + one_bool_column=true; + } else { + switch(value_type){ + case C_STRING: { + if(columns_count>2){ + error=SQL_Error("only 2 columns allowed for $.type[string]"); + return true; + } + break; + } + case C_TABLE: { + // create empty table which we'll copy later + empty=new Table(columns); + break; } - } - case C_TABLE: { - // create empty table which we'll copy later - empty=new Table(&columns); - columns_count=columns.count(); - } - case C_HASH: { - one_bool_column=columns.count()==1; } } return false; @@ -102,6 +111,12 @@ public: try { const String& cell=str ? *new String(str, String::L_TAINTED /* no length as 0x00 can be inside */) : String::Empty; + if(column_index==columns_count){ + // should never happen, buggy driver case + error=SQL_Error("columns index exceed the columns count"); + return true; + } + bool duplicate=false; if(one_bool_column) { duplicate=result.put_dont_replace(cell, &VBool::get(true)); // put. existed? @@ -143,7 +158,7 @@ public: } else { switch(value_type) { case C_HASH: { - row_value->get_hash()->put(*columns[column_index], new VString(cell)); + row_value->get_hash()->put(*columns->get(column_index), new VString(cell)); break; } case C_STRING: { @@ -183,7 +198,13 @@ static void _create_or_add(Request& r, M HashStringValue* self_hash=&(self.hash()); HashStringValue* src_hash; - if(VHashBase* src=dynamic_cast(&vsrc)) { + if(VArray* src=dynamic_cast(&vsrc)) { + for(ArrayValue::Iterator i(src->array()); i; i.next()){ + if(i.value()) + self_hash->put(i.key(), i.value()); + } + return; + } else if(VHashBase* src=dynamic_cast(&vsrc)) { src_hash=&(src->hash()); if(src_hash==self_hash) // same: doing nothing @@ -192,6 +213,7 @@ static void _create_or_add(Request& r, M if(Value* vdefault=src->get_default()) self.set_default(vdefault); } else { + // allows $h[^hash::create[non-blank string]], thus as_hash("param") is more correct, but is not backward compatible src_hash=vsrc.get_hash(); } @@ -458,7 +480,7 @@ enum AtResultType { AtResultTypeHash = 2 }; -inline Value& SingleElementHash(String::Body akey, Value* avalue) { +static Value& SingleElementHash(String::Body akey, Value* avalue) { Value& result=*new VHash; result.put_element(*new String(akey, String::L_TAINTED), avalue); return result; @@ -550,8 +572,6 @@ static void _at(Request& r, MethodParams HashStringValue& hash=GET_SELF(r, VHashBase).hash(); size_t count=hash.count(); - int pos=0; - // misha@ // I do not like that type is checked before whence. // But I do not like the idea to move it after whence (where process can be called) even more. @@ -566,20 +586,7 @@ static void _at(Request& r, MethodParams throw Exception(PARSER_RUNTIME, &stype, "type must be 'key', 'value' or 'hash'"); } - Value& vwhence=params[0]; - if(vwhence.is_string()) { - const String& swhence=*vwhence.get_string(); - if(swhence == "last") - pos=count-1; - else if(swhence != "first") - throw Exception(PARSER_RUNTIME, - &swhence, - "whence must be 'first', 'last' or expression"); - } else { - pos=r.process(vwhence).as_int(); - if(pos < 0) - pos+=count; - } + int pos=params.as_index(0, count, r); if(count && pos >= 0 && (size_t)pos < count){ switch(result_type) {