Annotation of parser3/src/classes/array.C, revision 1.11

1.1       moko        1: /** @file
                      2:        Parser: @b array parser class.
                      3: 
                      4:        Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com)
                      5:        Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
                      6: */
                      7: 
                      8: #include "classes.h"
                      9: #include "pa_vmethod_frame.h"
                     10: 
                     11: #include "pa_request.h"
                     12: #include "pa_charsets.h"
                     13: #include "pa_varray.h"
                     14: #include "pa_vvoid.h"
                     15: #include "pa_sql_connection.h"
                     16: #include "pa_vtable.h"
                     17: #include "pa_vbool.h"
                     18: #include "pa_vmethod_frame.h"
                     19: 
1.11    ! moko       20: volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.10 2024/09/22 18:23:22 moko Exp $";
1.1       moko       21: 
                     22: // class
                     23: 
                     24: class MArray: public Methoded {
                     25: public: // VStateless_class
                     26:        Value* create_new_value(Pool&) { return new VArray; }
                     27: 
                     28: public:
                     29:        MArray();
                     30: };
                     31: 
                     32: // global variable
                     33: 
                     34: DECLARE_CLASS_VAR(array, new MArray);
                     35: 
1.5       moko       36: const char* const PARAM_ARRAY_OR_HASH = "param must be array or hash";
1.7       moko       37: const char* const PARAM_INDEX = "index must be integer";
1.5       moko       38: 
1.1       moko       39: // methods
                     40: 
                     41: static void _create_or_add(Request& r, MethodParams& params) {
                     42:        if(params.count()) {
1.5       moko       43:                Value& vsrc=params.as_no_junction(0, PARAM_ARRAY_OR_HASH);
1.1       moko       44:                VArray& self=GET_SELF(r, VArray);
                     45:                ArrayValue& self_array=self.array();
                     46: 
1.3       moko       47:                if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
1.6       moko       48:                        if(src==&self)
                     49:                                throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array");
1.5       moko       50:                        self_array.append(src->array());
1.1       moko       51:                } else {
                     52:                        HashStringValue* src_hash=vsrc.get_hash();
1.5       moko       53:                        if(!src_hash)
                     54:                                return;
                     55:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
1.7       moko       56:                                self_array.put(VArray::index(i.key()), i.value());
1.5       moko       57:                        }
1.1       moko       58:                }
1.4       moko       59:                self.invalidate();
1.1       moko       60:        }
                     61: }
                     62: 
1.6       moko       63: static ArrayValue::Action_options get_action_options(Request& r, MethodParams& params, size_t options_index) {
                     64:        ArrayValue::Action_options result;
                     65:        if(params.count() <= options_index)
                     66:                return result;
                     67: 
                     68:        HashStringValue* options=params.as_hash(options_index);
                     69:        if(!options)
                     70:                return result;
                     71: 
                     72:        result.defined=true;
                     73:        int valid_options=0;
                     74: 
                     75:        if(Value* voffset=options->get(sql_offset_name)) {
                     76:                valid_options++;
1.8       moko       77:                int offset=r.process(*voffset).as_int();
                     78:                result.offset=offset < 0 ? 0 : offset;
1.6       moko       79:        }
                     80:        if(Value* vlimit=options->get(sql_limit_name)) {
                     81:                valid_options++;
1.8       moko       82:                int limit=r.process(*vlimit).as_int();
                     83:                result.limit=limit < 0 ? 0: limit;
1.6       moko       84:        }
                     85: 
                     86:        if(valid_options!=options->count())
                     87:                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                     88: 
                     89:        return result;
                     90: }
                     91: 
                     92: static void _join(Request& r, MethodParams& params) {
1.7       moko       93:        Value& vsrc=params.as_no_junction(0, PARAM_ARRAY_OR_HASH);
1.6       moko       94:        ArrayValue::Action_options o=get_action_options(r, params, 1);
                     95: 
                     96:        VArray& self=GET_SELF(r, VArray);
                     97:        ArrayValue& self_array=self.array();
                     98: 
                     99:        if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
                    100:                if(src==&self)
                    101:                        throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array");
                    102: 
                    103:                if(o.defined){
                    104:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
                    105:                                if(i.value()){
                    106:                                        if(o.offset > 0){
                    107:                                                o.offset--;
                    108:                                                continue;
                    109:                                        }
                    110:                                        if(o.limit-- == 0)
                    111:                                                break;
                    112:                                        self_array+=i.value();
                    113:                                }
                    114:                        }
                    115:                } else {
                    116:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
                    117:                                if(i.value())
                    118:                                        self_array+=i.value();
                    119:                        }
                    120:                }
1.7       moko      121:        } else {
                    122:                HashStringValue* src_hash=vsrc.get_hash();
                    123:                if(!src_hash)
                    124:                        return;
                    125:                if(o.defined){
                    126:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
                    127:                                if(o.offset > 0){
                    128:                                        o.offset--;
                    129:                                        continue;
                    130:                                }
                    131:                                if(o.limit-- == 0)
                    132:                                        break;
                    133:                                self_array+=i.value();
                    134:                        }
                    135:                } else {
                    136:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
                    137:                                self_array+=i.value();
                    138:                        }
                    139:                }
                    140:        }
                    141:        self.invalidate();
1.6       moko      142: }
                    143: 
1.11    ! moko      144: #ifndef DOXYGEN
        !           145: 
        !           146: #define STRING(str) ((str) ? *new String(str, String::L_TAINTED /* no length as 0x00 can be inside */) : String::Empty)
        !           147: 
        !           148: class SparseArray_sql_event_handlers: public SQL_Driver_query_event_handlers {
        !           149:        bool distinct;
        !           150:        ArrayValue& result;
        !           151:        Value* row_value;
        !           152:        int column_index;
        !           153:        ArrayString& columns;
        !           154:        bool one_bool_column;
        !           155:        Table2hash_value_type value_type;
        !           156:        int columns_count;
        !           157: public:
        !           158:        Table* empty;
        !           159: public:
        !           160:        SparseArray_sql_event_handlers(bool adistinct, ArrayValue& aresult, Table2hash_value_type avalue_type):
        !           161:                distinct(adistinct),
        !           162:                result(aresult),
        !           163:                row_value(0),
        !           164:                column_index(0),
        !           165:                columns(*new ArrayString),
        !           166:                one_bool_column(false),
        !           167:                value_type(avalue_type),
        !           168:                empty(0) {
        !           169:        }
        !           170: 
        !           171:        bool add_column(SQL_Error& error, const char* str, size_t ) {
        !           172:                try {
        !           173:                        columns+=&STRING(str);
        !           174:                        return false;
        !           175:                } catch(...) {
        !           176:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_column");
        !           177:                        return true;
        !           178:                }
        !           179:        }
        !           180: 
        !           181:        bool before_rows(SQL_Error& error) {
        !           182:                if(columns.count()<1) {
        !           183:                        error=SQL_Error("no columns");
        !           184:                        return true;
        !           185:                }
        !           186:                if(columns.count()==1) {
        !           187:                        one_bool_column=true;
        !           188:                } else {
        !           189:                        switch(value_type){
        !           190:                                case C_STRING: {
        !           191:                                        if(columns.count()>2){
        !           192:                                                error=SQL_Error("only 2 columns allowed for $.type[string] and $.sparse(true).");
        !           193:                                                return true;
        !           194:                                        }
        !           195:                                        break;
        !           196:                                }
        !           197:                                case C_TABLE: {
        !           198:                                        // create empty table which we'll copy later
        !           199:                                        empty=new Table(&columns);
        !           200:                                        columns_count=columns.count();
        !           201:                                        break;
        !           202:                                }
        !           203:                        }
        !           204:                }
        !           205:                return false;
        !           206:        }
        !           207: 
        !           208:        bool add_row(SQL_Error& /*error*/) {
        !           209:                column_index=0;
        !           210:                return false;
        !           211:        }
        !           212: 
        !           213:        bool add_row_cell(SQL_Error& error, const char *str, size_t ) {
        !           214:                try {
        !           215:                        bool duplicate=false;
        !           216:                        if(one_bool_column) {
        !           217:                                size_t index=str ? pa_atoui(str) : 0;
        !           218:                                duplicate=result.put_dont_replace(index, &VBool::get(true));  // put. existed?
        !           219:                        } else if(column_index==0) {
        !           220:                                size_t index=str ? pa_atoui(str) : 0;
        !           221:                                switch(value_type){
        !           222:                                        case C_HASH: {
        !           223:                                                VHash* row_vhash=new VHash;
        !           224:                                                row_value=row_vhash;
        !           225:                                                duplicate=result.put_dont_replace(index, row_vhash); // put. existed?
        !           226:                                                break;
        !           227:                                        }
        !           228:                                        case C_STRING: {
        !           229:                                                VString* row_vstring=new VString();
        !           230:                                                row_value=row_vstring;
        !           231:                                                duplicate=result.put_dont_replace(index, row_vstring);  // put. existed?
        !           232:                                                break;
        !           233:                                        }
        !           234:                                        case C_TABLE: {
        !           235:                                                VTable* vtable=(VTable*)result.get(index);
        !           236: 
        !           237:                                                if(vtable) { // table with this key exist?
        !           238:                                                        if(!distinct) {
        !           239:                                                                duplicate=true;
        !           240:                                                                break;
        !           241:                                                        }
        !           242:                                                } else {
        !           243:                                                        // no? creating table of same structure as source
        !           244:                                                        Table::Action_options table_options(0, 0);
        !           245:                                                        vtable=new VTable(new Table(*empty, table_options/*no rows, just structure*/));
        !           246:                                                        result.put(index, vtable); // put
        !           247:                                                }
        !           248:                                                ArrayString* row=new ArrayString(columns_count);
        !           249:                                                *row+=&STRING(str);
        !           250:                                                *vtable->get_table()+=row;
        !           251:                                                break;
        !           252:                                        }
        !           253:                                }
        !           254:                        } else {
        !           255:                                const String& cell=STRING(str);
        !           256:                                switch(value_type) {
        !           257:                                        case C_HASH: {
        !           258:                                                row_value->get_hash()->put(*columns[column_index], new VString(cell));
        !           259:                                                break;
        !           260:                                        }
        !           261:                                        case C_STRING: {
        !           262:                                                VString* row_string=(VString*)row_value;
        !           263:                                                row_string->set_string(cell);
        !           264:                                                break;
        !           265:                                        }
        !           266:                                        case C_TABLE: {
        !           267:                                                ArrayString* row=(ArrayString*)row_value;
        !           268:                                                *row+=&cell;
        !           269:                                                break;
        !           270:                                        }
        !           271:                                }
        !           272:                        }
        !           273: 
        !           274:                        if(duplicate & !distinct) {
        !           275:                                error=SQL_Error("duplicate key");
        !           276:                                return true;
        !           277:                        }
        !           278: 
        !           279:                        column_index++;
        !           280:                        return false;
        !           281:                } catch(const Exception& e) {
        !           282:                        error=SQL_Error(e.type(), e.comment());
        !           283:                        return true;
        !           284:                } catch(...) {
        !           285:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_row_cell");
        !           286:                        return true;
        !           287:                }
        !           288:        }
        !           289: };
        !           290: 
        !           291: class Array_sql_event_handlers: public SQL_Driver_query_event_handlers {
        !           292:        ArrayValue& result;
        !           293:        Value* row_value;
        !           294:        int column_index;
        !           295:        ArrayString& columns;
        !           296:        Table2hash_value_type value_type;
        !           297:        int columns_count;
        !           298: public:
        !           299:        Table* empty;
        !           300: public:
        !           301:        Array_sql_event_handlers(ArrayValue& aresult, Table2hash_value_type avalue_type):
        !           302:                result(aresult),
        !           303:                row_value(0),
        !           304:                column_index(0),
        !           305:                columns(*new ArrayString),
        !           306:                value_type(avalue_type),
        !           307:                empty(0) {
        !           308:        }
        !           309: 
        !           310:        bool add_column(SQL_Error& error, const char* str, size_t ) {
        !           311:                try {
        !           312:                        columns+=&STRING(str);
        !           313:                        return false;
        !           314:                } catch(...) {
        !           315:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_column");
        !           316:                        return true;
        !           317:                }
        !           318:        }
        !           319: 
        !           320:        bool before_rows(SQL_Error& error) {
        !           321:                if(columns.count()<1) {
        !           322:                        error=SQL_Error("no columns");
        !           323:                        return true;
        !           324:                }
        !           325:                switch(value_type){
        !           326:                        case C_STRING: {
        !           327:                                if(columns.count()>1){
        !           328:                                        error=SQL_Error("only one column allowed for $.type[string].");
        !           329:                                        return true;
        !           330:                                }
        !           331:                                break;
        !           332:                        }
        !           333:                        case C_TABLE: {
        !           334:                                // create empty table which we'll copy later
        !           335:                                empty=new Table(&columns);
        !           336:                                columns_count=columns.count();
        !           337:                                break;
        !           338:                        }
        !           339:                }
        !           340:                return false;
        !           341:        }
        !           342: 
        !           343:        bool add_row(SQL_Error& /*error*/) {
        !           344:                column_index=0;
        !           345:                return false;
        !           346:        }
        !           347: 
        !           348:        bool add_row_cell(SQL_Error& error, const char *str, size_t ) {
        !           349:                try {
        !           350:                        if(column_index==0) {
        !           351:                                switch(value_type){
        !           352:                                        case C_HASH: {
        !           353:                                                VHash* row_vhash=new VHash;
        !           354:                                                row_value=row_vhash;
        !           355:                                                result+=row_vhash;
        !           356:                                                break;
        !           357:                                        }
        !           358:                                        case C_STRING: {
        !           359:                                                VString* row_vstring=new VString();
        !           360:                                                row_value=row_vstring;
        !           361:                                                result+=row_vstring;
        !           362:                                                break;
        !           363:                                        }
        !           364:                                        case C_TABLE: {
        !           365:                                                // creating table of same structure as source
        !           366:                                                Table::Action_options table_options(0, 0);
        !           367:                                                VTable* vtable=new VTable(new Table(*empty, table_options/*no rows, just structure*/));
        !           368:                                                result+=vtable;
        !           369: 
        !           370:                                                ArrayString* row=new ArrayString(columns_count);
        !           371:                                                *vtable->get_table()+=row;
        !           372:                                                break;
        !           373:                                        }
        !           374:                                }
        !           375:                        }
        !           376: 
        !           377:                        const String& cell=STRING(str);
        !           378:                        switch(value_type) {
        !           379:                                case C_HASH: {
        !           380:                                        row_value->get_hash()->put(*columns[column_index], new VString(cell));
        !           381:                                        break;
        !           382:                                }
        !           383:                                case C_STRING: {
        !           384:                                        VString* row_string=(VString*)row_value;
        !           385:                                        row_string->set_string(cell);
        !           386:                                        break;
        !           387:                                }
        !           388:                                case C_TABLE: {
        !           389:                                        ArrayString* row=(ArrayString*)row_value;
        !           390:                                        *row+=&cell;
        !           391:                                        break;
        !           392:                                }
        !           393:                        }
        !           394: 
        !           395:                        column_index++;
        !           396:                        return false;
        !           397:                } catch(const Exception& e) {
        !           398:                        error=SQL_Error(e.type(), e.comment());
        !           399:                        return true;
        !           400:                } catch(...) {
        !           401:                        error=SQL_Error("exception occurred in Hash_sql_event_handlers::add_row_cell");
        !           402:                        return true;
        !           403:                }
        !           404:        }
        !           405: };
        !           406: 
        !           407: #endif
        !           408: 
        !           409: extern Table2hash_value_type get_value_type(Value& vvalue_type);
        !           410: extern int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders);
        !           411: extern void unmarshal_bind_updates(HashStringValue& hash, int placeholder_count, SQL_Driver::Placeholder* placeholders);
        !           412: 
        !           413: static void _sql(Request& r, MethodParams& params) {
        !           414:        Value& statement=params.as_junction(0, "statement must be code");
        !           415: 
        !           416:        HashStringValue* bind=0;
        !           417:        ulong limit=SQL_NO_LIMIT;
        !           418:        ulong offset=0;
        !           419:        bool distinct=false;
        !           420:        bool sparse=false;
        !           421:        Table2hash_value_type value_type=C_HASH;
        !           422:        if(params.count()>1)
        !           423:                if(HashStringValue* options=params.as_hash(1, "sql options")) {
        !           424:                        int valid_options=0;
        !           425:                        for(HashStringValue::Iterator i(*options); i; i.next() ){
        !           426:                                String::Body key=i.key();
        !           427:                                Value* value=i.value();
        !           428:                                if(key == sql_bind_name) {
        !           429:                                        bind=value->get_hash();
        !           430:                                        valid_options++;
        !           431:                                } else if(key == sql_limit_name) {
        !           432:                                        limit=(ulong)r.process(*value).as_double();
        !           433:                                        valid_options++;
        !           434:                                } else if(key == sql_offset_name) {
        !           435:                                        offset=(ulong)r.process(*value).as_double();
        !           436:                                        valid_options++;
        !           437:                                } else if (key == sql_distinct_name) {
        !           438:                                        distinct=r.process(*value).as_bool();
        !           439:                                        valid_options++;
        !           440:                                } else if (key == sql_value_type_name) {
        !           441:                                        sparse=r.process(*value).as_bool();
        !           442:                                        valid_options++;
        !           443:                                } else if (key == "sparse") {
        !           444:                                        value_type=get_value_type(r.process(*value));
        !           445:                                        valid_options++;
        !           446:                                }
        !           447:                        }
        !           448:                        if(valid_options!=options->count())
        !           449:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
        !           450:                }
        !           451: 
        !           452:        SQL_Driver::Placeholder* placeholders=0;
        !           453:        uint placeholders_count=0;
        !           454:        if(bind)
        !           455:                placeholders_count=marshal_binds(*bind, placeholders);
        !           456: 
        !           457:        const String& statement_string=r.process_to_string(statement);
        !           458:        const char* statement_cstr=statement_string.untaint_cstr(String::L_SQL, r.connection());
        !           459: 
        !           460:        VArray& self=GET_SELF(r, VArray);
        !           461:        
        !           462:        self.array().clear(); self.invalidate(); // just in case if called as method
        !           463: 
        !           464:        if(sparse){
        !           465:                SparseArray_sql_event_handlers handlers(distinct, self.array(), value_type);
        !           466:                r.connection()->query(statement_cstr, placeholders_count, placeholders, offset, limit, handlers, statement_string);
        !           467:        } else {
        !           468:                Array_sql_event_handlers handlers(self.array(), value_type);
        !           469:                r.connection()->query(statement_cstr, placeholders_count, placeholders, offset, limit, handlers, statement_string);
        !           470:        }
        !           471: 
        !           472:        if(bind)
        !           473:                unmarshal_bind_updates(*bind, placeholders_count, placeholders);
        !           474: }
        !           475: 
1.1       moko      476: 
                    477: 
1.10      moko      478: static void mid(Request& r, size_t offset=0, size_t limit=ARRAY_OPTION_LIMIT_ALL) {
1.9       moko      479:        ArrayValue& array=GET_SELF(r, VArray).array();
1.10      moko      480:        if(limit>0){
1.9       moko      481:                VArray *result=new VArray;
                    482:                ArrayValue& result_array=result->array();
                    483:                for(ArrayValue::Iterator i(array); i; i.next()){
                    484:                        if(i.value()){
1.10      moko      485:                                if(offset > 0){
                    486:                                        offset--;
1.9       moko      487:                                        continue;
                    488:                                }
1.10      moko      489:                                if(limit-- == 0)
1.9       moko      490:                                        break;
                    491:                                result_array+=i.value();
                    492:                        }
                    493:                }
                    494:                r.write(*result);
                    495:        } else {
                    496:                r.write(*new VArray);
                    497:        }
                    498: }
1.1       moko      499: 
1.9       moko      500: static void _left(Request& r, MethodParams& params) {
                    501:        int sn=params.as_int(0, "n must be int", r);
1.10      moko      502:        mid(r, 0, sn < 0 ? 0 : sn);
1.9       moko      503: }
                    504: 
                    505: static void _right(Request& r, MethodParams& params) {
                    506:        int sn=params.as_int(0, "n must be int", r);
                    507: 
                    508:        if(sn>0){
                    509:                size_t used=GET_SELF(r, VArray).array().used();
                    510:                if(sn<used){
1.10      moko      511:                        mid(r, used-sn, sn);
1.9       moko      512:                } else {
1.10      moko      513:                        mid(r);
1.9       moko      514:                }
                    515:        } else {
1.10      moko      516:                mid(r, 0, 0);
1.9       moko      517:        }
                    518: }
                    519: 
                    520: static void _mid(Request& r, MethodParams& params) {
                    521:        const String& string=GET_SELF(r, VString).string();
                    522: 
                    523:        int begin=params.as_int(0, "p must be int", r);
                    524:        if(begin<0)
                    525:                throw Exception(PARSER_RUNTIME, 0,  "p(%d) must be >=0", begin);
                    526: 
                    527:        size_t end;
                    528:        size_t length=0;
1.1       moko      529: 
1.9       moko      530:        if(params.count()>1) {
                    531:                int n=params.as_int(1, "n must be int", r);
                    532:                if(n<0)
                    533:                        throw Exception(PARSER_RUNTIME, 0, "n(%d) must be >=0", n);
1.10      moko      534:                mid(r, begin, n);
1.9       moko      535:        } else {
1.10      moko      536:                mid(r, begin);
1.9       moko      537:        }
                    538: }
1.1       moko      539: 
                    540: static void _keys(Request& r, MethodParams& params) {
                    541:        const String* keys_column_name;
                    542:        if(params.count()>0)
                    543:                keys_column_name=&params.as_string(0, COLUMN_NAME_MUST_BE_STRING);
                    544:        else 
                    545:                keys_column_name=new String("key");
                    546: 
                    547:        Table::columns_type columns(new ArrayString(1));
                    548:        *columns+=keys_column_name;
                    549:        Table* table=new Table(columns);
                    550: 
                    551:        ArrayValue& array=GET_SELF(r, VArray).array();
                    552:        for(ArrayValue::Iterator i(array); i; i.next()){
                    553:                if(i.value()){
                    554:                        Table::element_type row(new ArrayString(1));
                    555:                        *row+=new String(i.key(), String::L_TAINTED);
                    556:                        *table+=row;
                    557:                }
                    558:        }
                    559: 
                    560:        r.write(*new VTable(table));
                    561: }
                    562: 
1.5       moko      563: static void _count(Request& r, MethodParams& params) {
                    564:        ArrayValue& array=GET_SELF(r, VArray).array();
                    565:        if(params.count()>0){
                    566:                const String& what=params.as_string(0, PARAMETER_MUST_BE_STRING);
                    567:                if(!what.is_empty()){
                    568:                        if(what != "all")
                    569:                                throw Exception(PARSER_RUNTIME, &what, "param must be empty or 'all'");
                    570:                        return r.write(*new VInt(array.count()));
                    571:                }
                    572:        }
                    573:        r.write(*new VInt(array.used()));
1.1       moko      574: }
                    575: 
