Annotation of parser3/src/classes/hash.C, revision 1.54.2.4
1.1 paf 1: /** @file
2: Parser: @b hash parser class.
3:
1.54.2.2 paf 4: Copyright (c) 2001-2003 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.54.2.4! paf 8: static const char* IDENT_HASH_C="$Date: 2003/02/04 14:04:45 $";
1.1 paf 9:
10: #include "classes.h"
1.54.2.4! 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:
23: class MHash : public Methoded {
1.2 parser 24: public: // VStateless_class
1.54.2.1 paf 25: ValuePtr create_new_value() { return ValuePtr(new VHash()); }
1.2 parser 26:
1.1 paf 27: public:
28: MHash(Pool& pool);
29: public: // Methoded
1.2 parser 30: bool used_directly() { return true; }
1.1 paf 31: };
32:
33: // methods
34:
1.11 parser 35: #ifndef DOXYGEN
1.52 paf 36: class Hash_sql_event_handlers: public SQL_Driver_query_event_handlers {
1.11 parser 37: public:
38: Hash_sql_event_handlers(Pool& apool, const String& amethod_name,
1.54.2.2 paf 39: const String& astatement_string, const char* astatement_cstr,
1.49 paf 40: bool adistinct,
1.52 paf 41: Hash& arows_hash):
1.11 parser 42: pool(apool),
43: method_name(amethod_name),
44: statement_string(astatement_string),
45: statement_cstr(astatement_cstr),
1.49 paf 46: distinct(adistinct),
1.11 parser 47: rows_hash(arows_hash),
48: columns(pool),
49: row_index(0) {
50: }
1.53 paf 51: bool add_column(SQL_Error& error, void *ptr, size_t size) {
52: try {
53: String *column=new(pool) String(pool);
54: column->APPEND_TAINTED(
1.54.2.2 paf 55: (const char* )ptr, size,
1.53 paf 56: statement_cstr, 0);
57: columns+=column;
58:
59: return false;
60: } catch(...) {
61: error=SQL_Error("exception occured in Hash_sql_event_handlers::add_column");
62: return true;
63: }
1.11 parser 64: }
1.53 paf 65: bool before_rows(SQL_Error& error) {
66: if(columns.size()<=1) {
67: error=SQL_Error("parser.runtime",
1.11 parser 68: &method_name,
69: "column count must be more than 1 to create a hash");
1.53 paf 70: return true;
71: }
72:
73: return false;
1.11 parser 74: }
1.53 paf 75: bool add_row(SQL_Error& /*error*/) {
1.11 parser 76: column_index=0;
1.53 paf 77: return false;
1.11 parser 78: }
1.53 paf 79: bool add_row_cell(SQL_Error& error, void *ptr, size_t size) {
80: try {
81: String *cell=new(pool) String(pool);
82: if(size)
83: cell->APPEND_TAINTED(
1.54.2.2 paf 84: (const char* )ptr, size,
1.53 paf 85: statement_cstr, row_index++);
86: if(column_index==0) {
87: VHash *row_vhash=new(pool) VHash(pool);
88: row_hash=row_vhash->get_hash(0);
89: if(rows_hash.put_dont_replace(*cell, row_vhash)) // put. existed?
90: if(!distinct) {
91: error=SQL_Error("parser.runtime",
92: cell,
93: "duplicate key");
94: return true;
95: }
96: } else
97: row_hash->put(*columns.get_string(column_index), new(pool) VString(*cell));
98: column_index++;
99:
100: return false;
101: } catch(...) {
102: error=SQL_Error("exception occured in Hash_sql_event_handlers::add_row_cell");
103: return true;
104: }
1.11 parser 105: }
106:
107: private:
108: Pool& pool;
109: const String& method_name;
1.54.2.2 paf 110: const String& statement_string; const char* statement_cstr;
1.49 paf 111: bool distinct;
1.11 parser 112: Hash& rows_hash;
113: Hash *row_hash;
114: int column_index;
115: Array columns;
116: int row_index;
117: };
118: #endif
119:
1.22 parser 120: static void copy_all_overwrite_to(const Hash::Key& key, Hash::Val *value, void *info) {
1.20 parser 121: Hash& dest=*static_cast<Hash *>(info);
122: dest.put(key, value);
123: }
1.54.2.3 paf 124: static void _create_or_add(Request& r, StringPtr method_name, MethodParams& params) {
1.20 parser 125: Pool& pool=r.pool();
126:
1.54.2.3 paf 127: if(params.count()) {
128: Value& vb=params.as_no_junction(0, "param must be hash");
1.24 parser 129: if(Hash *b=vb.get_hash(&method_name))
1.50 paf 130: b->for_each(copy_all_overwrite_to, &static_cast<VHash *>(r.get_self())->hash(&method_name));
1.20 parser 131: }
132: }
133:
1.22 parser 134: static void remove_key_from(const Hash::Key& key, Hash::Val *value, void *info) {
135: Hash& dest=*static_cast<Hash *>(info);
1.25 paf 136: dest.remove(key);
1.22 parser 137: }
1.54.2.3 paf 138: static void _sub(Request& r, StringPtr method_name, MethodParams& params) {
1.22 parser 139: Pool& pool=r.pool();
140:
1.54.2.3 paf 141: Value& vb=params.as_no_junction(0, "param must be hash");
1.24 parser 142: if(Hash *b=vb.get_hash(&method_name))
1.50 paf 143: b->for_each(remove_key_from, &static_cast<VHash *>(r.get_self())->hash(&method_name));
1.22 parser 144: }
145:
146: static void copy_all_dontoverwrite_to(const Hash::Key& key, Hash::Val *value, void *info) {
147: Hash& dest=*static_cast<Hash *>(info);
148: dest.put_dont_replace(key, value);
149: }
1.54.2.3 paf 150: static void _union(Request& r, StringPtr method_name, MethodParams& params) {
1.22 parser 151: Pool& pool=r.pool();
152:
153: // dest = copy of self
1.50 paf 154: Hash& dest=*new(pool) Hash(static_cast<VHash *>(r.get_self())->hash(&method_name));
1.22 parser 155: // dest += b
1.54.2.3 paf 156: Value& vb=params.as_no_junction(0, "param must be hash");
1.24 parser 157: if(Hash *b=vb.get_hash(&method_name))
1.22 parser 158: b->for_each(copy_all_dontoverwrite_to, &dest);
159:
160: // return result
161: Value& result=*new(pool) VHash(pool, dest);
162: r.write_no_lang(result);
163: }
164:
165: #ifndef DOXYGEN
166: struct Copy_intersection_to_info {
167: Hash *b;
168: Hash *dest;
169: };
170: #endif
171:
172: static void copy_intersection_to(const Hash::Key& key, Hash::Val *value, void *info) {
173: Copy_intersection_to_info& i=*static_cast<Copy_intersection_to_info *>(info);
174:
175: if(i.b->get(key))
176: i.dest->put_dont_replace(key, value);
177: }
1.54.2.3 paf 178: static void _intersection(Request& r, StringPtr method_name, MethodParams& params) {
1.22 parser 179: Pool& pool=r.pool();
180:
181: // dest = copy of self
182: Hash& dest=*new(pool) Hash(pool);
183: // dest += b
1.54.2.3 paf 184: Value& vb=params.as_no_junction(0, "param must be hash");
1.24 parser 185: if(Hash *b=vb.get_hash(&method_name)) {
1.22 parser 186: Copy_intersection_to_info info={
187: b,
188: &dest
189: };
1.50 paf 190: static_cast<VHash *>(r.get_self())->hash(&method_name).for_each(copy_intersection_to, &info);
1.22 parser 191: }
192:
193: // return result
1.39 paf 194: r.write_no_lang(*new(pool) VHash(pool, dest));
1.22 parser 195: }
196:
197: static void *intersects(const Hash::Key& key, Hash::Val *value, void *info) {
198: return static_cast<Hash *>(info)->get(key);
199: }
200:
1.54.2.3 paf 201: static void _intersects(Request& r, StringPtr method_name, MethodParams& params) {
1.22 parser 202: Pool& pool=r.pool();
203:
204: bool yes=false;
205:
206: // dest = copy of self
207: Hash& dest=*new(pool) Hash(pool);
208: // dest += b
1.54.2.3 paf 209: Value& vb=params.as_no_junction(0, "param must be hash");
1.24 parser 210: if(Hash *b=vb.get_hash(&method_name))
1.50 paf 211: yes=static_cast<VHash *>(r.get_self())->hash(&method_name).first_that(intersects, b)!=0;
1.22 parser 212:
213: // return result
1.39 paf 214: r.write_no_lang(*new(pool) VBool(pool, yes));
1.22 parser 215: }
216:
217:
1.54.2.3 paf 218: static void _sql(Request& r, StringPtr method_name, MethodParams& params) {
1.2 parser 219: Pool& pool=r.pool();
220:
1.54.2.3 paf 221: Value& statement=params.as_junction(0, "statement must be code");
1.2 parser 222:
223: ulong limit=0;
1.33 paf 224: ulong offset=0;
1.49 paf 225: bool distinct=false;
1.54.2.3 paf 226: if(params.count()>1) {
227: Value& voptions=params.as_no_junction(1, "options must be hash, not code");
1.43 paf 228: if(!voptions.is_string())
1.33 paf 229: if(Hash *options=voptions.get_hash(&method_name)) {
1.49 paf 230: int valid_options=0;
231: if(Value *vlimit=(Value *)options->get(*sql_limit_name)) {
232: valid_options++;
1.37 paf 233: limit=(ulong)r.process_to_value(*vlimit).as_double();
1.49 paf 234: }
235: if(Value *voffset=(Value *)options->get(*sql_offset_name)) {
236: valid_options++;
1.37 paf 237: offset=(ulong)r.process_to_value(*voffset).as_double();
1.49 paf 238: }
239: if(Value *vdistinct=(Value *)options->get(*sql_distinct_name)) {
240: valid_options++;
241: distinct=r.process_to_value(*vdistinct).as_bool();
242: }
243: if(valid_options!=options->size())
244: throw Exception("parser.runtime",
245: &method_name,
246: "called with invalid option");
1.33 paf 247: } else
1.36 paf 248: throw Exception("parser.runtime",
1.33 paf 249: &method_name,
250: "options must be hash");
1.2 parser 251: }
252:
253: Temp_lang temp_lang(r, String::UL_SQL);
1.37 paf 254: const String& statement_string=r.process_to_string(statement);
1.54.2.2 paf 255: const char* statement_cstr=
1.32 paf 256: statement_string.cstr(String::UL_UNSPECIFIED, r.connection(&method_name));
1.50 paf 257: Hash& hash=static_cast<VHash *>(r.get_self())->hash(&method_name);
1.11 parser 258: hash.clear();
259: Hash_sql_event_handlers handlers(pool, method_name,
1.49 paf 260: statement_string, statement_cstr,
261: distinct,
262: hash);
1.23 parser 263:
1.32 paf 264: r.connection(&method_name)->query(
1.23 parser 265: statement_cstr, offset, limit,
1.45 paf 266: handlers,
267: statement_string);
1.2 parser 268: }
269:
1.9 parser 270: static void keys_collector(const Hash::Key& key, Hash::Val *value, void *info) {
271: Table& table=*static_cast<Table *>(info);
272: Pool& pool=table.pool();
273:
274: Array& row=*new(pool) Array(pool);
275: row+=&key;
276: table+=&row;
277: }
1.54.2.3 paf 278: static void _keys(Request& r, StringPtr method_name, MethodParams& ) {
1.9 parser 279: Pool& pool=r.pool();
280:
281: Array& columns=*new(pool) Array(pool);
282: columns+=new(pool) String(pool, "key");
283: Table& table=*new(pool) Table(pool, &method_name, &columns);
284:
1.50 paf 285: static_cast<VHash *>(r.get_self())->hash(&method_name).for_each(keys_collector, &table);
1.9 parser 286:
1.39 paf 287: r.write_no_lang(*new(pool) VTable(pool, &table));
1.9 parser 288: }
289:
1.54.2.3 paf 290: static void _count(Request& r, StringPtr method_name, MethodParams& ) {
1.16 parser 291: Pool& pool=r.pool();
292:
1.39 paf 293: r.write_no_lang(
1.50 paf 294: *new(pool) VInt(pool, static_cast<VHash *>(r.get_self())->hash(&method_name).size()));
1.16 parser 295: }
296:
1.54.2.3 paf 297: static void _delete(Request& r, StringPtr method_name, MethodParams& params) {
1.25 paf 298: Pool& pool=r.pool();
299:
1.54.2.3 paf 300: static_cast<VHash *>(r.get_self())->hash(&method_name).remove(params.as_string(0, "key must be string"));
1.25 paf 301: }
302:
1.26 paf 303: #ifndef DOXYGEN
304: struct Foreach_info {
305: Request *r;
306: const String* key_var_name;
307: const String* value_var_name;
308: Value *body_code;
309: Value *delim_maybe_code;
310:
311: VString *vkey;
312: bool need_delim;
313: };
314: #endif
315:
316: static void one_foreach_cycle(const Hash::Key& akey, Hash::Val *avalue,
317: void *info) {
318: Foreach_info& i=*static_cast<Foreach_info *>(info);
319:
320: i.vkey->set_string(akey);
1.51 paf 321: Value& ncontext=*i.r->get_method_frame()->caller();
322: ncontext.put_element(*i.key_var_name, i.vkey, false);
323: ncontext.put_element(*i.value_var_name, static_cast<Value *>(avalue), false);
1.26 paf 324:
1.47 paf 325: StringOrValue sv_processed=i.r->process(*i.body_code);
326: const String *s_processed=sv_processed.get_string();
327: if(i.delim_maybe_code && s_processed && s_processed->size()) { // delimiter set and we have body
328: if(i.need_delim) // need delim & iteration produced string?
1.38 paf 329: i.r->write_pass_lang(i.r->process(*i.delim_maybe_code));
1.26 paf 330: i.need_delim=true;
331: }
1.47 paf 332: i.r->write_pass_lang(sv_processed);
1.26 paf 333: }
1.54.2.3 paf 334: static void _foreach(Request& r, StringPtr method_name, MethodParams& params) {
1.26 paf 335: Pool& pool=r.pool();
1.54.2.3 paf 336: const String& key_var_name=params.as_string(0, "key-var name must be string");
337: const String& value_var_name=params.as_string(1, "value-var name must be string");
338: Value& body_code=params.as_junction(2, "body must be code");
339: Value *delim_maybe_code=params.count()>3?¶ms.get(3):0;
1.26 paf 340:
341: Foreach_info info={
342: &r,
343: &key_var_name, &value_var_name,
344: &body_code,
345: delim_maybe_code,
346:
347: new(pool) VString(pool),
348: false
349: };
1.50 paf 350: VHash& self=*static_cast<VHash *>(r.get_self());
1.28 paf 351: Hash& hash=self.hash(&method_name);
352: VHash_lock lock(self);
353: hash.for_each(one_foreach_cycle, &info);
1.26 paf 354: }
355:
1.1 paf 356: // constructor
357:
1.39 paf 358: MHash::MHash(Pool& apool) : Methoded(apool, "hash")
359: {
1.21 parser 360: // ^hash::create[[copy_from]]
1.22 parser 361: add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
362: // ^hash.add[add_from]
363: add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
364: // ^hash.sub[sub_from]
365: add_native_method("sub", Method::CT_DYNAMIC, _sub, 1, 1);
366: // ^a.union[b] = hash
367: add_native_method("union", Method::CT_DYNAMIC, _union, 1, 1);
368: // ^a.intersection[b] = hash
369: add_native_method("intersection", Method::CT_DYNAMIC, _intersection, 1, 1);
370: // ^a.intersects[b] = bool
371: add_native_method("intersects", Method::CT_DYNAMIC, _intersects, 1, 1);
1.25 paf 372:
373: // ^a.delete[key]
374: add_native_method("delete", Method::CT_DYNAMIC, _delete, 1, 1);
1.2 parser 375:
1.33 paf 376: // ^hash:sql[query][$.limit(1) $.offset(2)]
377: add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
1.2 parser 378:
1.14 parser 379: // ^hash._keys[]
1.13 parser 380: add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 0);
1.16 parser 381:
382: // ^hash._count[]
383: add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 0);
1.26 paf 384:
385: // ^hash.foreach[key;value]{code}[delim]
386: add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2+1, 2+1+1);
1.1 paf 387: }
388:
389: // global variable
390:
1.40 paf 391: Methoded *hash_class;
1.1 paf 392:
393: // creator
394:
395: Methoded *MHash_create(Pool& pool) {
1.40 paf 396: return hash_class=new(pool) MHash(pool);
1.1 paf 397: }
E-mail: