|
|
| version 1.196, 2004/02/11 15:33:13 | version 1.210, 2005/08/09 08:14:48 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b table parser class. | Parser: @b table parser class. |
| Copyright (c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com) | Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) |
| Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| */ | */ |
| Line 21 static const char * const IDENT_TABLE_C= | Line 21 static const char * const IDENT_TABLE_C= |
| class MTable: public Methoded { | class MTable: public Methoded { |
| public: // VStateless_class | public: // VStateless_class |
| Value* create_new_value(Pool&) { return new VTable(); } | Value* create_new_value(Pool&, HashStringValue&) { return new VTable(); } |
| public: | public: |
| MTable(); | MTable(); |
| Line 36 DECLARE_CLASS_VAR(table, new MTable, 0); | Line 36 DECLARE_CLASS_VAR(table, new MTable, 0); |
| // defines for globals | // defines for globals |
| #define SQL_BIND_NAME "bind" | |
| #define SQL_LIMIT_NAME "limit" | #define SQL_LIMIT_NAME "limit" |
| #define SQL_OFFSET_NAME "offset" | #define SQL_OFFSET_NAME "offset" |
| #define SQL_DEFAULT_NAME "default" | #define SQL_DEFAULT_NAME "default" |
| Line 44 DECLARE_CLASS_VAR(table, new MTable, 0); | Line 45 DECLARE_CLASS_VAR(table, new MTable, 0); |
| // globals | // globals |
| String sql_bind_name(SQL_BIND_NAME); | |
| String sql_limit_name(SQL_LIMIT_NAME); | String sql_limit_name(SQL_LIMIT_NAME); |
| String sql_offset_name(SQL_OFFSET_NAME); | String sql_offset_name(SQL_OFFSET_NAME); |
| String sql_default_name(SQL_DEFAULT_NAME); | String sql_default_name(SQL_DEFAULT_NAME); |
| Line 251 static void skip_empty_and_comment_lines | Line 253 static void skip_empty_and_comment_lines |
| while( char c=*data ) { | while( char c=*data ) { |
| if( c== '\n' || c == '#' ) { | if( c== '\n' || c == '#' ) { |
| /*nowhere=*/getrow(&data); // remove empty&comment lines | /*nowhere=*/getrow(&data); // remove empty&comment lines |
| *data_ref=data; | if(!(*data_ref=data)) |
| break; | |
| continue; | continue; |
| } | } |
| break; | break; |
| Line 260 static void skip_empty_and_comment_lines | Line 263 static void skip_empty_and_comment_lines |
| } | } |
| struct TableSeparators { | struct TableSeparators { |
| char column; | char column; const String* scolumn; |
| char encloser; | char encloser; const String* sencloser; |
| const String* sencloser; | |
| TableSeparators(): | |
| TableSeparators() { | column('\t'), scolumn(new String("\t", false)), |
| column='\t'; | encloser(0), sencloser(0) |
| encloser=0; | {} |
| } | |
| void load( HashStringValue& options ) { | void load( HashStringValue& options ) { |
| if(Value* vseparator=options.get(COLUMN_SEPARATOR_NAME)) { | if(Value* vseparator=options.get(COLUMN_SEPARATOR_NAME)) { |
| options.remove(COLUMN_SEPARATOR_NAME); | options.remove(COLUMN_SEPARATOR_NAME); |
| const String& sseparator=vseparator->as_string(); | scolumn=&vseparator->as_string(); |
| if(sseparator.length()!=1) | if(scolumn->length()!=1) |
| throw Exception("parser.runtime", | throw Exception("parser.runtime", |
| &sseparator, | scolumn, |
| "separator must be one character long"); | "separator must be one character long"); |
| column=sseparator.first_char(); | column=scolumn->first_char(); |
| } | } |
| if(Value* vencloser=options.get(COLUMN_ENCLOSER_NAME)) { | if(Value* vencloser=options.get(COLUMN_ENCLOSER_NAME)) { |
| options.remove(COLUMN_ENCLOSER_NAME); | options.remove(COLUMN_ENCLOSER_NAME); |
| Line 335 static void _load(Request& r, MethodPara | Line 337 static void _load(Request& r, MethodPara |
| Table::element_type row(new ArrayString); | Table::element_type row(new ArrayString); |
| skip_empty_and_comment_lines(&data); | skip_empty_and_comment_lines(&data); |
| while( lsplit_result sr=lsplit(&data, separators.column, '\n', separators.encloser) ) { | while( lsplit_result sr=lsplit(&data, separators.column, '\n', separators.encloser) ) { |
| if(!sr.delim && !row->count()) // append last empty column [if without \n] | if(!*sr.piece && !sr.delim && !row->count()) // append last empty column [if without \n] |
| break; | break; |
| *row+=new String(sr.piece, 0, true); | *row+=new String(sr.piece, 0, true); |
| if(sr.delim=='\n') { | if(sr.delim=='\n') { |
| Line 368 static void maybe_enclose( String& to, c | Line 370 static void maybe_enclose( String& to, c |
| to<<*sencloser; | to<<*sencloser; |
| } else | } else |
| to.append(from, String::L_TABLE); | to<<from; |
| } | } |
| Line 416 static void _save(Request& r, MethodPara | Line 418 static void _save(Request& r, MethodPara |
| for(Array_iterator<const String*> i(*table.columns()); i.has_next(); ) { | for(Array_iterator<const String*> i(*table.columns()); i.has_next(); ) { |
| maybe_enclose( sdata, *i.next(), separators.encloser, separators.sencloser ); | maybe_enclose( sdata, *i.next(), separators.encloser, separators.sencloser ); |
| if(i.has_next()) | if(i.has_next()) |
| sdata.append_know_length("\t", 1, String::L_CLEAN); | sdata<<*separators.scolumn; |
| } | } |
| } else { // nameless table [we were asked to output column names] | } else { // nameless table [we were asked to output column names] |
| if(int lsize=table.count()?table[0]->count():0) | if(int lsize=table.count()?table[0]->count():0) |
| Line 424 static void _save(Request& r, MethodPara | Line 426 static void _save(Request& r, MethodPara |
| char *cindex_tab=new(PointerFreeGC) char[MAX_NUMBER]; | char *cindex_tab=new(PointerFreeGC) char[MAX_NUMBER]; |
| sdata.append_know_length(cindex_tab, | sdata.append_know_length(cindex_tab, |
| snprintf(cindex_tab, MAX_NUMBER, | snprintf(cindex_tab, MAX_NUMBER, |
| column<lsize-1?"%d\t":"%d", column), | column<lsize-1?"%d%c":"%d", column, separators.column), |
| String::L_CLEAN); | String::L_CLEAN); |
| } | } |
| else | else |
| Line 439 static void _save(Request& r, MethodPara | Line 441 static void _save(Request& r, MethodPara |
| for(Array_iterator<const String*> c(*i.next()); c.has_next(); ) { | for(Array_iterator<const String*> c(*i.next()); c.has_next(); ) { |
| maybe_enclose( sdata, *c.next(), separators.encloser, separators.sencloser ); | maybe_enclose( sdata, *c.next(), separators.encloser, separators.sencloser ); |
| if(c.has_next()) | if(c.has_next()) |
| sdata.append_know_length("\t", 1, String::L_CLEAN); | sdata<<*separators.scolumn; |
| } | } |
| sdata.append_know_length("\n", 1, String::L_CLEAN); | sdata.append_know_length("\n", 1, String::L_CLEAN); |
| } | } |
| Line 629 static void _hash(Request& r, MethodPara | Line 631 static void _hash(Request& r, MethodPara |
| } else | } else |
| throw Exception("parser.runtime", | throw Exception("parser.runtime", |
| 0, | 0, |
| "value field(s) must be string or self_table" | "value field(s) must be string or table" |
| ); | ); |
| } else { // by all columns, including key | } else { // by all columns, including key |
| if(!(distinct!=D_ILLEGAL && distinct!=D_FIRST)) | if(!(distinct!=D_ILLEGAL && distinct!=D_FIRST)) |
| Line 644 static void _hash(Request& r, MethodPara | Line 646 static void _hash(Request& r, MethodPara |
| &r, | &r, |
| &self_table, | &self_table, |
| /*key_code=*/key_param->get_junction()?key_param:0, | /*key_code=*/key_param->get_junction()?key_param:0, |
| /*key_field=*/0, | /*key_field=*/0/*filled below*/, |
| &value_fields, | &value_fields, |
| &result.hash(), | &result.hash(), |
| distinct, | distinct, |
| Line 656 static void _hash(Request& r, MethodPara | Line 658 static void _hash(Request& r, MethodPara |
| int saved_current=self_table.current(); | int saved_current=self_table.current(); |
| self_table.for_each(table_row_to_hash, &info); | self_table.for_each(table_row_to_hash, &info); |
| self_table.set_current(saved_current); | self_table.set_current(saved_current); |
| result.extract_default(); | |
| } | } |
| } | } |
| r.write_no_lang(result); | r.write_no_lang(result); |
| Line 886 public: | Line 890 public: |
| } | } |
| }; | }; |
| #endif | #endif |
| static void marshal_bind( | |
| HashStringValue::key_type aname, | |
| HashStringValue::value_type avalue, | |
| SQL_Driver::Placeholder** pptr) | |
| { | |
| SQL_Driver::Placeholder& ph=**pptr; | |
| ph.name=aname.cstr(); | |
| ph.value=avalue->as_string().cstr(String::L_UNSPECIFIED); | |
| ph.is_null=avalue->get_class()==void_class; | |
| ph.were_updated=false; | |
| (*pptr)++; | |
| } | |
| // not static, used elsewhere | |
| int marshal_binds(HashStringValue& hash, SQL_Driver::Placeholder*& placeholders) { | |
| int hash_count=hash.count(); | |
| placeholders=new(UseGC) SQL_Driver::Placeholder[hash_count]; | |
| SQL_Driver::Placeholder* ptr=placeholders; | |
| hash.for_each(marshal_bind, &ptr); | |
| return hash_count; | |
| } | |
| // not static, used elsewhere | |
| void unmarshal_bind_updates(HashStringValue& hash, int placeholder_count, SQL_Driver::Placeholder* placeholders) { | |
| SQL_Driver::Placeholder* ph=placeholders; | |
| for(int i=0; i<placeholder_count; i++, ph++) | |
| if(ph->were_updated) { | |
| Value* value; | |
| if(ph->is_null) | |
| value=new VVoid(); | |
| else | |
| if(ph->value) | |
| value=new VString(*new String(ph->value, 0, true/*tainted*/)); | |
| else | |
| value=new VString(*new String()); | |
| hash.put(ph->name, value); | |
| } | |
| } | |
| static void _sql(Request& r, MethodParams& params) { | static void _sql(Request& r, MethodParams& params) { |
| Value& statement=params.as_junction(0, "statement must be code"); | Value& statement=params.as_junction(0, "statement must be code"); |
| HashStringValue* bind=0; | |
| ulong limit=0; | ulong limit=0; |
| ulong offset=0; | ulong offset=0; |
| if(params.count()>1) { | if(params.count()>1) { |
| Line 896 static void _sql(Request& r, MethodParam | Line 942 static void _sql(Request& r, MethodParam |
| if(!voptions.is_string()) | if(!voptions.is_string()) |
| if(HashStringValue* options=voptions.get_hash()) { | if(HashStringValue* options=voptions.get_hash()) { |
| int valid_options=0; | int valid_options=0; |
| if(Value* vbind=options->get(sql_bind_name)) { | |
| valid_options++; | |
| bind=vbind->get_hash(); | |
| } | |
| if(Value* vlimit=options->get(sql_limit_name)) { | if(Value* vlimit=options->get(sql_limit_name)) { |
| valid_options++; | valid_options++; |
| limit=(ulong)r.process_to_value(*vlimit).as_double(); | limit=(ulong)r.process_to_value(*vlimit).as_double(); |
| Line 914 static void _sql(Request& r, MethodParam | Line 964 static void _sql(Request& r, MethodParam |
| "options must be hash"); | "options must be hash"); |
| } | } |
| SQL_Driver::Placeholder* placeholders=0; | |
| uint placeholders_count=0; | |
| if(bind) | |
| placeholders_count=marshal_binds(*bind, placeholders); | |
| Temp_lang temp_lang(r, String::L_SQL); | Temp_lang temp_lang(r, String::L_SQL); |
| const String& statement_string=r.process_to_string(statement); | const String& statement_string=r.process_to_string(statement); |
| const char* statement_cstr= | const char* statement_cstr= |
| Line 925 static void _sql(Request& r, MethodParam | Line 980 static void _sql(Request& r, MethodParam |
| gettimeofday(&mt[0],NULL); | gettimeofday(&mt[0],NULL); |
| #endif | #endif |
| r.connection()->query( | r.connection()->query( |
| statement_cstr, offset, limit, | statement_cstr, |
| placeholders_count, placeholders, | |
| offset, limit, | |
| handlers, | handlers, |
| statement_string); | statement_string); |
| Line 940 static void _sql(Request& r, MethodParam | Line 997 static void _sql(Request& r, MethodParam |
| r.sql_request_time+=t[1]-t[0]; | r.sql_request_time+=t[1]-t[0]; |
| #endif | #endif |
| if(bind) | |
| unmarshal_bind_updates(*bind, placeholders_count, placeholders); | |
| Table& result= | Table& result= |
| handlers.table?*handlers.table: // query resulted in table? return it | handlers.table?*handlers.table: // query resulted in table? return it |
| *new Table(Table::columns_type(0)); // query returned no table, fake it | *new Table(Table::columns_type(0)); // query returned no table, fake it |