1.2       moko      576: static void _append(Request& r, MethodParams& params) {
                    577:        VArray& self=GET_SELF(r, VArray);
                    578:        ArrayValue& array=self.array();
                    579: 
                    580:        int count=params.count();
                    581: 
                    582:        for(int i=0; i<count; i++){
                    583:                array+=&r.process(params[i]);
                    584:        }
1.4       moko      585:        self.invalidate();
1.2       moko      586: }
                    587: 
                    588: static void _insert(Request& r, MethodParams& params) {
                    589:        VArray& self=GET_SELF(r, VArray);
                    590:        ArrayValue& array=self.array();
                    591: 
                    592:        int count=params.count();
1.7       moko      593:        size_t index=VArray::index(params.as_int(0, PARAM_INDEX, r));
1.2       moko      594: 
                    595:        for(int i=1; i<count; i++){
1.8       moko      596:                array.insert(index++, &r.process(params[i]));
1.2       moko      597:        }
1.4       moko      598:        self.invalidate();
1.2       moko      599: }
                    600: 
1.1       moko      601: static void _delete(Request& r, MethodParams& params) {
1.7       moko      602:        VArray& self=GET_SELF(r, VArray);
1.1       moko      603:        if(params.count()>0)
1.7       moko      604:                self.array().clear(VArray::index(params.as_int(0, PARAM_INDEX, r)));
1.1       moko      605:        else
1.7       moko      606:                self.array().clear();
                    607:        self.invalidate();
                    608: }
                    609: 
                    610: static void _remove(Request& r, MethodParams& params) {
                    611:        VArray& self=GET_SELF(r, VArray);
                    612:        self.array().remove(VArray::index(params.as_int(0, PARAM_INDEX, r)));
                    613:        self.invalidate();
1.1       moko      614: }
                    615: 
                    616: static void _contains(Request& r, MethodParams& params) {
                    617:        VArray& self=GET_SELF(r, VArray);
1.7       moko      618:        bool result=self.contains(VArray::index(params.as_int(0, PARAM_INDEX, r)));
1.1       moko      619:        r.write(VBool::get(result));
                    620: }
                    621: 
1.5       moko      622: static void _for(Request& r, MethodParams& params) {
                    623:        InCycle temp(r);
                    624: 
1.7       moko      625:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    626:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    627:        Value* body_code=&params.as_junction(2, "body must be code");
                    628:        Value* delim_maybe_code=params.count()>3 ? &params[3] : 0;
1.5       moko      629:        Value& caller=*r.get_method_frame()->caller();
                    630: 
1.7       moko      631:        if(key_var_name->is_empty()) key_var_name=0;
1.5       moko      632:        if(value_var_name->is_empty()) value_var_name=0;
                    633: 
                    634:        ArrayValue& array=GET_SELF(r, VArray).array();
                    635: 
                    636:        if(delim_maybe_code){ // delimiter set
                    637:                bool need_delim=false;
                    638:                for(ArrayValue::Iterator i(array); i; i.next()){
1.7       moko      639:                        if(key_var_name){
                    640:                                VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    641:                                r.put_element(caller, *key_var_name, vkey);
                    642:                        }
                    643: 
1.5       moko      644:                        if(value_var_name)
                    645:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    646: 
                    647:                        Value& sv_processed=r.process(*body_code);
                    648:                        TempSkip4Delimiter skip(r);
                    649: 
                    650:                        const String* s_processed=sv_processed.get_string();
                    651:                        if(s_processed && !s_processed->is_empty()) { // we have body
                    652:                                if(need_delim) // need delim & iteration produced string?
                    653:                                        r.write(r.process(*delim_maybe_code));
                    654:                                else
                    655:                                        need_delim=true;
                    656:                        }
                    657: 
                    658:                        r.write(sv_processed);
                    659: 
                    660:                        if(skip.check_break())
                    661:                                break;
                    662:                }
                    663:        } else {
                    664:                for(ArrayValue::Iterator i(array); i; i.next()){
1.7       moko      665:                        if(key_var_name){
                    666:                                VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    667:                                r.put_element(caller, *key_var_name, vkey);
                    668:                        }
                    669: 
1.5       moko      670:                        if(value_var_name)
                    671:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    672: 
                    673:                        r.process_write(*body_code);
                    674: 
                    675:                        if(r.check_skip_break())
                    676:                                break;
                    677:                }
                    678:        }
                    679: }
                    680: 
1.1       moko      681: static void _foreach(Request& r, MethodParams& params) {
                    682:        InCycle temp(r);
                    683: 
                    684:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    685:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    686:        Value* body_code=&params.as_junction(2, "body must be code");
1.7       moko      687:        Value* delim_maybe_code=params.count()>3 ? &params[3] : 0;
1.1       moko      688:        Value& caller=*r.get_method_frame()->caller();
                    689: 
                    690:        if(key_var_name->is_empty()) key_var_name=0;
                    691:        if(value_var_name->is_empty()) value_var_name=0;
                    692: 
                    693:        ArrayValue& array=GET_SELF(r, VArray).array();
                    694: 
                    695:        if(delim_maybe_code){ // delimiter set
                    696:                bool need_delim=false;
                    697:                for(ArrayValue::Iterator i(array); i; i.next()){
                    698:                        if(i.value()){
                    699:                                if(key_var_name){
                    700:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    701:                                        r.put_element(caller, *key_var_name, vkey);
                    702:                                }
                    703: 
                    704:                                if(value_var_name)
                    705:                                        r.put_element(caller, *value_var_name, i.value());
                    706: 
                    707:                                Value& sv_processed=r.process(*body_code);
                    708:                                TempSkip4Delimiter skip(r);
                    709: 
                    710:                                const String* s_processed=sv_processed.get_string();
                    711:                                if(s_processed && !s_processed->is_empty()) { // we have body
                    712:                                        if(need_delim) // need delim & iteration produced string?
                    713:                                                r.write(r.process(*delim_maybe_code));
                    714:                                        else
                    715:                                                need_delim=true;
                    716:                                }
                    717: 
                    718:                                r.write(sv_processed);
                    719: 
                    720:                                if(skip.check_break())
                    721:                                        break;
                    722:                        }
                    723:                }
                    724:        } else {
                    725:                for(ArrayValue::Iterator i(array); i; i.next()){
                    726:                        if(i.value()){
                    727:                                if(key_var_name){
                    728:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    729:                                        r.put_element(caller, *key_var_name, vkey);
                    730:                                }
                    731: 
                    732:                                if(value_var_name)
                    733:                                        r.put_element(caller, *value_var_name, i.value());
                    734: 
                    735:                                r.process_write(*body_code);
                    736: 
                    737:                                if(r.check_skip_break())
                    738:                                        break;
                    739:                        }
                    740:                }
                    741:        }
                    742: }
                    743: 
                    744: #ifndef DOXYGEN
                    745: struct Array_seq_item : public PA_Allocated {
                    746:        Value *array_data;
                    747:        union {
                    748:                const char *c_str;
                    749:                double d;
                    750:        } value;
                    751: };
                    752: #endif
                    753: 
                    754: static int sort_cmp_string(const void *a, const void *b) {
                    755:        return strcmp(
                    756:                static_cast<const Array_seq_item *>(a)->value.c_str,
                    757:                static_cast<const Array_seq_item *>(b)->value.c_str
                    758:        );
                    759: }
                    760: static int sort_cmp_double(const void *a, const void *b) {
                    761:        double va=static_cast<const Array_seq_item *>(a)->value.d;
                    762:        double vb=static_cast<const Array_seq_item *>(b)->value.d;
                    763:        if(va<vb)
                    764:                return -1;
                    765:        else if(va>vb)
                    766:                return +1;
                    767:        else 
                    768:                return 0;
                    769: }
                    770: 
                    771: static void _sort(Request& r, MethodParams& params){
                    772:        const String& key_var_name=params.as_string(0, "key-var name must be string");
                    773:        const String& value_var_name=params.as_string(1, "value-var name must be string");
                    774:        Value& key_maker=params.as_junction(2, "key-maker must be code");
                    775:        bool reverse=params.count()>3 && params.as_no_junction(3, "order must not be code").as_string()=="desc"; // default=asc
                    776: 
                    777:        const String* key_var=key_var_name.is_empty()? 0 : &key_var_name;
                    778:        const String* value_var=value_var_name.is_empty()? 0 : &value_var_name;
                    779:        VMethodFrame* context=r.get_method_frame()->caller();
                    780: 
                    781:        VArray& self=GET_SELF(r, VArray);
                    782:        ArrayValue& array=self.array();
1.4       moko      783:        int count=array.used(); // not array.count()
1.1       moko      784: 
                    785:        Array_seq_item* seq=new Array_seq_item[count];
                    786:        int pos=0;
                    787:        bool key_values_are_strings=true;
                    788: 
                    789:        for(ArrayValue::Iterator i(array); i; i.next() ){
                    790:                if(i.value()){
                    791:                        if(key_var)
                    792:                                r.put_element(*context, *key_var, new VString(*new String(i.key(), String::L_TAINTED)));
                    793:                        if(value_var)
                    794:                                r.put_element(*context, *value_var, i.value());
                    795: 
                    796:                        Value& value=r.process(key_maker);
                    797:                        if(pos==0) // determining key values type by first one
                    798:                                key_values_are_strings=value.is_string();
                    799: 
                    800:                        seq[pos].array_data=i.value();
                    801:                        if(key_values_are_strings)
                    802:                                seq[pos++].value.c_str=value.as_string().cstr();
                    803:                        else
                    804:                                seq[pos++].value.d=value.as_expr_result().as_double();
                    805:                }
                    806:        }
                    807: 
                    808:        // @todo: handle this elsewhere
                    809:        if(r.charsets.source().NAME()=="KOI8-R" && key_values_are_strings)
                    810:                for(pos=0; pos<count; pos++)
                    811:                        if(*seq[pos].value.c_str)
                    812:                                seq[pos].value.c_str=Charset::transcode(seq[pos].value.c_str, r.charsets.source(), pa_UTF8_charset).cstr();
                    813: 
                    814:        // sort keys
                    815:        qsort(seq, count, sizeof(Array_seq_item), key_values_are_strings ? sort_cmp_string : sort_cmp_double);
                    816: 
                    817:        // reorder array as required in 'seq'
                    818:        array.clear();
                    819:        if(reverse)
                    820:                for(pos=count-1; pos>=0; pos--)
                    821:                        array+=seq[pos].array_data;
                    822:        else
                    823:                for(pos=0; pos<count; pos++)
                    824:                        array+=seq[pos].array_data;
                    825: 
                    826:        delete[] seq;
                    827: }
                    828: 
1.5       moko      829: enum AtResultType {
                    830:        AtResultTypeValue = 0,
                    831:        AtResultTypeKey = 1,
                    832:        AtResultTypeHash = 2
                    833: };
                    834: 
                    835: inline Value& SingleElementHash(String::Body akey, Value* avalue) {
                    836:        Value& result=*new VHash;
                    837:        result.put_element(*new String(akey, String::L_TAINTED), avalue);
                    838:        return result;
                    839: }
                    840: 
1.1       moko      841: static void _at(Request& r, MethodParams& params) {
                    842:        VArray& self=GET_SELF(r, VArray);
                    843:        ArrayValue& array=self.array();
1.5       moko      844:        size_t count=array.used(); // not array.count()
1.1       moko      845: 
                    846:        int pos=0;
                    847: 
                    848:        AtResultType result_type=AtResultTypeValue;
                    849:        if(params.count() > 1) {
                    850:                const String& stype=params.as_string(1, "type must be string");
                    851:                if(stype == "key")
                    852:                        result_type=AtResultTypeKey;
                    853:                else if(stype == "hash")
                    854:                        result_type=AtResultTypeHash;
                    855:                else if(stype != "value")
                    856:                        throw Exception(PARSER_RUNTIME, &stype, "type must be 'key', 'value' or 'hash'");
                    857:        }
                    858: 
                    859:        Value& vwhence=params[0];
                    860:        if(vwhence.is_string()) {
                    861:                const String& swhence=*vwhence.get_string();
                    862:                if(swhence == "last")
                    863:                        pos=count-1;
                    864:                else if(swhence != "first")
                    865:                        throw Exception(PARSER_RUNTIME, &swhence, "whence must be 'first', 'last' or expression");
                    866:        } else {
                    867:                pos=r.process(vwhence).as_int();
                    868:                if(pos < 0)
                    869:                        pos+=count;
                    870:        }
                    871: 
                    872:        if(count && pos >= 0 && (size_t)pos < count){
                    873:                switch(result_type) {
                    874:                        case AtResultTypeKey:
                    875:                                {
                    876:                                        for(ArrayValue::Iterator i(array); i; i.next() ){
                    877:                                                if(i.value() && !(pos--)){
                    878:                                                        r.write(*new VString(*new String(i.key(), String::L_TAINTED)));
                    879:                                                        break;
                    880:                                                }
                    881:                                        }
                    882:                                        break;
                    883:                                }
                    884:                        case AtResultTypeValue:
                    885:                                {
                    886:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    887:                                                if(i.value() &&!(pos--)){
                    888:                                                        r.write(*i.value());
                    889:                                                        break;
                    890:                                                }
                    891:                                        break;
                    892:                                }
                    893:                        case AtResultTypeHash:
                    894:                                {
                    895:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    896:                                                if(i.value() &&!(pos--)){
                    897:                                                        r.write(SingleElementHash(i.key(), i.value()));
                    898:                                                        break;
                    899:                                                }
                    900:                                        break;
                    901:                                }
                    902:                }
                    903:        }
                    904: }
                    905: 
                    906: 
                    907: extern String table_reverse_name;
                    908: 
                    909: static void _select(Request& r, MethodParams& params) {
                    910:        InCycle temp(r);
                    911:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    912:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    913:        Value& vcondition=params.as_expression(2, "condition must be number, bool or expression");
                    914: 
                    915:        if(key_var_name->is_empty()) key_var_name=0;
                    916:        if(value_var_name->is_empty()) value_var_name=0;
                    917: 
                    918:        ArrayValue& source_array=GET_SELF(r, VArray).array();
                    919:        Value& caller=*r.get_method_frame()->caller();
                    920: 
                    921:        int limit=source_array.count();
                    922:        bool reverse=false;
                    923: 
                    924:        if(params.count()>3)
                    925:                if(HashStringValue* options=params.as_hash(3)) {
                    926:                        int valid_options=0;
                    927:                        if(Value* vlimit=options->get(sql_limit_name)) {
                    928:                                valid_options++;
                    929:                                limit=r.process(*vlimit).as_int();
                    930:                        }
                    931:                        if(Value* vreverse=options->get(table_reverse_name)) {
                    932:                                valid_options++;
                    933:                                reverse=r.process(*vreverse).as_bool();
                    934:                        }
                    935:                        if(valid_options!=options->count())
                    936:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                    937:                }
                    938: 
                    939:        VArray *result=new VArray;
                    940:        ArrayValue& result_array=result->array();
                    941: 
                    942:        if(limit>0){
                    943:                if(reverse){
                    944:                        for(ArrayValue::ReverseIterator i(source_array); i; ){
1.5       moko      945:                                if(Value *value=i.prev()){ // here for correct i.key()
                    946:                                        if(key_var_name)
                    947:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    948:                                        if(value_var_name)
                    949:                                                r.put_element(caller, *value_var_name, value);
1.1       moko      950: 
1.5       moko      951:                                        bool condition=r.process(vcondition).as_bool();
1.1       moko      952: 
1.5       moko      953:                                        if(r.check_skip_break())
                    954:                                                break;
1.1       moko      955: 
1.5       moko      956:                                        if(condition){
                    957:                                                result_array+=value;
                    958:                                                if(!--limit)
                    959:                                                        break;
                    960:                                        }
1.1       moko      961:                                }
                    962:                        }
                    963:                } else {
                    964:                        for(ArrayValue::Iterator i(source_array); i; i.next() ){
1.5       moko      965:                                if(Value *value=i.value()){
1.1       moko      966:                                        if(key_var_name)
                    967:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    968:                                        if(value_var_name)
                    969:                                                r.put_element(caller, *value_var_name, value);
                    970: 
                    971:                                        bool condition=r.process(vcondition).as_bool();
                    972: 
                    973:                                        if(r.check_skip_break())
                    974:                                                break;
                    975: 
                    976:                                        if(condition){
                    977:                                                result_array+=value;
                    978:                                                if(!--limit)
                    979:                                                        break;
                    980:                                        }
                    981:                                }
                    982:                        }
                    983:                }
                    984:        }
                    985: 
                    986:        r.write(*result);
                    987: }
                    988: 
                    989: static void _reverse(Request& r, MethodParams& params) {
1.5       moko      990:        ArrayValue& source_array=GET_SELF(r, VArray).array();
1.1       moko      991: 
1.5       moko      992:        VArray& result=*new VArray(source_array.count());
1.1       moko      993:        ArrayValue& result_array=result.array();
                    994: 
1.4       moko      995:        for(ArrayValue::ReverseIterator i(source_array); i; ){
                    996:                result_array+=i.prev();
1.1       moko      997:        }
                    998: 
                    999:        r.write(result);
                   1000: }
                   1001: 
                   1002: 
                   1003: // constructor
                   1004: 
                   1005: MArray::MArray(): Methoded(VARRAY_TYPE) {
                   1006: 
                   1007:        // ^array::create[[copy_from]]
                   1008:        add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
                   1009:        // ^array.add[add_from]
                   1010:        add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
1.6       moko     1011:        // ^array.join[join_from[;options]]
                   1012:        add_native_method("join", Method::CT_DYNAMIC, _join, 1, 2);
1.1       moko     1013: 
1.9       moko     1014:        // ^array.left(n)
                   1015:        add_native_method("left", Method::CT_DYNAMIC, _left, 1, 1);
                   1016:        // ^array.right(n)
                   1017:        add_native_method("right", Method::CT_DYNAMIC, _right, 1, 1);
                   1018:        // ^array.mid(p)
                   1019:        // ^array.mid(p;n)
                   1020:        add_native_method("mid", Method::CT_DYNAMIC, _mid, 1, 2);
1.1       moko     1021: 
1.8       moko     1022:        // ^array::new[value;value]
                   1023:        add_native_method("new", Method::CT_DYNAMIC, _append, 0, 10000);
1.2       moko     1024:        // ^array.append[value;value]
                   1025:        add_native_method("append", Method::CT_DYNAMIC, _append, 1, 10000);
                   1026:        // ^array.insert[index;value...]
                   1027:        add_native_method("insert", Method::CT_DYNAMIC, _insert, 2, 10000);
                   1028: 
                   1029:        // ^array.delete[index]
1.1       moko     1030:        add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1);
1.7       moko     1031:        // ^array.remove[index]
                   1032:        add_native_method("remove", Method::CT_DYNAMIC, _remove, 1, 1);
