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