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