1.1       moko     1033: 
1.2       moko     1034:        // ^array.contains[index]
1.1       moko     1035:        add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
                   1036: 
                   1037:        // ^array::sql[query][options array]
                   1038:        add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
                   1039: 
                   1040:        // ^array._keys[[column name]]
                   1041:        add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
                   1042: 
1.5       moko     1043:        // ^array._count[[all]]
                   1044:        add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 1);
1.1       moko     1045: 
1.7       moko     1046:        // ^array.for[index;value]{code}[delim]
                   1047:        add_native_method("for", Method::CT_DYNAMIC, _for, 3, 3+1);
1.2       moko     1048:        // ^array.foreach[index;value]{code}[delim]
1.7       moko     1049:        add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 3, 3+1);
1.1       moko     1050: 
1.2       moko     1051:        // ^array.sort[index;value]{string-key-maker}[[asc|desc]]
                   1052:        // ^array.sort[index;value](numeric-key-maker)[[asc|desc]]
1.1       moko     1053:        add_native_method("sort", Method::CT_DYNAMIC, _sort, 3, 4);
                   1054: 
1.2       moko     1055:        // ^array.select[index;value](bool-condition)[options hash]
1.1       moko     1056:        add_native_method("select", Method::CT_DYNAMIC, _select, 3, 4);
                   1057: 
                   1058:        // ^array.reverse[]
                   1059:        add_native_method("reverse", Method::CT_DYNAMIC, _reverse, 0, 0);
                   1060: 
1.2       moko     1061:        // ^array._at[first|last[;'key'|'value'|'hash']]
                   1062:        // ^array._at([-+]offset)[['key'|'value'|'hash']]
1.1       moko     1063:        add_native_method("_at", Method::CT_DYNAMIC, _at, 1, 2);
                   1064: 
                   1065: #ifdef FEATURE_GET_ELEMENT4CALL
                   1066:        // aliases without "_"
                   1067:        add_native_method("keys", Method::CT_DYNAMIC, _keys, 0, 1);
                   1068:        add_native_method("count", Method::CT_DYNAMIC, _count, 0, 0);
                   1069:        add_native_method("at", Method::CT_DYNAMIC, _at, 1, 2);
                   1070: #endif
                   1071: 
                   1072: }

E-mail: