--- parser3/src/classes/array.C 2024/09/28 14:15:13 1.11 +++ parser3/src/classes/array.C 2024/10/02 17:58:15 1.16 @@ -17,7 +17,7 @@ #include "pa_vbool.h" #include "pa_vmethod_frame.h" -volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.11 2024/09/28 14:15:13 moko Exp $"; +volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.16 2024/10/02 17:58:15 moko Exp $"; // class @@ -46,10 +46,17 @@ static void _create_or_add(Request& r, M if(VArray* src=dynamic_cast(&vsrc)) { if(src==&self) - throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array"); - self_array.append(src->array()); + return; + if(self_array.count()){ + for(ArrayValue::Iterator i(src->array()); i; i.next()){ + if(i.value()) + self_array.put(i.index(), i.value()); + } + } else { + self_array.append(src->array()); + } } else { - HashStringValue* src_hash=vsrc.get_hash(); + HashStringValue* src_hash=vsrc.as_hash("param must be array or"); if(!src_hash) return; for(HashStringValue::Iterator i(*src_hash); i; i.next()){ @@ -119,7 +126,7 @@ static void _join(Request& r, MethodPara } } } else { - HashStringValue* src_hash=vsrc.get_hash(); + HashStringValue* src_hash=vsrc.as_hash("param must be array or"); if(!src_hash) return; if(o.defined){ @@ -150,7 +157,7 @@ class SparseArray_sql_event_handlers: pu ArrayValue& result; Value* row_value; int column_index; - ArrayString& columns; + ArrayString* columns; bool one_bool_column; Table2hash_value_type value_type; int columns_count; @@ -162,7 +169,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) { @@ -170,7 +177,12 @@ public: bool add_column(SQL_Error& error, const char* str, size_t ) { try { - columns+=&STRING(str); + if(columns_count){ + // another query in multi_statements mode + columns=new ArrayString; + columns_count=0; + } + *columns+=&STRING(str); return false; } catch(...) { error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_column"); @@ -179,25 +191,25 @@ 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; } - if(columns.count()==1) { + 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] and $.sparse(true)."); + if(columns_count>2){ + error=SQL_Error("only 2 columns allowed for $.type[string] and $.sparse(true)"); return true; } break; } case C_TABLE: { // create empty table which we'll copy later - empty=new Table(&columns); - columns_count=columns.count(); + empty=new Table(columns); break; } } @@ -212,6 +224,12 @@ public: bool add_row_cell(SQL_Error& error, const char *str, size_t ) { try { + 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) { size_t index=str ? pa_atoui(str) : 0; @@ -248,6 +266,7 @@ public: ArrayString* row=new ArrayString(columns_count); *row+=&STRING(str); *vtable->get_table()+=row; + row_value=(Value*)row; break; } } @@ -255,7 +274,7 @@ public: const String& cell=STRING(str); 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: { @@ -292,7 +311,7 @@ class Array_sql_event_handlers: public S ArrayValue& result; Value* row_value; int column_index; - ArrayString& columns; + ArrayString* columns; Table2hash_value_type value_type; int columns_count; public: @@ -302,14 +321,19 @@ public: result(aresult), row_value(0), column_index(0), - columns(*new ArrayString), + columns(new ArrayString), value_type(avalue_type), empty(0) { } bool add_column(SQL_Error& error, const char* str, size_t ) { try { - columns+=&STRING(str); + if(columns_count){ + // another query in multi_statements mode + columns=new ArrayString; + columns_count=0; + } + *columns+=&STRING(str); return false; } catch(...) { error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_column"); @@ -318,22 +342,22 @@ 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()>1){ - error=SQL_Error("only one column allowed for $.type[string]."); + if(columns_count>1){ + error=SQL_Error("only one column allowed for $.type[string]"); return true; } break; } case C_TABLE: { // create empty table which we'll copy later - empty=new Table(&columns); - columns_count=columns.count(); + empty=new Table(columns); break; } } @@ -347,6 +371,12 @@ public: bool add_row_cell(SQL_Error& error, const char *str, size_t ) { try { + if(column_index==columns_count){ + // should never happen, buggy driver case + error=SQL_Error("columns index exceed the columns count"); + return true; + } + if(column_index==0) { switch(value_type){ case C_HASH: { @@ -365,10 +395,10 @@ public: // creating table of same structure as source Table::Action_options table_options(0, 0); VTable* vtable=new VTable(new Table(*empty, table_options/*no rows, just structure*/)); - result+=vtable; - ArrayString* row=new ArrayString(columns_count); *vtable->get_table()+=row; + row_value=(Value*)row; + result+=vtable; break; } } @@ -377,7 +407,7 @@ public: const String& cell=STRING(str); 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: { @@ -422,6 +452,7 @@ static void _sql(Request& r, MethodParam if(params.count()>1) if(HashStringValue* options=params.as_hash(1, "sql options")) { int valid_options=0; + bool distinct_specified=false; for(HashStringValue::Iterator i(*options); i; i.next() ){ String::Body key=i.key(); Value* value=i.value(); @@ -436,17 +467,20 @@ static void _sql(Request& r, MethodParam valid_options++; } else if (key == sql_distinct_name) { distinct=r.process(*value).as_bool(); + distinct_specified=true; valid_options++; } else if (key == sql_value_type_name) { - sparse=r.process(*value).as_bool(); + value_type=get_value_type(r.process(*value)); valid_options++; } else if (key == "sparse") { - value_type=get_value_type(r.process(*value)); + sparse=r.process(*value).as_bool(); valid_options++; } } if(valid_options!=options->count()) throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); + if(distinct_specified && !sparse) + throw Exception(PARSER_RUNTIME, 0, "'distinct' option can only be used when $.sparse(true) is specified"); } SQL_Driver::Placeholder* placeholders=0; @@ -458,7 +492,7 @@ static void _sql(Request& r, MethodParam const char* statement_cstr=statement_string.untaint_cstr(String::L_SQL, r.connection()); VArray& self=GET_SELF(r, VArray); - + self.array().clear(); self.invalidate(); // just in case if called as method if(sparse){ @@ -832,7 +866,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; @@ -1065,7 +1099,7 @@ MArray::MArray(): Methoded(VARRAY_TYPE) #ifdef FEATURE_GET_ELEMENT4CALL // aliases without "_" add_native_method("keys", Method::CT_DYNAMIC, _keys, 0, 1); - add_native_method("count", Method::CT_DYNAMIC, _count, 0, 0); + add_native_method("count", Method::CT_DYNAMIC, _count, 0, 1); add_native_method("at", Method::CT_DYNAMIC, _at, 1, 2); #endif