Annotation of parser3/src/classes/hash.C, revision 1.106
1.1 paf 1: /** @file
2: Parser: @b hash parser class.
3:
1.89 misha 4: Copyright (c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com)
1.35 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.41 paf 6: */
1.1 paf 7:
1.106 ! misha 8: static const char * const IDENT_HASH_C="$Date: 2010-08-04 15:11:44 $";
1.1 paf 9:
10: #include "classes.h"
1.57 paf 11: #include "pa_vmethod_frame.h"
12:
1.1 paf 13: #include "pa_request.h"
14: #include "pa_vhash.h"
1.6 parser 15: #include "pa_vvoid.h"
1.2 parser 16: #include "pa_sql_connection.h"
1.9 parser 17: #include "pa_vtable.h"
1.22 parser 18: #include "pa_vbool.h"
1.26 paf 19: #include "pa_vmethod_frame.h"
1.2 parser 20:
1.1 paf 21: // class
22:
1.57 paf 23: class MHash: public Methoded {
1.2 parser 24: public: // VStateless_class
1.103 misha 25: Value* create_new_value(Pool&) { return new VHash(); }
1.2 parser 26:
1.1 paf 27: public:
1.57 paf 28: MHash();
1.1 paf 29: public: // Methoded
1.2 parser 30: bool used_directly() { return true; }
1.1 paf 31: };
32:
1.57 paf 33: // global variable
34:
35: DECLARE_CLASS_VAR(hash, new MHash, 0);
36:
1.1 paf 37: // methods
38:
1.11 parser 39: #ifndef DOXYGEN
1.52 paf 40: class Hash_sql_event_handlers: public SQL_Driver_query_event_handlers {
1.57 paf 41: const String& statement_string; const char* statement_cstr;
42: bool distinct;
43: HashStringValue& rows_hash;
1.86 misha 44: Value* row_value;
1.57 paf 45: int column_index;
1.86 misha 46: ArrayString& columns;
47: bool one_bool_column;
1.67 paf 48: static VBool only_one_column_value;
1.86 misha 49: Table2hash_value_type value_type;
50: int columns_count;
51: public:
52: Table* empty;
1.11 parser 53: public:
1.57 paf 54: Hash_sql_event_handlers(
1.86 misha 55: const String& astatement_string,
56: const char* astatement_cstr,
1.49 paf 57: bool adistinct,
1.86 misha 58: HashStringValue& arows_hash,
59: Table2hash_value_type avalue_type)
60: :
61: statement_string(astatement_string),
62: statement_cstr(astatement_cstr),
1.49 paf 63: distinct(adistinct),
1.11 parser 64: rows_hash(arows_hash),
1.86 misha 65: value_type(avalue_type),
66: row_value(0),
1.67 paf 67: column_index(0),
1.86 misha 68: one_bool_column(false),
69: columns(*new ArrayString),
70: empty(0) {
1.11 parser 71: }
1.86 misha 72:
1.96 misha 73: bool add_column(SQL_Error& error, const char* str, size_t length) {
1.53 paf 74: try {
1.96 misha 75: columns+=new String(str, String::L_TAINTED, length);
1.53 paf 76: return false;
77: } catch(...) {
78: error=SQL_Error("exception occured in Hash_sql_event_handlers::add_column");
79: return true;
80: }
1.11 parser 81: }
1.86 misha 82:
1.53 paf 83: bool before_rows(SQL_Error& error) {
1.67 paf 84: if(columns.count()<1) {
1.79 misha 85: error=SQL_Error(PARSER_RUNTIME, "no columns");
1.53 paf 86: return true;
87: }
1.86 misha 88: switch(value_type){
89: case C_STRING: {
90: if(columns.count()>2){
91: error=SQL_Error(PARSER_RUNTIME, "only 2 columns allowed for $.type[string].");
92: return true;
93: }
94: }
95: case C_TABLE: {
96: // create empty table which we'll copy later
97: empty=new Table(&columns);
98: columns_count=columns.count();
99: }
100: case C_HASH: {
101: one_bool_column=columns.count()==1;
102: }
103: }
1.53 paf 104: return false;
1.11 parser 105: }
1.86 misha 106:
1.53 paf 107: bool add_row(SQL_Error& /*error*/) {
1.11 parser 108: column_index=0;
1.53 paf 109: return false;
1.11 parser 110: }
1.86 misha 111:
1.96 misha 112: bool add_row_cell(SQL_Error& error, const char *ptr, size_t length) {
1.53 paf 113: try {
1.96 misha 114: String& cell=*new String(ptr, String::L_TAINTED, length);
1.86 misha 115:
1.69 paf 116: bool duplicate=false;
1.86 misha 117: if(one_bool_column) {
1.69 paf 118: duplicate=rows_hash.put_dont_replace(cell, &only_one_column_value); // put. existed?
119: } else if(column_index==0) {
1.86 misha 120: switch(value_type){
121: case C_HASH: {
122: VHash* row_vhash=new VHash;
123: row_value=row_vhash;
124: duplicate=rows_hash.put_dont_replace(cell, row_vhash); // put. existed?
125: break;
126: }
127: case C_STRING: {
128: VString* row_vstring=new VString();
129: row_value=row_vstring;
130: duplicate=rows_hash.put_dont_replace(cell, row_vstring); // put. existed?
131: break;
132: }
133: case C_TABLE: {
134: VTable* vtable=(VTable*)rows_hash.get(cell);
135: Table* table;
136:
137: if(vtable) { // table with this key exist?
138: if(!distinct) {
139: duplicate=true;
140: break;
141: }
142: table=vtable->get_table();
143: } else {
144: // no? creating table of same structure as source
145: Table::Action_options table_options(0, 0);
146: table=new Table(*empty, table_options/*no rows, just structure*/);
147: vtable=new VTable(table);
148: rows_hash.put(cell, vtable); // put
149: }
150: ArrayString* row=new ArrayString(columns_count);
151: row_value=(Value*)row;
152: *row+=&cell;
153: *table+=row;
154: break;
155: }
156: }
157: } else {
158: switch(value_type) {
159: case C_HASH: {
160: row_value->get_hash()->put(*columns[column_index], new VString(cell));
161: break;
162: }
163: case C_STRING: {
164: VString* row_string=(VString*)row_value;
165: row_string->set_string(cell);
166: break;
167: }
168: case C_TABLE: {
169: ArrayString* row=(ArrayString*)row_value;
170: *row+=&cell;
171: break;
172: }
173: }
174: }
1.69 paf 175:
176: if(duplicate & !distinct) {
1.79 misha 177: error=SQL_Error(PARSER_RUNTIME, "duplicate key");
1.69 paf 178: return true;
179: }
180:
1.53 paf 181: column_index++;
182: return false;
183: } catch(...) {
184: error=SQL_Error("exception occured in Hash_sql_event_handlers::add_row_cell");
185: return true;
186: }
1.11 parser 187: }
188:
189: };
1.67 paf 190: VBool Hash_sql_event_handlers::only_one_column_value(true);
191:
1.11 parser 192: #endif
193:
1.57 paf 194: static void _create_or_add(Request& r, MethodParams& params) {
195: if(params.count()) {
1.66 paf 196: Value& vsrc=params.as_no_junction(0, "param must be hash");
197: if(HashStringValue* src=vsrc.get_hash()) {
1.72 paf 198: VHash& self=GET_SELF(r, VHash);
199: HashStringValue* self_hash=&(self.hash());
200: if(src==self_hash) // same: doing nothing
1.66 paf 201: return;
1.77 paf 202: src->for_each<HashStringValue*>(copy_all_overwrite_to, self_hash);
1.72 paf 203:
1.103 misha 204: if(VHash* vhash_src=static_cast<VHash*>(vsrc.as(VHASH_TYPE)))
1.78 paf 205: {
206: if(Value* vdefault=vhash_src->get_default())
207: {
208: if(vdefault->is_defined())
209: {
210: self.set_default(vdefault);
211: }
212: }
213: }
1.66 paf 214: }
1.20 parser 215: }
216: }
1.22 parser 217:
1.57 paf 218: static void _sub(Request& r, MethodParams& params) {
1.66 paf 219: Value& vsrc=params.as_no_junction(0, "param must be hash");
220: if(HashStringValue* src=vsrc.get_hash()) {
221: HashStringValue* self=&(GET_SELF(r, VHash).hash());
222: if(src==self) { // same: clearing
223: self->clear();
224: return;
225: }
1.77 paf 226: src->for_each<HashStringValue*>(remove_key_from, self);
1.66 paf 227: }
1.57 paf 228: }
229:
230: static void copy_all_dontoverwrite_to(
1.100 misha 231: HashStringValue::key_type key,
232: HashStringValue::value_type value,
233: HashStringValue* dest) {
1.57 paf 234: dest->put_dont_replace(key, value);
1.22 parser 235: }
1.57 paf 236: static void _union(Request& r, MethodParams& params) {
1.22 parser 237: // dest = copy of self
1.57 paf 238: Value& result=*new VHash(GET_SELF(r, VHash).hash());
1.22 parser 239: // dest += b
1.66 paf 240: Value& vsrc=params.as_no_junction(0, "param must be hash");
241: if(HashStringValue* src=vsrc.get_hash())
1.77 paf 242: src->for_each<HashStringValue*>(copy_all_dontoverwrite_to, result.get_hash());
1.22 parser 243:
244: // return result
245: r.write_no_lang(result);
246: }
247:
248: #ifndef DOXYGEN
249: struct Copy_intersection_to_info {
1.57 paf 250: HashStringValue* b;
251: HashStringValue* dest;
1.22 parser 252: };
253: #endif
1.57 paf 254: static void copy_intersection_to(
1.100 misha 255: HashStringValue::key_type key,
256: HashStringValue::value_type value,
257: Copy_intersection_to_info *info) {
1.57 paf 258: if(info->b->get(key))
259: info->dest->put_dont_replace(key, value);
1.22 parser 260: }
1.57 paf 261: static void _intersection(Request& r, MethodParams& params) {
262: Value& result=*new VHash;
1.22 parser 263: // dest += b
1.57 paf 264: Value& vb=params.as_no_junction(0, "param must be hash");
265: if(HashStringValue* b=vb.get_hash()) {
266: Copy_intersection_to_info info={b, result.get_hash()};
1.77 paf 267: GET_SELF(r, VHash).hash().for_each<Copy_intersection_to_info*>(copy_intersection_to, &info);
1.22 parser 268: }
269:
270: // return result
1.57 paf 271: r.write_no_lang(result);
1.22 parser 272: }
273:
1.57 paf 274: static bool intersects(
1.100 misha 275: HashStringValue::key_type key,
276: HashStringValue::value_type /*value*/,
277: HashStringValue* b) {
1.57 paf 278: return b->get(key)!=0;
1.22 parser 279: }
280:
1.57 paf 281: static void _intersects(Request& r, MethodParams& params) {
282: bool result=false;
283:
284: Value& vb=params.as_no_junction(0, "param must be hash");
285: if(HashStringValue* b=vb.get_hash())
1.77 paf 286: result=GET_SELF(r, VHash).hash().first_that<HashStringValue*>(intersects, b)!=0;
1.22 parser 287:
288: // return result
1.89 misha 289: r.write_no_lang(VBool::get(result));
1.22 parser 290: }
291:
292:
1.71 paf 293: extern String sql_bind_name;
1.57 paf 294: extern String sql_limit_name;
295: extern String sql_offset_name;
296: extern String sql_default_name;
297: extern String sql_distinct_name;
1.81 misha 298: extern String sql_value_type_name;
299: extern Table2hash_value_type get_value_type(Value& vvalue_type);
1.71 paf 300: extern int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders);
301: extern void unmarshal_bind_updates(HashStringValue& hash, int placeholder_count, SQL_Driver::Placeholder* placeholders);
302:
1.57 paf 303: static void _sql(Request& r, MethodParams& params) {
304: Value& statement=params.as_junction(0, "statement must be code");
1.2 parser 305:
1.71 paf 306: HashStringValue* bind=0;
1.87 misha 307: ulong limit=SQL_NO_LIMIT;
1.33 paf 308: ulong offset=0;
1.49 paf 309: bool distinct=false;
1.81 misha 310: Table2hash_value_type value_type=C_HASH;
1.57 paf 311: if(params.count()>1) {
312: Value& voptions=params.as_no_junction(1, "options must be hash, not code");
1.76 paf 313: if(voptions.is_defined() && !voptions.is_string())
1.57 paf 314: if(HashStringValue* options=voptions.get_hash()) {
1.49 paf 315: int valid_options=0;
1.71 paf 316: if(Value* vbind=options->get(sql_bind_name)) {
317: valid_options++;
318: bind=vbind->get_hash();
319: }
1.57 paf 320: if(Value* vlimit=options->get(sql_limit_name)) {
1.88 misha 321: valid_options++;
322: limit=(ulong)r.process_to_value(*vlimit).as_double();
1.49 paf 323: }
1.57 paf 324: if(Value* voffset=options->get(sql_offset_name)) {
1.49 paf 325: valid_options++;
1.37 paf 326: offset=(ulong)r.process_to_value(*voffset).as_double();
1.49 paf 327: }
1.57 paf 328: if(Value* vdistinct=options->get(sql_distinct_name)) {
1.49 paf 329: valid_options++;
330: distinct=r.process_to_value(*vdistinct).as_bool();
331: }
1.81 misha 332: if(Value* vvalue_type=options->get(sql_value_type_name)) {
333: valid_options++;
334: value_type=get_value_type(r.process_to_value(*vvalue_type));
335: }
1.57 paf 336: if(valid_options!=options->count())
1.104 misha 337: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
1.33 paf 338: } else
1.104 misha 339: throw Exception(PARSER_RUNTIME, 0, OPTIONS_MUST_BE_HASH);
1.2 parser 340: }
341:
1.71 paf 342: SQL_Driver::Placeholder* placeholders=0;
343: uint placeholders_count=0;
344: if(bind)
345: placeholders_count=marshal_binds(*bind, placeholders);
346:
1.57 paf 347: Temp_lang temp_lang(r, String::L_SQL);
1.37 paf 348: const String& statement_string=r.process_to_string(statement);
1.99 misha 349: const char* statement_cstr=statement_string.untaint_cstr(r.flang, r.connection());
1.97 misha 350:
1.57 paf 351: HashStringValue& hash=GET_SELF(r, VHash).hash();
1.11 parser 352: hash.clear();
1.57 paf 353: Hash_sql_event_handlers handlers(
1.49 paf 354: statement_string, statement_cstr,
355: distinct,
1.86 misha 356: hash,
357: value_type);
1.87 misha 358:
1.57 paf 359: r.connection()->query(
1.70 paf 360: statement_cstr,
1.73 paf 361: placeholders_count, placeholders,
1.70 paf 362: offset, limit,
1.45 paf 363: handlers,
364: statement_string);
1.71 paf 365:
366: if(bind)
367: unmarshal_bind_updates(*bind, placeholders_count, placeholders);
1.2 parser 368: }
369:
1.57 paf 370: static void keys_collector(
1.100 misha 371: HashStringValue::key_type key,
372: HashStringValue::value_type,
373: Table *table) {
374: Table::element_type row(new ArrayString(1));
1.57 paf 375: *row+=new String(key, String::L_TAINTED);
376: *table+=row;
1.9 parser 377: }
1.68 paf 378: static void _keys(Request& r, MethodParams& params) {
379: const String* keys_column_name;
380: if(params.count()>0)
1.81 misha 381: keys_column_name=¶ms.as_string(0, COLUMN_NAME_MUST_BE_STRING);
1.68 paf 382: else
383: keys_column_name=new String("key");
384:
1.101 misha 385: Table::columns_type columns(new ArrayString(1));
1.68 paf 386: *columns+=keys_column_name;
1.57 paf 387: Table* table=new Table(columns);
1.9 parser 388:
1.77 paf 389: GET_SELF(r, VHash).hash().for_each<Table*>(keys_collector, table);
1.9 parser 390:
1.57 paf 391: r.write_no_lang(*new VTable(table));
1.9 parser 392: }
393:
1.57 paf 394: static void _count(Request& r, MethodParams&) {
395: r.write_no_lang(*new VInt(GET_SELF(r, VHash).hash().count()));
1.16 parser 396: }
397:
1.57 paf 398: static void _delete(Request& r, MethodParams& params) {
1.25 paf 399:
1.57 paf 400: GET_SELF(r, VHash).hash().remove(params.as_string(0, "key must be string"));
1.25 paf 401: }
402:
1.82 misha 403: static void _contains(Request& r, MethodParams& params) {
1.89 misha 404: bool result=GET_SELF(r, VHash).hash().contains(params.as_string(0, "key must be string"));
405: r.write_no_lang(VBool::get(result));
1.80 misha 406: }
407:
1.26 paf 408: #ifndef DOXYGEN
1.59 paf 409: struct Foreach_info {
1.26 paf 410: Request *r;
411: const String* key_var_name;
412: const String* value_var_name;
1.57 paf 413: Value* body_code;
414: Value* delim_maybe_code;
1.26 paf 415:
1.84 misha 416: Value* var_context;
1.26 paf 417: bool need_delim;
418: };
419: #endif
1.90 misha 420: static bool one_foreach_cycle(
421: HashStringValue::key_type akey,
422: HashStringValue::value_type avalue,
423: Foreach_info *info) {
1.84 misha 424: Value& var_context=*info->var_context;
1.85 misha 425: if(info->key_var_name){
1.96 misha 426: VString* vkey=new VString(*new String(akey, String::L_TAINTED));
1.103 misha 427: var_context.put_element(*info->key_var_name, vkey, false);
1.85 misha 428: }
429: if(info->value_var_name)
1.103 misha 430: var_context.put_element(*info->value_var_name, avalue, false);
1.57 paf 431:
1.91 misha 432: if(info->delim_maybe_code){ // delimiter set
1.90 misha 433: StringOrValue sv_processed=info->r->process(*info->body_code);
434: Request::Skip lskip=info->r->get_skip(); info->r->set_skip(Request::SKIP_NOTHING);
435:
436: const String* s_processed=sv_processed.get_string();
1.91 misha 437: if(s_processed && !s_processed->is_empty()) { // we have body
1.90 misha 438: if(info->need_delim) // need delim & iteration produced string?
439: info->r->write_pass_lang(info->r->process(*info->delim_maybe_code));
440: else
441: info->need_delim=true;
442: }
443: info->r->write_pass_lang(sv_processed);
444: return lskip==Request::SKIP_BREAK;
445: } else {
446: info->r->process_write(*info->body_code);
447: Request::Skip lskip=info->r->get_skip(); info->r->set_skip(Request::SKIP_NOTHING);
448: return lskip==Request::SKIP_BREAK;
1.26 paf 449: }
450: }
1.57 paf 451: static void _foreach(Request& r, MethodParams& params) {
1.102 misha 452: InCycle temp(r);
1.77 paf 453:
1.85 misha 454: const String& key_var_name=params.as_string(0, "key-var name must be string");
455: const String& value_var_name=params.as_string(1, "value-var name must be string");
456:
1.63 paf 457: Foreach_info info={
458: &r,
1.85 misha 459: key_var_name.is_empty()? 0 : &key_var_name,
460: value_var_name.is_empty()? 0 : &value_var_name,
1.63 paf 461: ¶ms.as_junction(2, "body must be code"),
1.84 misha 462: /*delimiter*/params.count()>3?params.get(3):0,
463: /*var_context*/r.get_method_frame()->caller(),
1.85 misha 464: false
1.63 paf 465: };
1.57 paf 466:
467: VHash& self=GET_SELF(r, VHash);
468: HashStringValue& hash=self.hash();
1.28 paf 469: VHash_lock lock(self);
1.77 paf 470: hash.first_that<Foreach_info*>(one_foreach_cycle, &info);
1.26 paf 471: }
472:
1.105 misha 473: static void _at(Request& r, MethodParams& params) {
474: HashStringValue& hash=GET_SELF(r, VHash).hash();
475: size_t count=hash.count();
476:
477: int pos=0;
478:
479: Value& vwhence=*params.get(0);
480: if(vwhence.is_string()){
481: const String& swhence=*vwhence.get_string();
482: if(swhence == "last")
483: pos=count-1;
484: else if(swhence != "first")
485: throw Exception(PARSER_RUNTIME,
486: &swhence,
487: "whence must be 'first', 'last' or expression");
488: } else {
489: pos=r.process_to_value(vwhence).as_int();
490: if(pos < 0)
491: pos+=count;
492: }
493:
1.106 ! misha 494: if(count && pos >= 0 && (size_t)pos < count){
1.105 misha 495: if(pos == 0)
496: r.write_assign_lang(*hash.first_value());
1.106 ! misha 497: else if((size_t)pos == count-1)
1.105 misha 498: r.write_assign_lang(*hash.last_value());
499: else
500: for(HashStringValue::Iterator i(hash); i; i.next(), pos-- )
501: if(!pos){
502: r.write_assign_lang(*i.value());
503: break;
504: }
505: }
506:
507: }
508:
1.1 paf 509: // constructor
510:
1.57 paf 511: MHash::MHash(): Methoded("hash")
1.39 paf 512: {
1.21 parser 513: // ^hash::create[[copy_from]]
1.57 paf 514: add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
1.22 parser 515: // ^hash.add[add_from]
1.57 paf 516: add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
1.22 parser 517: // ^hash.sub[sub_from]
518: add_native_method("sub", Method::CT_DYNAMIC, _sub, 1, 1);
519: // ^a.union[b] = hash
520: add_native_method("union", Method::CT_DYNAMIC, _union, 1, 1);
521: // ^a.intersection[b] = hash
522: add_native_method("intersection", Method::CT_DYNAMIC, _intersection, 1, 1);
523: // ^a.intersects[b] = bool
524: add_native_method("intersects", Method::CT_DYNAMIC, _intersects, 1, 1);
1.25 paf 525:
526: // ^a.delete[key]
527: add_native_method("delete", Method::CT_DYNAMIC, _delete, 1, 1);
1.2 parser 528:
1.82 misha 529: // ^a.contains[key]
530: add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
531: // backward
532: add_native_method("contain", Method::CT_DYNAMIC, _contains, 1, 1);
1.80 misha 533:
534: // ^hash::sql[query][options hash]
1.33 paf 535: add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
1.2 parser 536:
1.68 paf 537: // ^hash._keys[[column name]]
538: add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
1.16 parser 539:
540: // ^hash._count[]
541: add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 0);
1.26 paf 542:
543: // ^hash.foreach[key;value]{code}[delim]
544: add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2+1, 2+1+1);
1.105 misha 545:
546: // ^hash._at[first|last]
547: // ^hash._at([-]offset)
548: add_native_method("_at", Method::CT_DYNAMIC, _at, 1, 1);
1.1 paf 549: }
E-mail: