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