Annotation of parser3/src/classes/hash.C, revision 1.102
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.102 ! misha 8: static const char * const IDENT_HASH_C="$Date: 2009-07-08 00:30:58 $";
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.94 misha 25: Value* create_new_value(Pool&, HashStringValue*) { 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:
204: if(VHash* vhash_src=static_cast<VHash*>(vsrc.as(VHASH_TYPE, false)))
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.79 misha 337: throw Exception(PARSER_RUNTIME,
1.57 paf 338: 0,
1.49 paf 339: "called with invalid option");
1.33 paf 340: } else
1.79 misha 341: throw Exception(PARSER_RUNTIME,
1.57 paf 342: 0,
1.33 paf 343: "options must be hash");
1.2 parser 344: }
345:
1.71 paf 346: SQL_Driver::Placeholder* placeholders=0;
347: uint placeholders_count=0;
348: if(bind)
349: placeholders_count=marshal_binds(*bind, placeholders);
350:
1.57 paf 351: Temp_lang temp_lang(r, String::L_SQL);
1.37 paf 352: const String& statement_string=r.process_to_string(statement);
1.99 misha 353: const char* statement_cstr=statement_string.untaint_cstr(r.flang, r.connection());
1.97 misha 354:
1.57 paf 355: HashStringValue& hash=GET_SELF(r, VHash).hash();
1.11 parser 356: hash.clear();
1.57 paf 357: Hash_sql_event_handlers handlers(
1.49 paf 358: statement_string, statement_cstr,
359: distinct,
1.86 misha 360: hash,
361: value_type);
1.87 misha 362:
1.57 paf 363: r.connection()->query(
1.70 paf 364: statement_cstr,
1.73 paf 365: placeholders_count, placeholders,
1.70 paf 366: offset, limit,
1.45 paf 367: handlers,
368: statement_string);
1.71 paf 369:
370: if(bind)
371: unmarshal_bind_updates(*bind, placeholders_count, placeholders);
1.2 parser 372: }
373:
1.57 paf 374: static void keys_collector(
1.100 misha 375: HashStringValue::key_type key,
376: HashStringValue::value_type,
377: Table *table) {
378: Table::element_type row(new ArrayString(1));
1.57 paf 379: *row+=new String(key, String::L_TAINTED);
380: *table+=row;
1.9 parser 381: }
1.68 paf 382: static void _keys(Request& r, MethodParams& params) {
383: const String* keys_column_name;
384: if(params.count()>0)
1.81 misha 385: keys_column_name=¶ms.as_string(0, COLUMN_NAME_MUST_BE_STRING);
1.68 paf 386: else
387: keys_column_name=new String("key");
388:
1.101 misha 389: Table::columns_type columns(new ArrayString(1));
1.68 paf 390: *columns+=keys_column_name;
1.57 paf 391: Table* table=new Table(columns);
1.9 parser 392:
1.77 paf 393: GET_SELF(r, VHash).hash().for_each<Table*>(keys_collector, table);
1.9 parser 394:
1.57 paf 395: r.write_no_lang(*new VTable(table));
1.9 parser 396: }
397:
1.57 paf 398: static void _count(Request& r, MethodParams&) {
399: r.write_no_lang(*new VInt(GET_SELF(r, VHash).hash().count()));
1.16 parser 400: }
401:
1.57 paf 402: static void _delete(Request& r, MethodParams& params) {
1.25 paf 403:
1.57 paf 404: GET_SELF(r, VHash).hash().remove(params.as_string(0, "key must be string"));
1.25 paf 405: }
406:
1.82 misha 407: static void _contains(Request& r, MethodParams& params) {
1.89 misha 408: bool result=GET_SELF(r, VHash).hash().contains(params.as_string(0, "key must be string"));
409: r.write_no_lang(VBool::get(result));
1.80 misha 410: }
411:
1.26 paf 412: #ifndef DOXYGEN
1.59 paf 413: struct Foreach_info {
1.26 paf 414: Request *r;
415: const String* key_var_name;
416: const String* value_var_name;
1.57 paf 417: Value* body_code;
418: Value* delim_maybe_code;
1.26 paf 419:
1.84 misha 420: Value* var_context;
1.26 paf 421: bool need_delim;
422: };
423: #endif
1.90 misha 424: static bool one_foreach_cycle(
425: HashStringValue::key_type akey,
426: HashStringValue::value_type avalue,
427: Foreach_info *info) {
1.84 misha 428: Value& var_context=*info->var_context;
1.85 misha 429: if(info->key_var_name){
1.96 misha 430: VString* vkey=new VString(*new String(akey, String::L_TAINTED));
431: var_context.put_element(var_context, *info->key_var_name, vkey, false);
1.85 misha 432: }
433: if(info->value_var_name)
434: var_context.put_element(var_context, *info->value_var_name, avalue, false);
1.57 paf 435:
1.91 misha 436: if(info->delim_maybe_code){ // delimiter set
1.90 misha 437: StringOrValue sv_processed=info->r->process(*info->body_code);
438: Request::Skip lskip=info->r->get_skip(); info->r->set_skip(Request::SKIP_NOTHING);
439:
440: const String* s_processed=sv_processed.get_string();
1.91 misha 441: if(s_processed && !s_processed->is_empty()) { // we have body
1.90 misha 442: if(info->need_delim) // need delim & iteration produced string?
443: info->r->write_pass_lang(info->r->process(*info->delim_maybe_code));
444: else
445: info->need_delim=true;
446: }
447: info->r->write_pass_lang(sv_processed);
448: return lskip==Request::SKIP_BREAK;
449: } else {
450: info->r->process_write(*info->body_code);
451: Request::Skip lskip=info->r->get_skip(); info->r->set_skip(Request::SKIP_NOTHING);
452: return lskip==Request::SKIP_BREAK;
1.26 paf 453: }
454: }
1.57 paf 455: static void _foreach(Request& r, MethodParams& params) {
1.102 ! misha 456: InCycle temp(r);
1.77 paf 457:
1.85 misha 458: const String& key_var_name=params.as_string(0, "key-var name must be string");
459: const String& value_var_name=params.as_string(1, "value-var name must be string");
460:
1.63 paf 461: Foreach_info info={
462: &r,
1.85 misha 463: key_var_name.is_empty()? 0 : &key_var_name,
464: value_var_name.is_empty()? 0 : &value_var_name,
1.63 paf 465: ¶ms.as_junction(2, "body must be code"),
1.84 misha 466: /*delimiter*/params.count()>3?params.get(3):0,
467: /*var_context*/r.get_method_frame()->caller(),
1.85 misha 468: false
1.63 paf 469: };
1.57 paf 470:
471: VHash& self=GET_SELF(r, VHash);
472: HashStringValue& hash=self.hash();
1.28 paf 473: VHash_lock lock(self);
1.77 paf 474: hash.first_that<Foreach_info*>(one_foreach_cycle, &info);
1.26 paf 475: }
476:
1.1 paf 477: // constructor
478:
1.57 paf 479: MHash::MHash(): Methoded("hash")
1.39 paf 480: {
1.21 parser 481: // ^hash::create[[copy_from]]
1.57 paf 482: add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
1.22 parser 483: // ^hash.add[add_from]
1.57 paf 484: add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
1.22 parser 485: // ^hash.sub[sub_from]
486: add_native_method("sub", Method::CT_DYNAMIC, _sub, 1, 1);
487: // ^a.union[b] = hash
488: add_native_method("union", Method::CT_DYNAMIC, _union, 1, 1);
489: // ^a.intersection[b] = hash
490: add_native_method("intersection", Method::CT_DYNAMIC, _intersection, 1, 1);
491: // ^a.intersects[b] = bool
492: add_native_method("intersects", Method::CT_DYNAMIC, _intersects, 1, 1);
1.25 paf 493:
494: // ^a.delete[key]
495: add_native_method("delete", Method::CT_DYNAMIC, _delete, 1, 1);
1.2 parser 496:
1.82 misha 497: // ^a.contains[key]
498: add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
499: // backward
500: add_native_method("contain", Method::CT_DYNAMIC, _contains, 1, 1);
1.80 misha 501:
502: // ^hash::sql[query][options hash]
1.33 paf 503: add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
1.2 parser 504:
1.68 paf 505: // ^hash._keys[[column name]]
506: add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
1.16 parser 507:
508: // ^hash._count[]
509: add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 0);
1.26 paf 510:
511: // ^hash.foreach[key;value]{code}[delim]
512: add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2+1, 2+1+1);
1.1 paf 513: }
E-mail: