Annotation of parser3/src/classes/table.C, revision 1.227
1.20 paf 1: /** @file
1.47 paf 2: Parser: @b table parser class.
1.20 paf 3:
1.210 paf 4: Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com)
1.144 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.154 paf 6: */
1.221 paf 7:
1.227 ! misha 8: static const char * const IDENT_TABLE_C="$Date: 2007/04/23 10:30:10 $";
1.224 misha 9:
10: #include <sstream>
11: using namespace std;
1.1 paf 12:
1.105 parser 13: #include "classes.h"
1.182 paf 14: #include "pa_vmethod_frame.h"
15:
1.16 paf 16: #include "pa_common.h"
1.1 paf 17: #include "pa_request.h"
18: #include "pa_vtable.h"
1.6 paf 19: #include "pa_vint.h"
1.51 paf 20: #include "pa_sql_connection.h"
1.72 paf 21: #include "pa_vbool.h"
1.1 paf 22:
1.66 paf 23: // class
24:
1.182 paf 25: class MTable: public Methoded {
1.66 paf 26: public: // VStateless_class
1.209 paf 27: Value* create_new_value(Pool&, HashStringValue&) { return new VTable(); }
1.66 paf 28:
29: public:
1.182 paf 30: MTable();
1.70 paf 31:
32: public: // Methoded
1.66 paf 33: bool used_directly() { return true; }
34: };
1.1 paf 35:
1.182 paf 36: // global variable
37:
38: DECLARE_CLASS_VAR(table, new MTable, 0);
39:
1.221 paf 40: // externs
41:
42: extern String cycle_data_name;
43:
1.182 paf 44: // defines for globals
45:
1.203 paf 46: #define SQL_BIND_NAME "bind"
1.182 paf 47: #define SQL_DEFAULT_NAME "default"
48: #define SQL_DISTINCT_NAME "distinct"
1.227 ! misha 49: #define SQL_VALUE_TYPE_NAME "type"
1.182 paf 50: #define TABLE_REVERSE_NAME "reverse"
51:
1.227 ! misha 52:
1.182 paf 53: // globals
54:
1.203 paf 55: String sql_bind_name(SQL_BIND_NAME);
1.214 paf 56: String sql_limit_name(PA_SQL_LIMIT_NAME);
57: String sql_offset_name(PA_SQL_OFFSET_NAME);
1.182 paf 58: String sql_default_name(SQL_DEFAULT_NAME);
59: String sql_distinct_name(SQL_DISTINCT_NAME);
1.227 ! misha 60: String sql_value_type_name(SQL_VALUE_TYPE_NAME);
1.182 paf 61: String table_reverse_name(TABLE_REVERSE_NAME);
62:
1.1 paf 63: // methods
1.66 paf 64:
1.182 paf 65: static Table::Action_options get_action_options(Request& r, MethodParams& params,
66: const Table& source) {
1.176 paf 67: Table::Action_options result;
1.182 paf 68: if(!params.count())
69: return result;
70:
71: Value& maybe_options=params.last();
72: /* can not do it:
73: want to enable ^table::create[$source;
74: # $.option[]
75: ]
76: but there is ^table.locate[name;value]
1.176 paf 77:
1.212 paf 78: ...if(voptions.is_defined() && !voptions.is_string()))
1.182 paf 79: if(maybe_options.is_string()) { // allow empty options
80: result.defined=true;
1.176 paf 81: return result;
1.182 paf 82: }
83: */
84: HashStringValue* options=maybe_options.get_hash();
1.176 paf 85: if(!options)
86: return result;
87:
88: result.defined=true;
89: bool defined_offset=false;
90:
91: int valid_options=0;
1.182 paf 92: if(Value* voffset=options->get(sql_offset_name)) {
1.176 paf 93: valid_options++;
94: defined_offset=true;
95: if(voffset->is_string()) {
1.182 paf 96: const String& soffset=*voffset->get_string();
1.176 paf 97: if(soffset == "cur")
98: result.offset=source.current();
99: else
1.226 misha 100: throw Exception(PARSER_RUNTIME,
1.176 paf 101: &soffset,
102: "must be 'cur' string or expression");
103: } else
104: result.offset=r.process_to_value(*voffset).as_int();
105: }
1.182 paf 106: if(Value* vlimit=options->get(sql_limit_name)) {
1.176 paf 107: valid_options++;
108: result.limit=r.process_to_value(*vlimit).as_int();
109: }
1.182 paf 110: if(Value *vreverse=(Value *)options->get(table_reverse_name)) {
111: valid_options++;
112: result.reverse=r.process_to_value(*vreverse).as_bool();
113: if(result.reverse && !defined_offset)
114: result.offset=source.count()-1;
115: }
116:
117: if(valid_options!=options->count())
1.226 misha 118: throw Exception(PARSER_RUNTIME,
1.182 paf 119: 0,
1.176 paf 120: "called with invalid option");
121:
122: return result;
123: }
1.180 paf 124: static void check_option_param(bool options_defined,
1.182 paf 125: MethodParams& params, size_t next_param_index,
1.176 paf 126: const char *msg) {
1.182 paf 127: if(next_param_index+(options_defined?1:0) != params.count())
1.226 misha 128: throw Exception(PARSER_RUNTIME,
1.182 paf 129: 0,
1.176 paf 130: "%s", msg);
1.157 paf 131: }
132:
1.182 paf 133: static void _create(Request& r, MethodParams& params) {
1.157 paf 134: // clone/copy part?
1.182 paf 135: if(Table *source=params[0].get_table()) {
136: Table::Action_options o=get_action_options(r, params, *source);
137: check_option_param(o.defined, params, 1,
1.176 paf 138: "too many parameters");
1.182 paf 139: GET_SELF(r, VTable).set_table(*new Table(*source, o));
1.157 paf 140: return;
141: }
1.142 paf 142:
1.1 paf 143: // data is last parameter
1.182 paf 144: Temp_lang temp_lang(r, String::L_PASS_APPENDED);
145: const String& data=
146: r.process_to_string(params.as_junction(params.count()-1, "body must be code"));
1.44 paf 147:
148: // parse columns
1.182 paf 149: size_t raw_pos_after=0;
150: Table::columns_type columns;
151: if(params.count()==2) {
1.216 paf 152: const String& snameless=params.as_string(0, "called with two params, first param may only be string 'nameless'");
1.213 paf 153: if(snameless!="nameless")
1.226 misha 154: throw Exception(PARSER_RUNTIME,
1.213 paf 155: &snameless,
156: "table::create called with two params, first param may only be 'nameless'");
1.182 paf 157: columns=Table::columns_type(0); // nameless
1.21 paf 158: } else {
1.182 paf 159: columns=Table::columns_type(new ArrayString);
1.44 paf 160:
1.182 paf 161: ArrayString head;
162: data.split(head, raw_pos_after, "\n", String::L_AS_IS, 1);
163: if(head.count()) {
164: size_t col_pos_after=0;
165: head[0]->split(*columns, col_pos_after, "\t", String::L_AS_IS);
166: }
1.44 paf 167: }
168:
1.182 paf 169: Table& table=*new Table(columns);
1.44 paf 170: // parse cells
1.182 paf 171:
172: ArrayString rows;
173: data.split(rows, raw_pos_after, "\n", String::L_AS_IS);
174: Array_iterator<const String*> i(rows);
1.98 parser 175: while(i.has_next()) {
1.182 paf 176: Table::element_type row(new ArrayString);
177: const String& string=*i.next();
1.118 parser 178: // remove comment lines
1.182 paf 179: if(!string.length())
1.44 paf 180: continue;
181:
1.182 paf 182: size_t col_pos_after=0;
183: string.split(*row, col_pos_after, "\t", String::L_AS_IS);
184: table+=row;
1.17 paf 185: }
1.1 paf 186:
1.44 paf 187: // replace any previous table value
1.182 paf 188: GET_SELF(r, VTable).set_table(table);
1.44 paf 189: }
190:
1.187 paf 191: struct lsplit_result {
192: char* piece;
193: char delim;
194:
195: operator bool() { return piece!=0; }
196: };
197:
198: inline lsplit_result lsplit(char* string, char delim1, char delim2) {
199: lsplit_result result;
200: if(string) {
201: char delims[]={delim1, delim2, 0};
202: if(char* v=strpbrk(string, delims)) {
203: result.delim=*v;
204: *v=0;
205: result.piece=v+1;
206: return result;
207: }
208: }
209: result.piece=0;
210: result.delim=0;
211: return result;
212: }
213:
214: inline lsplit_result lsplit(char* *string_ref, char delim1, char delim2) {
215: lsplit_result result;
216: result.piece=*string_ref;
217: lsplit_result next=lsplit(*string_ref, delim1, delim2);
218: result.delim=next.delim;
219: *string_ref=next.piece;
220: return result;
221: }
222:
223: static lsplit_result lsplit(char** string_ref, char delim1, char delim2, char encloser) {
224: lsplit_result result;
225:
226: if(char* string=*string_ref) {
227: if(encloser && *string==encloser) {
228: string++;
229:
230: char *read;
231: char *write;
232: write=read=string;
233: char c;
1.193 paf 234: while((c=*read++)) {
1.187 paf 235: if(c==encloser) {
236: char n=*read;
237: if(n==encloser) // double-encloser stands for encloser
238: read++;
239: else if(n==delim1 || n==delim2) {
240: result.delim=n;
241: read++;
242: break;
243: }
244: }
245:
246: *write++=c;
247: }
248: *write=0; // terminate
249: *string_ref=c? read: 0;
250: result.piece=string;
251: return result;
252: } else
253: return lsplit(string_ref, delim1, delim2);
254: }
255: result.piece=0;
256: return result;
257: }
258:
259: static void skip_empty_and_comment_lines( char** data_ref ) {
260: if(char *data=*data_ref) {
261: while( char c=*data ) {
262: if( c== '\n' || c == '#' ) {
263: /*nowhere=*/getrow(&data); // remove empty&comment lines
1.199 paf 264: if(!(*data_ref=data))
265: break;
1.187 paf 266: continue;
267: }
268: break;
269: }
270: }
271: }
272:
273: struct TableSeparators {
1.206 paf 274: char column; const String* scolumn;
275: char encloser; const String* sencloser;
276:
277: TableSeparators():
278: column('\t'), scolumn(new String("\t", false)),
279: encloser(0), sencloser(0)
280: {}
1.214 paf 281: int load( HashStringValue& options ) {
282: int result=0;
283: if(Value* vseparator=options.get(PA_COLUMN_SEPARATOR_NAME)) {
1.206 paf 284: scolumn=&vseparator->as_string();
285: if(scolumn->length()!=1)
1.226 misha 286: throw Exception(PARSER_RUNTIME,
1.206 paf 287: scolumn,
1.187 paf 288: "separator must be one character long");
1.206 paf 289: column=scolumn->first_char();
1.214 paf 290: result++;
1.187 paf 291: }
1.214 paf 292: if(Value* vencloser=options.get(PA_COLUMN_ENCLOSER_NAME)) {
1.188 paf 293: sencloser=&vencloser->as_string();
294: if(sencloser->length()!=1)
1.226 misha 295: throw Exception(PARSER_RUNTIME,
1.188 paf 296: sencloser,
1.187 paf 297: "encloser must be one character long");
1.188 paf 298: encloser=sencloser->first_char();
1.214 paf 299: result++;
1.187 paf 300: }
1.214 paf 301: return result;
1.187 paf 302: }
303: };
304:
1.182 paf 305: static void _load(Request& r, MethodParams& params) {
306: const String& first_param=params.as_string(0, "file name must be string");
1.168 paf 307: int filename_param_index=0;
308: bool nameless=first_param=="nameless";
309: if(nameless)
310: filename_param_index++;
1.182 paf 311: size_t options_param_index=filename_param_index+1;
1.186 paf 312:
313: HashStringValue *options=0;
1.187 paf 314: TableSeparators separators;
1.186 paf 315: if(options_param_index<params.count()
316: && (options=params.as_no_junction(options_param_index, "additional params must be hash").get_hash())) {
317: // cloning, so that we could change [to simplify checks on params inside file_read_text
318: options=new HashStringValue(*options);
1.187 paf 319: separators.load(*options);
1.186 paf 320: }
321:
1.44 paf 322: // loading text
1.182 paf 323: char *data=file_read_text(r.charsets,
324: r.absolute(params.as_string(filename_param_index, "file name must be string")),
1.168 paf 325: true,
1.186 paf 326: options
1.168 paf 327: );
1.44 paf 328:
1.1 paf 329: // parse columns
1.182 paf 330: Table::columns_type columns;
1.168 paf 331: if(nameless) {
1.182 paf 332: columns=Table::columns_type(0); // nameless
1.1 paf 333: } else {
1.182 paf 334: columns=Table::columns_type(new ArrayString);
1.1 paf 335:
1.187 paf 336: skip_empty_and_comment_lines(&data);
337: while( lsplit_result sr=lsplit(&data, separators.column, '\n', separators.encloser) ) {
338: *columns+=new String(sr.piece, 0, true);
339: if(sr.delim=='\n')
340: break;
1.139 paf 341: }
1.1 paf 342: }
1.187 paf 343:
344: Table& table=*new Table(columns);
1.219 paf 345: int columns_count=columns? columns->count(): 0;
1.1 paf 346:
347: // parse cells
1.218 paf 348: Table::element_type row(new ArrayString(columns_count));
1.187 paf 349: skip_empty_and_comment_lines(&data);
350: while( lsplit_result sr=lsplit(&data, separators.column, '\n', separators.encloser) ) {
1.201 paf 351: if(!*sr.piece && !sr.delim && !row->count()) // append last empty column [if without \n]
352: break;
1.187 paf 353: *row+=new String(sr.piece, 0, true);
354: if(sr.delim=='\n') {
355: table+=row;
1.218 paf 356: row=new ArrayString(columns_count);
1.187 paf 357: skip_empty_and_comment_lines(&data);
1.1 paf 358: }
1.187 paf 359: }
360: // last line [if without \n]
361: if(row->count())
1.1 paf 362: table+=row;
1.187 paf 363:
1.1 paf 364: // replace any previous table value
1.182 paf 365: GET_SELF(r, VTable).set_table(table);
1.1 paf 366: }
1.2 paf 367:
1.224 misha 368: void maybe_enclose( String& to, const String& from, char encloser, const String* sencloser ) {
1.188 paf 369: if(encloser) {
370: to<<*sencloser;
371: // while we have 'encloser'...
1.189 paf 372: size_t pos_after=0;
1.220 paf 373: for( size_t pos_before; (pos_before=from.pos( encloser, pos_after ))!=STRING_NOT_FOUND; pos_after=pos_before) {
374: pos_before++; // including first encloser (and skipping it for next pos)
375: to<<from.mid(pos_after, pos_before);
376: to<<*sencloser; // doubling encloser
1.188 paf 377: }
378: // last piece
379: size_t from_length=from.length();
380: if(pos_after<from_length)
381: to<<from.mid(pos_after, from_length);
382:
383: to<<*sencloser;
384: } else
1.202 paf 385: to<<from;
1.188 paf 386: }
387:
1.224 misha 388: void maybe_enclose( ostringstream& to, const String& from, char encloser ) {
389: if(encloser) {
390: to<<encloser;
391: // while we have 'encloser'...
392: size_t pos_after=0;
393: for( size_t pos_before; (pos_before=from.pos( encloser, pos_after ))!=STRING_NOT_FOUND; pos_after=pos_before) {
394: pos_before++; // including first encloser (and skipping it for next pos)
395: to<<from.mid(pos_after, pos_before).cstr();
396: to<<encloser; // doubling encloser
397: }
398: // last piece
399: size_t from_length=from.length();
400: if(pos_after<from_length)
401: to<<from.mid(pos_after, from_length).cstr();
402:
403: to<<encloser;
404: } else
405: to<<from.cstr();
406: }
407:
1.188 paf 408:
1.224 misha 409: static void _save_old(Request& r, MethodParams& params) {
1.188 paf 410: const String& first_arg=params.as_string(0, "first argument must not be code");
411: size_t param_index=1;
412:
413: bool do_append=false;
414: bool output_column_names=true;
415:
416: // mode?
417: if(first_arg=="append")
418: do_append=true;
419: else if(first_arg=="nameless")
420: output_column_names=false;
421: else
422: --param_index;
423:
424: const String& file_name=params.as_string(param_index++, "file name must not be code");
425:
426: TableSeparators separators;
427: if(param_index<params.count()) {
1.222 paf 428: Value& voptions=params.as_no_junction(param_index++, "additional params must be hash");
429: if( voptions.is_defined() && !voptions.is_string() ) {
430: if(HashStringValue* options=voptions.get_hash()) {
1.223 paf 431: int valid_options=separators.load(*options);
432: if(valid_options!=options->count())
1.226 misha 433: throw Exception(PARSER_RUNTIME,
1.223 paf 434: 0,
435: "invalid option passed");
436: } else {
1.226 misha 437: throw Exception(PARSER_RUNTIME,
1.188 paf 438: 0,
1.223 paf 439: "additional params must be hash (did you spell mode parameter correctly?)");
440: }
1.222 paf 441: }
1.188 paf 442: }
443: if(param_index<params.count())
1.226 misha 444: throw Exception(PARSER_RUNTIME,
1.188 paf 445: 0,
446: "bad mode (must be nameless or append)");
1.22 paf 447:
1.182 paf 448: Table& table=GET_SELF(r, VTable).table();
1.31 paf 449:
1.182 paf 450: String sdata;
1.188 paf 451: if(output_column_names) {
1.31 paf 452: if(table.columns()) { // named table
1.182 paf 453: for(Array_iterator<const String*> i(*table.columns()); i.has_next(); ) {
1.188 paf 454: maybe_enclose( sdata, *i.next(), separators.encloser, separators.sencloser );
1.98 parser 455: if(i.has_next())
1.206 paf 456: sdata<<*separators.scolumn;
1.31 paf 457: }
1.188 paf 458: } else { // nameless table [we were asked to output column names]
1.182 paf 459: if(int lsize=table.count()?table[0]->count():0)
1.31 paf 460: for(int column=0; column<lsize; column++) {
1.182 paf 461: char *cindex_tab=new(PointerFreeGC) char[MAX_NUMBER];
462: sdata.append_know_length(cindex_tab,
1.185 paf 463: snprintf(cindex_tab, MAX_NUMBER,
1.206 paf 464: column<lsize-1?"%d%c":"%d", column, separators.column),
1.185 paf 465: String::L_CLEAN);
1.31 paf 466: }
467: else
1.182 paf 468: sdata.append_help_length("empty nameless table", 0, String::L_CLEAN);
1.31 paf 469: }
1.182 paf 470: sdata.append_know_length("\n", 1, String::L_CLEAN);
1.188 paf 471: }
1.129 paf 472:
1.31 paf 473: // data lines
1.182 paf 474: Array_iterator<ArrayString*> i(table);
1.98 parser 475: while(i.has_next()) {
1.182 paf 476: for(Array_iterator<const String*> c(*i.next()); c.has_next(); ) {
1.188 paf 477: maybe_enclose( sdata, *c.next(), separators.encloser, separators.sencloser );
1.98 parser 478: if(c.has_next())
1.206 paf 479: sdata<<*separators.scolumn;
1.31 paf 480: }
1.182 paf 481: sdata.append_know_length("\n", 1, String::L_CLEAN);
1.31 paf 482: }
483:
484: // write
1.217 paf 485: {
486: const char* data_cstr=sdata.cstr();
487: file_write(r.absolute(file_name),
488: data_cstr, sdata.length(), true, do_append);
489: if(*data_cstr) // not empty (when empty it's not heap memory)
490: pa_free((void*)data_cstr); // not needed anymore
491: }
1.22 paf 492: }
493:
1.224 misha 494: static void _save(Request& r, MethodParams& params) {
495: const String& first_arg=params.as_string(0, "first argument must not be code");
496: size_t param_index=1;
497:
498: bool do_append=false;
499: bool output_column_names=true;
500:
501: // mode?
502: if(first_arg=="append")
503: do_append=true;
504: else if(first_arg=="nameless")
505: output_column_names=false;
506: else
507: --param_index;
508:
509: const String& file_name=params.as_string(param_index++, "file name must not be code");
510:
511: TableSeparators separators;
512: if(param_index<params.count()) {
513: Value& voptions=params.as_no_junction(param_index++, "additional params must be hash");
514: if( voptions.is_defined() && !voptions.is_string() ) {
515: if(HashStringValue* options=voptions.get_hash()) {
516: int valid_options=separators.load(*options);
517: if(valid_options!=options->count())
1.226 misha 518: throw Exception(PARSER_RUNTIME,
1.224 misha 519: 0,
520: "invalid option passed");
521: } else {
1.226 misha 522: throw Exception(PARSER_RUNTIME,
1.224 misha 523: 0,
524: "additional params must be hash (did you spell mode parameter correctly?)");
525: }
526: }
527: }
528: if(param_index<params.count())
1.226 misha 529: throw Exception(PARSER_RUNTIME,
1.224 misha 530: 0,
531: "bad mode (must be nameless or append)");
532:
533: Table& table=GET_SELF(r, VTable).table();
534:
535: ostringstream ost(stringstream::out);
536:
537: // process header
538: if(output_column_names) {
539: if(table.columns()) { // named table
540: for(Array_iterator<const String*> i(*table.columns()); i.has_next(); ) {
541: maybe_enclose( ost, *i.next(), separators.encloser );
542: if(i.has_next()){
543: ost<<separators.column;
544: }
545: }
546: } else { // nameless table [we were asked to output column names]
547: if(int lsize=table.count()?table[0]->count():0)
548: for(int column=0; column<lsize; column++) {
1.225 misha 549: if(separators.encloser) {
550: ost<<separators.encloser<<column<<separators.encloser;
551: } else {
1.224 misha 552: ost<<column;
1.225 misha 553: }
1.224 misha 554: if(column<lsize-1){
555: ost<<separators.column;
556: }
557: }
558: else
559: ost<<"empty nameless table";
560: }
561: ost<<'\n';
562: }
563:
564: // process data lines
565: Array_iterator<ArrayString*> i(table);
566: while(i.has_next()) {
567: for(Array_iterator<const String*> c(*i.next()); c.has_next(); ) {
568: maybe_enclose( ost, *c.next(), separators.encloser );
569: if(c.has_next())
570: ost<<separators.column;
571: }
572: ost<<'\n';
573: }
574:
575: // write
576: {
577: string data=ost.str();
578: const char* data_cstr = data.c_str();
579:
580: file_write(r.absolute(file_name), data_cstr, data.length(), true /* as text */, do_append);
581: }
582: }
583:
1.182 paf 584: static void _count(Request& r, MethodParams&) {
585: int result=GET_SELF(r, VTable).table().count();
586: r.write_no_lang(*new VInt(result));
1.6 paf 587: }
588:
1.182 paf 589: static void _line(Request& r, MethodParams&) {
590: int result=1+GET_SELF(r, VTable).table().current();
591: r.write_no_lang(*new VInt(result));
1.6 paf 592: }
593:
1.182 paf 594: static void _offset(Request& r, MethodParams& params) {
595: Table& table=GET_SELF(r, VTable).table();
596: if(params.count()) {
1.133 paf 597: bool absolute=false;
1.182 paf 598: if(params.count()>1) {
599: const String& whence=params.as_string(0, "whence must be string");
1.133 paf 600: if(whence=="cur")
1.134 paf 601: absolute=false;
1.133 paf 602: else if(whence=="set")
1.134 paf 603: absolute=true;
1.133 paf 604: else
1.226 misha 605: throw Exception(PARSER_RUNTIME,
1.134 paf 606: &whence,
607: "is invalid whence, valid are 'cur' or 'set'");
1.157 paf 608: }
1.133 paf 609:
1.211 paf 610: int offset=params.as_int(params.count()-1, "offset must be expression", r);
611: table.offset(absolute, offset);
1.151 paf 612: } else
1.182 paf 613: r.write_no_lang(*new VInt(table.current()));
1.6 paf 614: }
615:
1.182 paf 616: static void _menu(Request& r, MethodParams& params) {
1.221 paf 617: Temp_hash_value<const String::Body, void*>
618: cycle_data_setter(r.classes_conf, cycle_data_name, /*any not null flag*/&r);
619:
1.182 paf 620: Value& body_code=params.as_junction(0, "body must be code");
1.7 paf 621:
1.182 paf 622: Value* delim_maybe_code=params.count()>1?¶ms[1]:0;
1.7 paf 623:
1.182 paf 624: Table& table=GET_SELF(r, VTable).table();
1.7 paf 625: bool need_delim=false;
1.99 parser 626: int saved_current=table.current();
1.182 paf 627: int size=table.count();
1.99 parser 628: for(int row=0; row<size; row++) {
1.22 paf 629: table.set_current(row);
1.7 paf 630:
1.161 paf 631: StringOrValue sv_processed=r.process(body_code);
1.221 paf 632: Request::Skip lskip=r.get_skip(); r.set_skip(Request::SKIP_NOTHING);
1.182 paf 633: const String* s_processed=sv_processed.get_string();
634: if(delim_maybe_code && s_processed && s_processed->length()) { // delimiter set and we have body
1.161 paf 635: if(need_delim) // need delim & iteration produced string?
1.150 paf 636: r.write_pass_lang(r.process(*delim_maybe_code));
1.7 paf 637: need_delim=true;
638: }
1.161 paf 639: r.write_pass_lang(sv_processed);
1.221 paf 640:
641: if(lskip==Request::SKIP_BREAK)
642: break;
1.7 paf 643: }
1.99 parser 644: table.set_current(saved_current);
1.7 paf 645: }
646:
1.110 parser 647: #ifndef DOXYGEN
1.227 ! misha 648: enum Table2hash_distint { D_ILLEGAL, D_FIRST };
! 649: enum Table2hash_value_type { C_HASH, C_STRING, C_TABLE };
1.74 paf 650: struct Row_info {
1.166 paf 651: Request *r;
1.74 paf 652: Table *table;
1.182 paf 653: Value* key_code;
654: size_t key_field;
655: Array<int>* value_fields;
656: HashStringValue* hash;
1.174 paf 657: Table2hash_distint distinct;
1.182 paf 658: size_t row;
1.227 ! misha 659: Table2hash_value_type value_type;
1.74 paf 660: };
1.110 parser 661: #endif
1.182 paf 662: static void table_row_to_hash(Table::element_type row, Row_info *info) {
663: const String* key;
664: if(info->key_code) {
665: info->table->set_current(info->row++); // change context row
666: StringOrValue sv_processed=info->r->process(*info->key_code);
1.166 paf 667: key=&sv_processed.as_string();
1.227 ! misha 668: } else {
1.182 paf 669: key=info->key_field<row->count()?row->get(info->key_field):0;
1.227 ! misha 670: }
1.166 paf 671:
672: if(!key)
673: return; // ignore rows without key [too-short-record_array if-indexed]
1.74 paf 674:
1.227 ! misha 675: bool exist = false;
! 676: switch (info->value_type) {
! 677: case C_STRING:
! 678: {
! 679: exist=info->hash->put_dont_replace(*key, new VString(*row->get(info->value_fields->get(0))));
! 680: }
! 681: break;
! 682: case C_HASH:
1.174 paf 683: {
1.182 paf 684: VHash* vhash=new VHash;
685: HashStringValue& hash=vhash->hash();
686: for(Array_iterator<int> i(*info->value_fields); i.has_next(); ) {
687: size_t value_field=i.next();
688: if(value_field<row->count())
1.174 paf 689: hash.put(
1.182 paf 690: *info->table->columns()->get(value_field),
691: new VString(*row->get(value_field)));
1.174 paf 692: }
693:
1.227 ! misha 694: exist=info->hash->put_dont_replace(*key, vhash);
1.174 paf 695: }
696: break;
1.227 ! misha 697: case C_TABLE:
1.174 paf 698: {
1.182 paf 699: VTable* vtable=(VTable*)info->hash->get(*key); // put. table existed?
1.227 ! misha 700: if( info->distinct==D_ILLEGAL ){
! 701: exist=true;
! 702: break;
! 703: }
! 704:
1.174 paf 705: Table* table;
1.227 ! misha 706: if(vtable) {
1.174 paf 707: table=vtable->get_table();
1.227 ! misha 708: } else {
1.174 paf 709: // no? creating table of same structure as source
1.179 paf 710: Table::Action_options table_options(0, 0);
1.182 paf 711: table=new Table(*info->table, table_options/*no rows, just structure*/);
712: info->hash->put(*key, new VTable(table));
1.174 paf 713: }
1.182 paf 714: *table+=row;
1.174 paf 715: }
716: break;
1.74 paf 717: }
1.227 ! misha 718: if(exist && info->distinct==D_ILLEGAL)
! 719: throw Exception(PARSER_RUNTIME,
! 720: key,
! 721: "duplicate key");
1.74 paf 722: }
1.182 paf 723: static void _hash(Request& r, MethodParams& params) {
724: Table& self_table=GET_SELF(r, VTable).table();
725: VHash& result=*new VHash;
726: if(Table::columns_type columns=self_table.columns())
727: if(columns->count()>0) {
1.174 paf 728: Table2hash_distint distinct=D_ILLEGAL;
1.227 ! misha 729: Table2hash_value_type value_type=C_HASH;
1.182 paf 730: int param_index=params.count()-1;
1.166 paf 731: if(param_index>0) {
1.182 paf 732: if(HashStringValue* options=
1.227 ! misha 733: params.as_no_junction(param_index, "param must not be code").get_hash()
! 734: ){
1.166 paf 735: --param_index;
736: int valid_options=0;
1.182 paf 737: if(Value* vdistinct_code=options->get(sql_distinct_name)) {
1.166 paf 738: valid_options++;
1.174 paf 739: Value& vdistinct_value=r.process_to_value(*vdistinct_code);
740: if(vdistinct_value.is_string()) {
741: const String& sdistinct=*vdistinct_value.get_string();
1.227 ! misha 742: if(sdistinct=="tables") {
! 743: value_type=C_TABLE;
! 744: distinct=D_FIRST;
! 745: } else
1.226 misha 746: throw Exception(PARSER_RUNTIME,
1.174 paf 747: &sdistinct,
748: "must be 'tables' or true/false");
749: } else
750: distinct=vdistinct_value.as_bool()?D_FIRST:D_ILLEGAL;
1.166 paf 751: }
1.227 ! misha 752: if(Value* vvalue_type_code=options->get(sql_value_type_name)) {
! 753: if(value_type==C_TABLE){ // $.distinct[tables] was specified
! 754: throw Exception(PARSER_RUNTIME,
! 755: 0,
! 756: "you can't specify $.distinct[tables] and $.values[] together.");
! 757: } else {
! 758: valid_options++;
! 759: Value& vvalue_type_value=r.process_to_value(*vvalue_type_code);
! 760: if(vvalue_type_value.is_string()) {
! 761: const String& svalue_type=*vvalue_type_value.get_string();
! 762: if(svalue_type == "table"){
! 763: value_type=C_TABLE;
! 764: } else if (svalue_type == "string") {
! 765: value_type=C_STRING;
! 766: } else if (svalue_type == "hash") {
! 767: value_type=C_HASH;
! 768: } else {
! 769: throw Exception(PARSER_RUNTIME,
! 770: &svalue_type,
! 771: "must be 'hash', 'table' or 'string'");
! 772: }
! 773: }
! 774: }
! 775: }
1.226 misha 776:
1.182 paf 777: if(valid_options!=options->count())
1.226 misha 778: throw Exception(PARSER_RUNTIME,
1.182 paf 779: 0,
1.166 paf 780: "called with invalid option");
1.163 paf 781: }
782: }
783: if(param_index==2) // bad options param type
1.226 misha 784: throw Exception(PARSER_RUNTIME,
1.182 paf 785: 0,
1.163 paf 786: "options must be hash");
787:
1.182 paf 788: Array<int> value_fields;
1.163 paf 789: if(param_index>0) {
1.227 ! misha 790: if(value_type==C_TABLE)
! 791: throw Exception(PARSER_RUNTIME,
1.174 paf 792: 0,
793: "in distinct[tables] mode you may not specify value field(s)");
1.182 paf 794: Value& value_fields_param=params.as_no_junction(param_index, "value field(s) must not be code");
1.123 parser 795: if(value_fields_param.is_string()) {
1.182 paf 796: value_fields+=self_table.column_name2index(
797: *value_fields_param.get_string(), true);
798: } else if(Table* value_fields_table=value_fields_param.get_table()) {
799: for(Array_iterator<Table::element_type> i(*value_fields_table);
800: i.has_next(); ) {
801: const String& value_field_name
802: =*i.next()->get(0);
803: value_fields
804: +=self_table.column_name2index(value_field_name, true);
1.123 parser 805: }
806: } else
1.226 misha 807: throw Exception(PARSER_RUNTIME,
1.182 paf 808: 0,
1.226 misha 809: "value field(s) must be string or table");
810:
1.123 parser 811: } else { // by all columns, including key
1.227 ! misha 812: if(value_type==C_STRING)
! 813: throw Exception(PARSER_RUNTIME,
! 814: 0,
! 815: "with $.values[string] you must specify one value field(s)");
! 816: // if(!(distinct!=D_ILLEGAL && distinct!=D_FIRST))
1.182 paf 817: for(size_t i=0; i<columns->count(); i++)
1.174 paf 818: value_fields+=i;
1.74 paf 819: }
820:
1.227 ! misha 821: if(value_type==C_STRING && value_fields.count()!=1)
! 822: throw Exception(PARSER_RUNTIME,
! 823: 0,
! 824: "you can specify one value field with this $.type[].");
1.182 paf 825:
826: {
827: Value* key_param=¶ms[0];
1.193 paf 828: Row_info info={
829: &r,
830: &self_table,
831: /*key_code=*/key_param->get_junction()?key_param:0,
1.197 paf 832: /*key_field=*/0/*filled below*/,
1.193 paf 833: &value_fields,
834: &result.hash(),
835: distinct,
1.227 ! misha 836: /*row=*/0,
! 837: value_type
1.193 paf 838: };
1.195 paf 839: info.key_field=(info.key_code?-1
840: :self_table.column_name2index(key_param->as_string(), true));
1.182 paf 841:
842: int saved_current=self_table.current();
843: self_table.for_each(table_row_to_hash, &info);
844: self_table.set_current(saved_current);
1.207 paf 845:
846: result.extract_default();
1.182 paf 847: }
1.74 paf 848: }
1.97 parser 849: r.write_no_lang(result);
1.74 paf 850: }
851:
1.110 parser 852: #ifndef DOXYGEN
1.78 paf 853: struct Table_seq_item {
1.182 paf 854: ArrayString* row;
1.34 paf 855: union {
1.182 paf 856: const char *c_str;
1.34 paf 857: double d;
858: } value;
1.32 paf 859: };
1.110 parser 860: #endif
1.34 paf 861: static int sort_cmp_string(const void *a, const void *b) {
862: return strcmp(
1.78 paf 863: static_cast<const Table_seq_item *>(a)->value.c_str,
864: static_cast<const Table_seq_item *>(b)->value.c_str
1.34 paf 865: );
866: }
867: static int sort_cmp_double(const void *a, const void *b) {
1.78 paf 868: double va=static_cast<const Table_seq_item *>(a)->value.d;
869: double vb=static_cast<const Table_seq_item *>(b)->value.d;
1.34 paf 870: if(va<vb)
871: return -1;
872: else if(va>vb)
873: return +1;
874: else
875: return 0;
876: }
1.182 paf 877: static void _sort(Request& r, MethodParams& params) {
878: Value& key_maker=params.as_junction(0, "key-maker must be code");
1.61 paf 879:
1.182 paf 880: bool reverse=params.count()>1/*..[desc|asc|]*/?
881: reverse=params.as_no_junction(1, "order must not be code").as_string()=="desc":
1.104 parser 882: false; // default=asc
1.32 paf 883:
1.182 paf 884: Table& old_table=GET_SELF(r, VTable).table();
885: Table& new_table=*new Table(old_table.columns());
1.34 paf 886:
1.182 paf 887: Table_seq_item* seq=new(PointerFreeGC) Table_seq_item[old_table.count()];
1.34 paf 888: int i;
889:
890: // calculate key values
891: bool key_values_are_strings=true;
1.182 paf 892: int old_count=old_table.count();
893: for(i=0; i<old_count; i++) {
1.101 parser 894: old_table.set_current(i);
1.32 paf 895: // calculate key value
1.182 paf 896: seq[i].row=old_table[i];
897: Value& value=r.process_to_value(key_maker).as_expr_result(true/*return string as-is*/);
1.34 paf 898: if(i==0) // determining key values type by first one
899: key_values_are_strings=value.is_string();
900:
901: if(key_values_are_strings)
902: seq[i].value.c_str=value.as_string().cstr();
903: else
904: seq[i].value.d=value.as_double();
1.32 paf 905: }
906: // sort keys
1.182 paf 907: _qsort(seq, old_count, sizeof(Table_seq_item),
1.34 paf 908: key_values_are_strings?sort_cmp_string:sort_cmp_double);
1.32 paf 909:
1.34 paf 910: // reorder table as they require in 'seq'
1.182 paf 911: for(i=0; i<old_count; i++)
912: new_table+=Table::element_type(seq[reverse?old_count-1-i:i].row);
913:
914: delete[] seq;
1.32 paf 915:
1.116 parser 916: // replace any previous table value
1.182 paf 917: GET_SELF(r, VTable).set_table(new_table);
1.32 paf 918: }
919:
1.176 paf 920: #ifndef DOXYGEN
1.182 paf 921: struct Expression_is_true_info {
1.176 paf 922: Request* r;
923: Value* expression_code;
924: };
925: #endif
1.191 paf 926: static bool expression_is_true(Table&, Expression_is_true_info* info) {
927: return info->r->process_to_value(*info->expression_code).as_bool();
1.176 paf 928: }
929: static bool _locate_expression(Table& table, Table::Action_options o,
1.182 paf 930: Request& r, MethodParams& params) {
931: check_option_param(o.defined, params, 1,
1.176 paf 932: "locate by expression only has parameters: expression and, maybe, options");
1.182 paf 933: Value& expression_code=params.as_junction(0, "must be expression");
1.145 paf 934:
1.182 paf 935: Expression_is_true_info info={&r, &expression_code};
936: return table.table_first_that(expression_is_true, &info, o);
1.145 paf 937: }
1.176 paf 938: static bool _locate_name_value(Table& table, Table::Action_options o,
1.191 paf 939: Request&, MethodParams& params) {
1.182 paf 940: check_option_param(o.defined, params, 2,
1.176 paf 941: "locate by locate by name has parameters: name, value and, maybe, options");
1.182 paf 942: const String& name=params.as_string(0, "column name must be string");
943: const String& value=params.as_string(1, "value must be string");
944: return table.locate(name, value, o);
945: }
946: static void _locate(Request& r, MethodParams& params) {
947: Table& table=GET_SELF(r, VTable).table();
1.72 paf 948:
1.182 paf 949: Table::Action_options o=get_action_options(r, params, table);
950:
951: bool result=params[0].get_junction()?
952: _locate_expression(table, o, r, params) :
953: _locate_name_value(table, o, r, params);
954: r.write_no_lang(*new VBool(result));
1.145 paf 955: }
1.182 paf 956:
957:
1.191 paf 958: static void _flip(Request& r, MethodParams&) {
1.182 paf 959: Table& old_table=GET_SELF(r, VTable).table();
1.184 paf 960: Table& new_table=*new Table(0);
1.182 paf 961: if(size_t old_count=old_table.count())
962: if(size_t old_cols=old_table[0]->count())
963: for(size_t column=0; column<old_cols; column++) {
964: Table::element_type new_row(new ArrayString(old_count));
965: for(size_t i=0; i<old_count; i++) {
966: Table::element_type old_row=old_table[i];
967: *new_row+=column<old_row->count()?old_row->get(column):new String;
1.39 paf 968: }
1.182 paf 969: new_table+=new_row;
1.39 paf 970: }
971:
1.182 paf 972: r.write_no_lang(*new VTable(&new_table));
1.39 paf 973: }
974:
1.182 paf 975: static void _append(Request& r, MethodParams& params) {
1.134 paf 976: // data
1.182 paf 977: Temp_lang temp_lang(r, String::L_PASS_APPENDED);
978: const String& string=r.process_to_string(params.as_junction(0, "body must be code"));
1.41 paf 979:
980: // parse cells
1.182 paf 981: Table::element_type row=new ArrayString;
982: size_t pos_after=0;
983: string.split(*row, pos_after, "\t", String::L_AS_IS);
1.41 paf 984:
1.182 paf 985: GET_SELF(r, VTable).table()+=row;
1.41 paf 986: }
987:
1.182 paf 988: static void join_named_row(Table& src, Table* dest) {
989: Table::columns_type dest_columns=dest->columns();
990: size_t dest_columns_count=dest_columns->count();
991: Table::element_type dest_row(new ArrayString(dest_columns_count));
992: for(size_t dest_column=0; dest_column<dest_columns_count; dest_column++) {
993: const String* src_item=src.item(*dest_columns->get(dest_column));
994: *dest_row+=src_item?src_item:new String;
995: }
996: *dest+=dest_row;
997: }
998: static void join_nameless_row(Table& src, Table* dest) {
999: *dest+=src[src.current()];
1000: }
1001: static void _join(Request& r, MethodParams& params) {
1002: Table* maybe_src=params.as_no_junction(0, "table ref must not be code").get_table();
1.42 paf 1003: if(!maybe_src)
1.226 misha 1004: throw Exception(PARSER_RUNTIME,
1.182 paf 1005: 0,
1.42 paf 1006: "source is not a table");
1.176 paf 1007: Table& src=*maybe_src;
1008:
1.182 paf 1009: Table::Action_options o=get_action_options(r, params, src);
1010: check_option_param(o.defined, params, 1,
1.176 paf 1011: "invalid extra parameter");
1.42 paf 1012:
1.182 paf 1013: Table& dest=GET_SELF(r, VTable).table();
1.42 paf 1014: if(&src == &dest)
1.226 misha 1015: throw Exception(PARSER_RUNTIME,
1.182 paf 1016: 0,
1.42 paf 1017: "source and destination are same table");
1018:
1.193 paf 1019: if(dest.columns()) // dest is named
1.182 paf 1020: src.table_for_each(join_named_row, &dest, o);
1021: else // dest is nameless
1022: src.table_for_each(join_nameless_row, &dest, o);
1.42 paf 1023: }
1024:
1.94 parser 1025: #ifndef DOXYGEN
1.169 paf 1026: class Table_sql_event_handlers: public SQL_Driver_query_event_handlers {
1.182 paf 1027: ArrayString& columns;
1.218 paf 1028: int columns_count;
1.182 paf 1029: ArrayString* row;
1.94 parser 1030: public:
1.182 paf 1031: Table* table;
1032: public:
1033: Table_sql_event_handlers() :
1034: columns(*new ArrayString), row(0), table(0) {
1.94 parser 1035: }
1036:
1.182 paf 1037: bool add_column(SQL_Error& error, const char *str, size_t length) {
1.170 paf 1038: try {
1.182 paf 1039: columns+=new String(str, length, true);
1.170 paf 1040: return false;
1041: } catch(...) {
1042: error=SQL_Error("exception occured in Table_sql_event_handlers::add_column");
1043: return true;
1044: }
1045: }
1046: bool before_rows(SQL_Error& error) {
1047: try {
1.182 paf 1048: table=new Table(&columns);
1.218 paf 1049: columns_count=columns.count();
1.170 paf 1050: return false;
1051: } catch(...) {
1052: error=SQL_Error("exception occured in Table_sql_event_handlers::before_rows");
1053: return true;
1054: }
1055: }
1056: bool add_row(SQL_Error& error) {
1057: try {
1.218 paf 1058: *table+=row=new ArrayString(columns_count);
1.170 paf 1059: return false;
1060: } catch(...) {
1061: error=SQL_Error("exception occured in Table_sql_event_handlers::add_row");
1062: return true;
1063: }
1064: }
1.182 paf 1065: bool add_row_cell(SQL_Error& error, const char* str, size_t length) {
1.170 paf 1066: try {
1.182 paf 1067: String& cell=*new String;
1068: if(length)
1069: cell.append_know_length(str, length, String::L_TAINTED);
1070: *row+=&cell;
1.170 paf 1071: return false;
1072: } catch(...) {
1073: error=SQL_Error("exception occured in Table_sql_event_handlers::add_row_cell");
1074: return true;
1075: }
1.94 parser 1076: }
1077: };
1078: #endif
1.204 paf 1079:
1080: static void marshal_bind(
1081: HashStringValue::key_type aname,
1082: HashStringValue::value_type avalue,
1083: SQL_Driver::Placeholder** pptr)
1084: {
1085: SQL_Driver::Placeholder& ph=**pptr;
1086: ph.name=aname.cstr();
1.205 paf 1087: ph.value=avalue->as_string().cstr(String::L_UNSPECIFIED);
1.204 paf 1088: ph.is_null=avalue->get_class()==void_class;
1089: ph.were_updated=false;
1090:
1091: (*pptr)++;
1092: }
1093:
1094: // not static, used elsewhere
1095: int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders) {
1096: int hash_count=hash.count();
1097: placeholders=new(UseGC) SQL_Driver::Placeholder[hash_count];
1098: SQL_Driver::Placeholder* ptr=placeholders;
1.221 paf 1099: hash.for_each<SQL_Driver::Placeholder**>(marshal_bind, &ptr);
1.204 paf 1100: return hash_count;
1101: }
1102:
1103: // not static, used elsewhere
1104: void unmarshal_bind_updates(HashStringValue& hash, int placeholder_count, SQL_Driver::Placeholder* placeholders) {
1105: SQL_Driver::Placeholder* ph=placeholders;
1106: for(int i=0; i<placeholder_count; i++, ph++)
1107: if(ph->were_updated) {
1108: Value* value;
1109: if(ph->is_null)
1110: value=new VVoid();
1111: else
1112: if(ph->value)
1113: value=new VString(*new String(ph->value, 0, true/*tainted*/));
1114: else
1115: value=new VString(*new String());
1116: hash.put(ph->name, value);
1117: }
1118: }
1119:
1.182 paf 1120: static void _sql(Request& r, MethodParams& params) {
1121: Value& statement=params.as_junction(0, "statement must be code");
1.49 paf 1122:
1.204 paf 1123: HashStringValue* bind=0;
1.53 paf 1124: ulong limit=0;
1.100 parser 1125: ulong offset=0;
1.182 paf 1126: if(params.count()>1) {
1127: Value& voptions=params.as_no_junction(1, "options must be hash, not code");
1.212 paf 1128: if(voptions.is_defined() && !voptions.is_string())
1.182 paf 1129: if(HashStringValue* options=voptions.get_hash()) {
1.164 paf 1130: int valid_options=0;
1.204 paf 1131: if(Value* vbind=options->get(sql_bind_name)) {
1132: valid_options++;
1133: bind=vbind->get_hash();
1134: }
1.182 paf 1135: if(Value* vlimit=options->get(sql_limit_name)) {
1.164 paf 1136: valid_options++;
1.147 paf 1137: limit=(ulong)r.process_to_value(*vlimit).as_double();
1.164 paf 1138: }
1.182 paf 1139: if(Value* voffset=options->get(sql_offset_name)) {
1.164 paf 1140: valid_options++;
1.147 paf 1141: offset=(ulong)r.process_to_value(*voffset).as_double();
1.164 paf 1142: }
1.182 paf 1143: if(valid_options!=options->count())
1.226 misha 1144: throw Exception(PARSER_RUNTIME,
1.182 paf 1145: 0,
1.164 paf 1146: "called with invalid option");
1.109 parser 1147: } else
1.226 misha 1148: throw Exception(PARSER_RUNTIME,
1.182 paf 1149: 0,
1.109 parser 1150: "options must be hash");
1.49 paf 1151: }
1152:
1.204 paf 1153: SQL_Driver::Placeholder* placeholders=0;
1154: uint placeholders_count=0;
1155: if(bind)
1156: placeholders_count=marshal_binds(*bind, placeholders);
1157:
1.182 paf 1158: Temp_lang temp_lang(r, String::L_SQL);
1159: const String& statement_string=r.process_to_string(statement);
1160: const char* statement_cstr=
1161: statement_string.cstr(String::L_UNSPECIFIED, r.connection());
1162: Table_sql_event_handlers handlers;
1.135 paf 1163: #ifdef RESOURCES_DEBUG
1164: struct timeval mt[2];
1165: //measure:before
1166: gettimeofday(&mt[0],NULL);
1167: #endif
1.182 paf 1168: r.connection()->query(
1.203 paf 1169: statement_cstr,
1.208 paf 1170: placeholders_count, placeholders,
1.203 paf 1171: offset, limit,
1.160 paf 1172: handlers,
1173: statement_string);
1.135 paf 1174:
1175: #ifdef RESOURCES_DEBUG
1176: //measure:after connect
1177: gettimeofday(&mt[1],NULL);
1178:
1179: double t[2];
1180: for(int i=0;i<2;i++)
1181: t[i]=mt[i].tv_sec+mt[i].tv_usec/1000000.0;
1182:
1183: r.sql_request_time+=t[1]-t[0];
1184: #endif
1.204 paf 1185:
1186: if(bind)
1187: unmarshal_bind_updates(*bind, placeholders_count, placeholders);
1.96 parser 1188:
1.182 paf 1189: Table& result=
1190: handlers.table?*handlers.table: // query resulted in table? return it
1191: *new Table(Table::columns_type(0)); // query returned no table, fake it
1.49 paf 1192:
1193: // replace any previous table value
1.182 paf 1194: GET_SELF(r, VTable).set_table(result);
1.49 paf 1195: }
1196:
1.182 paf 1197: static void _columns(Request& r, MethodParams&) {
1.88 parser 1198:
1.182 paf 1199: Table::columns_type result_columns(new ArrayString);
1200: *result_columns+=new String("column");
1201: Table& result_table=*new Table(result_columns);
1.88 parser 1202:
1.182 paf 1203: Table& source_table=GET_SELF(r, VTable).table();
1204: if(Table::columns_type source_columns=source_table.columns()) {
1205: for(Array_iterator<const String*> i(*source_columns); i.has_next(); ) {
1206: Table::element_type result_row(new ArrayString);
1207: *result_row+=i.next();
1208: result_table+=result_row;
1.88 parser 1209: }
1210: }
1211:
1.182 paf 1212: r.write_no_lang(*new VTable(&result_table));
1.88 parser 1213: }
1214:
1.182 paf 1215: static void _select(Request& r, MethodParams& params) {
1216: Value& vcondition=params.as_junction(0, "condition must be expression");
1.148 paf 1217:
1.182 paf 1218: Table& source_table=GET_SELF(r, VTable).table();
1219: Table& result_table=*new Table(source_table.columns());
1.148 paf 1220:
1221: int saved_current=source_table.current();
1.182 paf 1222: int size=source_table.count();
1.148 paf 1223: for(int row=0; row<size; row++) {
1224: source_table.set_current(row);
1225:
1.150 paf 1226: bool condition=r.process_to_value(vcondition,
1.148 paf 1227: false/*don't intercept string*/).as_bool();
1228:
1229: if(condition) // ...condition is true=
1.182 paf 1230: result_table+=source_table[row]; // =green light to go to result
1.148 paf 1231: }
1232: source_table.set_current(saved_current);
1233:
1.182 paf 1234: r.write_no_lang(*new VTable(&result_table));
1.148 paf 1235: }
1236:
1.66 paf 1237: // constructor
1238:
1.182 paf 1239: MTable::MTable(): Methoded("table") {
1.142 paf 1240: // ^table::create{data}
1241: // ^table::create[nameless]{data}
1242: // ^table::create[table]
1243: add_native_method("create", Method::CT_DYNAMIC, _create, 1, 2);
1244: // old name for compatibility with <= v 1.141 2002/01/25 11:33:45 paf
1245: add_native_method("set", Method::CT_DYNAMIC, _create, 1, 2);
1.2 paf 1246:
1.142 paf 1247: // ^table::load[file]
1248: // ^table::load[nameless;file]
1.168 paf 1249: add_native_method("load", Method::CT_DYNAMIC, _load, 1, 3);
1.22 paf 1250:
1251: // ^table.save[file]
1252: // ^table.save[nameless;file]
1.188 paf 1253: add_native_method("save", Method::CT_DYNAMIC, _save, 1, 3);
1.6 paf 1254:
1.224 misha 1255: // add_native_method("save_old", Method::CT_DYNAMIC, _save_old, 1, 3);
1256:
1.6 paf 1257: // ^table.count[]
1.66 paf 1258: add_native_method("count", Method::CT_DYNAMIC, _count, 0, 0);
1.6 paf 1259:
1260: // ^table.line[]
1.66 paf 1261: add_native_method("line", Method::CT_DYNAMIC, _line, 0, 0);
1.6 paf 1262:
1.10 paf 1263: // ^table.offset[]
1.133 paf 1264: // ^table.offset(offset)
1265: // ^table.offset[cur|set](offset)
1266: add_native_method("offset", Method::CT_DYNAMIC, _offset, 0, 2);
1.7 paf 1267:
1.10 paf 1268: // ^table.menu{code}
1269: // ^table.menu{code}[delim]
1.66 paf 1270: add_native_method("menu", Method::CT_DYNAMIC, _menu, 1, 2);
1.74 paf 1271:
1.79 paf 1272: // ^table:hash[key field name]
1.123 parser 1273: // ^table:hash[key field name][value field name(s) string/table]
1.163 paf 1274: add_native_method("hash", Method::CT_DYNAMIC, _hash, 1, 3);
1.32 paf 1275:
1.102 parser 1276: // ^table.sort{string-key-maker} ^table.sort{string-key-maker}[desc|asc]
1277: // ^table.sort(numeric-key-maker) ^table.sort(numeric-key-maker)[desc|asc]
1.66 paf 1278: add_native_method("sort", Method::CT_DYNAMIC, _sort, 1, 2);
1.8 paf 1279:
1.36 paf 1280: // ^table.locate[field;value]
1.176 paf 1281: add_native_method("locate", Method::CT_DYNAMIC, _locate, 1, 3);
1.39 paf 1282:
1283: // ^table.flip[]
1.66 paf 1284: add_native_method("flip", Method::CT_DYNAMIC, _flip, 0, 0);
1.41 paf 1285:
1286: // ^table.append{r{tab}e{tab}c{tab}o{tab}r{tab}d}
1.66 paf 1287: add_native_method("append", Method::CT_DYNAMIC, _append, 1, 1);
1.42 paf 1288:
1.157 paf 1289: // ^table.join[table][$.limit(10) $.offset(1) $.offset[cur] ]
1290: add_native_method("join", Method::CT_DYNAMIC, _join, 1, 2);
1.49 paf 1291:
1292:
1.100 parser 1293: // ^table:sql[query]
1294: // ^table:sql[query][$.limit(1) $.offset(2)]
1295: add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
1.88 parser 1296:
1297: // ^table:columns[]
1298: add_native_method("columns", Method::CT_DYNAMIC, _columns, 0, 0);
1.148 paf 1299:
1300: // ^table.select(expression) = table
1301: add_native_method("select", Method::CT_DYNAMIC, _select, 1, 1);
1.40 paf 1302: }
E-mail: