Diff for /parser3/src/classes/table.C between versions 1.192 and 1.208.6.5

version 1.192, 2003/11/20 16:34:23 version 1.208.6.5, 2005/11/16 14:18:05
Line 1 Line 1
 /** @file  /** @file
         Parser: @b table parser class.          Parser: @b table parser class.
   
         Copyright (c) 2001-2003 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 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_LIMIT_NAME "limit"  #define SQL_BIND_NAME "bind"
 #define SQL_OFFSET_NAME "offset"  
 #define SQL_DEFAULT_NAME "default"  #define SQL_DEFAULT_NAME "default"
 #define SQL_DISTINCT_NAME "distinct"  #define SQL_DISTINCT_NAME "distinct"
 #define TABLE_REVERSE_NAME "reverse"  #define TABLE_REVERSE_NAME "reverse"
   
 // globals  // globals
   
 String sql_limit_name(SQL_LIMIT_NAME);  String sql_bind_name(SQL_BIND_NAME);
 String sql_offset_name(SQL_OFFSET_NAME);  String sql_limit_name(PA_SQL_LIMIT_NAME);
   String sql_offset_name(PA_SQL_OFFSET_NAME);
 String sql_default_name(SQL_DEFAULT_NAME);  String sql_default_name(SQL_DEFAULT_NAME);
 String sql_distinct_name(SQL_DISTINCT_NAME);  String sql_distinct_name(SQL_DISTINCT_NAME);
 String table_reverse_name(TABLE_REVERSE_NAME);  String table_reverse_name(TABLE_REVERSE_NAME);
   
 // local defines  
   
 #define COLUMN_SEPARATOR_NAME "separator"  
 #define COLUMN_ENCLOSER_NAME "encloser"  
   
   
 // methods  // methods
   
 static Table::Action_options get_action_options(Request& r, MethodParams& params,   static Table::Action_options get_action_options(Request& r, MethodParams& params, 
Line 71  static Table::Action_options get_action_ Line 65  static Table::Action_options get_action_
         ]          ]
         but there is ^table.locate[name;value]          but there is ^table.locate[name;value]
   
           ...if(voptions.is_defined() && !voptions.is_string()))
         if(maybe_options.is_string()) { // allow empty options          if(maybe_options.is_string()) { // allow empty options
                 result.defined=true;                  result.defined=true;
                 return result;                  return result;
Line 144  static void _create(Request& r, MethodPa Line 139  static void _create(Request& r, MethodPa
         size_t raw_pos_after=0;          size_t raw_pos_after=0;
         Table::columns_type columns;          Table::columns_type columns;
         if(params.count()==2) {          if(params.count()==2) {
                   String snameless=params.as_string(0, "called with two params, first param may only be string 'nameless'");
                   if(snameless!="nameless")
                           throw Exception("parser.runtime",
                                   &snameless,
                                   "table::create called with two params, first param may only be 'nameless'");
                 columns=Table::columns_type(0); // nameless                  columns=Table::columns_type(0); // nameless
         } else {          } else {
                 columns=Table::columns_type(new ArrayString);                  columns=Table::columns_type(new ArrayString);
Line 178  static void _create(Request& r, MethodPa Line 178  static void _create(Request& r, MethodPa
         GET_SELF(r, VTable).set_table(table);          GET_SELF(r, VTable).set_table(table);
 }  }
   
 static char* remove_encloser(char* cstr, char encloser) {  
         if(cstr[0]!=encloser)  
                 return cstr;  
   
         size_t length=strlen(cstr);  
         if(length<2 || cstr[length-1]!=encloser)  
                 return cstr;  
   
         // 'string'  
   
         cstr[length-1]=0;  
         cstr++;  
   
         // double-encloser stands for encloser  
         char *read;  
         char *write;  
         write=read=cstr;  
         while(char c=*read++) {  
                 if(c==encloser && *read==encloser)  
                         read++;  
   
                 *write++=c;  
         }  
         *write=0; // terminate  
   
         return cstr;  
 }  
   
 struct lsplit_result {  struct lsplit_result {
         char* piece;          char* piece;
         char delim;          char delim;
Line 249  static lsplit_result lsplit(char** strin Line 221  static lsplit_result lsplit(char** strin
                         char *write;                          char *write;
                         write=read=string;                          write=read=string;
                         char c;                          char c;
                         while(c=*read++) {                          while((c=*read++)) {
                                 if(c==encloser) {                                  if(c==encloser) {
                                         char n=*read;                                          char n=*read;
                                         if(n==encloser) // double-encloser stands for encloser                                          if(n==encloser) // double-encloser stands for encloser
Line 279  static void skip_empty_and_comment_lines Line 251  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 288  static void skip_empty_and_comment_lines Line 261  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;          {}
         }          int load( HashStringValue& options ) {
         void load( HashStringValue& options ) {                  int result=0;
                 if(Value* vseparator=options.get(COLUMN_SEPARATOR_NAME)) {                  if(Value* vseparator=options.get(PA_COLUMN_SEPARATOR_NAME)) {
                         options.remove(COLUMN_SEPARATOR_NAME);                          scolumn=&vseparator->as_string();
                         const String& sseparator=vseparator->as_string();                          if(scolumn->length()!=1)
                         if(sseparator.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();
                           result++;
                 }                  }
                 if(Value* vencloser=options.get(COLUMN_ENCLOSER_NAME)) {                  if(Value* vencloser=options.get(PA_COLUMN_ENCLOSER_NAME)) {
                         options.remove(COLUMN_ENCLOSER_NAME);  
                         sencloser=&vencloser->as_string();                          sencloser=&vencloser->as_string();
                         if(sencloser->length()!=1)                          if(sencloser->length()!=1)
                                 throw Exception("parser.runtime",                                  throw Exception("parser.runtime",
                                         sencloser,                                          sencloser,
                                         "encloser must be one character long");                                          "encloser must be one character long");
                         encloser=sencloser->first_char();                          encloser=sencloser->first_char();
                           result++;
                 }                  }
                   return result;
         }          }
 };  };
   
Line 363  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 387  static void maybe_enclose( String& to, c Line 361  static void maybe_enclose( String& to, c
                 size_t pos_after=0;                  size_t pos_after=0;
                 for( size_t pos_before; (pos_before=from.pos( encloser, pos_after ))!=STRING_NOT_FOUND; pos_after=pos_before+1) {                  for( size_t pos_before; (pos_before=from.pos( encloser, pos_after ))!=STRING_NOT_FOUND; pos_after=pos_before+1) {
             to<<from.mid(pos_before+1/*+found encloser*/, pos_after-pos_before);              to<<from.mid(pos_before+1/*+found encloser*/, pos_after-pos_before);
                         to<<*sencloser; // doubling encloser                          to<<*sencloser;  to<<*sencloser; // doubling encloser
                 }                  }
                 // last piece                  // last piece
                 size_t from_length=from.length();                  size_t from_length=from.length();
Line 396  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 420  static void _save(Request& r, MethodPara Line 394  static void _save(Request& r, MethodPara
         TableSeparators separators;          TableSeparators separators;
         if(param_index<params.count()) {          if(param_index<params.count()) {
                 if(HashStringValue *options=params.as_no_junction(param_index++, "additional params must be hash").get_hash()) {                  if(HashStringValue *options=params.as_no_junction(param_index++, "additional params must be hash").get_hash()) {
                         separators.load(*options);                          int valid_options=separators.load(*options);
                         if(options->count())                          if(valid_options!=options->count())
                                 throw Exception("parser.runtime",                                  throw Exception("parser.runtime",
                                         0,                                          0,
                                         "invalid option passed");                                          "invalid option passed");
Line 444  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 452  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 467  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 503  static void _offset(Request& r, MethodPa Line 477  static void _offset(Request& r, MethodPa
                                         "is invalid whence, valid are 'cur' or 'set'");                                          "is invalid whence, valid are 'cur' or 'set'");
                 }                  }
                                   
                 Value& offset_expr=params.as_junction(params.count()-1, "offset must be expression");                  int offset=params.as_int(params.count()-1, "offset must be expression", r);
                 table.offset(absolute, r.process_to_value(offset_expr).as_int());                  table.offset(absolute, offset);
         } else          } else
                 r.write_no_lang(*new VInt(table.current()));                  r.write_no_lang(*new VInt(table.current()));
 }  }
Line 657  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 667  static void _hash(Request& r, MethodPara Line 641  static void _hash(Request& r, MethodPara
   
   
                         {                          {
                                 Row_info info={0};  
                                 info.r=&r;  
                                 info.table=&self_table;  
                                 Value* key_param=&params[0];                                  Value* key_param=&params[0];
                                 info.key_code=key_param->get_junction()?key_param:0;                                  Row_info info={
                                 info.key_field=info.key_code?-1                                          &r,
                                         :self_table.column_name2index(key_param->as_string(), true);                                          &self_table,
                                 info.value_fields=&value_fields;                                          /*key_code=*/key_param->get_junction()?key_param:0,
                                 info.hash=&result.hash();                                          /*key_field=*/0/*filled below*/,
                                 info.distinct=distinct;                                          &value_fields,
                                 info.row=0;                                          &result.hash(),
                                           distinct,
                                           /*row=*/0
                                   };
                                   info.key_field=(info.key_code?-1
                                                   :self_table.column_name2index(key_param->as_string(), true));
   
                                 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 854  static void _join(Request& r, MethodPara Line 832  static void _join(Request& r, MethodPara
                         0,                           0, 
                         "source and destination are same table");                          "source and destination are same table");
   
         if(Table::columns_type dest_columns=dest.columns()) // dest is named          if(dest.columns()) // dest is named
                 src.table_for_each(join_named_row, &dest, o);                  src.table_for_each(join_named_row, &dest, o);
         else // dest is nameless          else // dest is nameless
                 src.table_for_each(join_nameless_row, &dest, o);                  src.table_for_each(join_nameless_row, &dest, o);
Line 912  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) {
                 Value& voptions=params.as_no_junction(1, "options must be hash, not code");                  Value& voptions=params.as_no_junction(1, "options must be hash, not code");
                 if(!voptions.is_string())                  if(voptions.is_defined() && !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 940  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 951  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 966  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
Line 1004  static void _select(Request& r, MethodPa Line 1038  static void _select(Request& r, MethodPa
                 source_table.set_current(row);                  source_table.set_current(row);
   
                 bool condition=r.process_to_value(vcondition,                   bool condition=r.process_to_value(vcondition, 
                                 /*0/*no name* /,*/  
                                 false/*don't intercept string*/).as_bool();                                  false/*don't intercept string*/).as_bool();
   
                 if(condition) // ...condition is true=                  if(condition) // ...condition is true=

Removed from v.1.192  
changed lines
  Added in v.1.208.6.5


E-mail: