shared_ptr<EntitiesEntities> EntitiesEntity::getChilds(const shared_ptr<IPortalDatabase> &database, const ObjectsTypes &types, const RangeUint32 &range, CommonOrder order) const { shared_ptr<DbSqlSelect> select; getChildsStatement(database, types, range, false, false, false, select); switch(order) { case coSubmitDateAsc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foAsc); break; case coSubmitDateDesc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foDesc); break; case coPositionAsc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::POSITION, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foAsc); break; case coPositionDesc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::POSITION, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foDesc); break; default: OS_EXCEPTION("Invalid order"); break; } return getChilds(database, types, range, select); }
shared_ptr<EntitiesEntities> EntitiesEntity::getChilds(const shared_ptr<IPortalDatabase> &database, const ExtensionsComponentID &component, const RangeUint32 &range, CommonOrder order) const { OS_TIMER_PERFORMANCE(TP, _S("Entity::getChilds")); shared_ptr<DbSqlSelect> select; getChildsStatement(database, portalObjectTypeSection, range, false, false, false, select); // Aggiunge alla select la tabella delle sezioni select->tables.add(DbSqlTable(DBTABLES::SECTIONS_TABLE)); // Crea la join tra la tabella di cache e quella delle sezioni select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::CURRENT, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField(DBTABLES::SECTIONS::ID, DBTABLES::SECTIONS_TABLE)); // Seleziona il componente specificato select->where.add(DbSqlField(DBTABLES::SECTIONS::COMPONENT, DBTABLES::SECTIONS_TABLE), Convert::toSQL(component.toUTF16())); switch(order) { case coSubmitDateAsc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foAsc); break; case coSubmitDateDesc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foDesc); break; case coPositionAsc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::POSITION, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foAsc); break; case coPositionDesc: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::POSITION, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField::foDesc); break; default: OS_EXCEPTION("Invalid order"); break; } return getChilds(database, portalObjectTypeSection, range, select); }
void EntitiesEntity::getChildsStatement(const shared_ptr<IPortalDatabase> &database, const ObjectsTypes &types, const RangeUint32 &range, bool count, bool includeSystem, bool includeInvisible, shared_ptr<DbSqlSelect> &statement) const { OS_LOCK(m_cs); // Nota: qui bisogna specificare sempre la tabella perch dall'esterno la select pu essere messa in join con altre tabelle shared_ptr<DbSqlSelect> select(OS_NEW DbSqlSelect(DBTABLES::SNAPSHOT_OBJECTS_TABLE)); if(count) select->count = true; else select->fields.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE)); database->getPortal()->getSnapshotManager()->ensureChilds(database, getEntityID()); select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::PARENT, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(getEntityID())); // Genera il filtro sui tipi di figli StringList childsTypes; for(ObjectsTypes::const_iterator i = types.begin(); i != types.end(); ++i) { if((*i) != portalObjectTypeUnknown) childsTypes.push_back(Convert::toSQL(static_cast<uint32>(*i))); } // Se stato specificato un filtro sul tipo di figli lo applica if(childsTypes.empty() == false) { select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::TYPE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), childsTypes); } // Includi le sezioni di sistema (virtuali)? // Sono figlie della root, per cui la condizione ha senso solo in quel caso if(getEntityID() == ObjectsSystem::instance()->getRootID()) { if(includeSystem == true) { // Salta la root stessa select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(ObjectsSystem::instance()->getRootID()), DbSqlCondition::cfDifferent | DbSqlCondition::cfAnd); } else { // VERYURGENT: Le classi statement non hanno una "not in"... poco male, ma userebbe meglio l'indice. select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::INSERT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(DateTime::EMPTY), DbSqlCondition::cfDifferent | DbSqlCondition::cfAnd); } } if(includeInvisible == false) select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::VISIBLE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(true)); uint32 size = range.size(); if(size != 0) select->limit.setCount(size); uint32 start = range.start(); if(start != 0) select->limit.setOffset(start); statement = select; }
bool IExtensionsExtension::isExtensionRegistered(shared_ptr<Portal> portal, shared_ptr<IPortalDatabase> db, Version &version) const { OS_ASSERT(portal != nullptr); OS_EXCEPT_IF(db == nullptr, "Invalid database"); shared_ptr<DbSqlSelect> select(OS_NEW DbSqlSelect(DBTABLES::EXTENSIONS_TABLE)); select->fields.add(DbSqlField(DBTABLES::EXTENSIONS::VERSION)); select->where.add(DBTABLES::EXTENSIONS::ID, Convert::toSQL(getID().toUTF16())); select->limit.setCount(1); DataTable result; if(db->execute(select, result) == false) return false; if(result.rows() == 0) return false; version.fromString(static_cast<String>(*result[0][0]).to_ascii()); return true; }
String CommentableObjectViewer::localizePost(const EntityID &id) { String href; shared_ptr<EntitiesEntity> entity = getPortal()->getEntity(getDatabase(), id); if(entity != nullptr) { shared_ptr<ObjectsPost> post = objects_post_cast(entity->getCurrent()); if(post != nullptr) { shared_ptr<EntitiesEntity> parent_entity = getPortal()->getEntity(getDatabase(), post->getParent()); if(parent_entity != nullptr) { shared_ptr<DbSqlSelect> select; // Ottiene la select per l'estrazione dei posts parent_entity->getChildsStatement(getDatabase(), portalObjectTypePost, RangeUint32::EMPTY, true, false, false, select); // Aggiunge come limite la data del post specificato per localizzarne l'indice select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(entity->getEntitySubmitDate()), DbSqlCondition::cfMinor | DbSqlCondition::cfEqual | DbSqlCondition::cfAnd); ordered_map<std::wstring, std::wstring> params; // Calcola il numero di posts fino a quello specificato uint32 count = parent_entity->getChildsCount(getDatabase(), portalObjectTypePost, select); if(count > 0) { // Ottiene il numero uint32 pager_items = getSkin()->getPagerItems(); // Calcola l'offset per la visualizzazione del post uint32 offset = static_cast<uint32>(count/pager_items)*pager_items; if(offset > 0) params.set(OS_URL_PARAM_OFFSET, conversions::to_wstring(offset)); } // Genera un link al padre con ancora l'id del post href = HttpParser::createAnchor(parent_entity->getViewLink(getPortal(), params), entity->getEntityID().getString()); } } } return href; }
void SearchResultsDatabase::executeQuery(shared_ptr<IPortalDatabase> database, shared_ptr<Portal> portal, shared_ptr<SearchQuery> query, const StringMap& objectsMap) { clear(); shared_ptr<DbSqlSelect> select(new DbSqlSelect(DBTABLES::SNAPSHOT_OBJECTS_TABLE)); select->fields.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE)); // Se il primo step di un groupMode, ho bisogno di estrarre anche il padre. if(query->getGroupMode() && (objectsMap.empty())) select->fields.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::PARENT, DBTABLES::SNAPSHOT_OBJECTS_TABLE)); // Solo se non specificata gi una lista di oggetti. // Le where si riferiscono agli oggetti singoli (ex. post), non al loro oggetto da visualizzare. // Quindi non necessario specificarle nel secondo giro. if(objectsMap.empty()) { // current != "" select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::CURRENT, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(String::EMPTY), DbSqlCondition::cfDifferent | DbSqlCondition::cfAnd); // Controlla se stata specificata una data di creazione minima const DateTime &fromSubmitDate = query->convertSearchDateTime(query->getFromSubmitDateType(),query->getFromSubmitDateSpecific()); if(fromSubmitDate.isValid()) { // submit_date >= fromSubmitDate select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(fromSubmitDate), DbSqlCondition::cfMajor | DbSqlCondition::cfEqual | DbSqlCondition::cfAnd); } // Controlla se stata specificata una data di creazione massima const DateTime &toSubmitDate = query->convertSearchDateTime(query->getToSubmitDateType(),query->getToSubmitDateSpecific()); if(toSubmitDate.isValid()) { // submit_date <= toSubmitDate select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(toSubmitDate), DbSqlCondition::cfMinor | DbSqlCondition::cfEqual | DbSqlCondition::cfAnd); } // Controlla se stata specificata una data di inserimento minima const DateTime &fromInsertDate = query->convertSearchDateTime(query->getFromInsertDateType(),query->getFromInsertDateSpecific()); if(fromInsertDate.isValid()) { // insert_date >= fromInsertDate select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::INSERT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(fromInsertDate), DbSqlCondition::cfMajor | DbSqlCondition::cfEqual | DbSqlCondition::cfAnd); } // Controlla se stata specificata una data di creazione massima const DateTime &toInsertDate = query->convertSearchDateTime(query->getToInsertDateType(),query->getToInsertDateSpecific()); if(toInsertDate.isValid()) { // insert_date <= toInsertDate select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::INSERT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(toInsertDate), DbSqlCondition::cfMinor | DbSqlCondition::cfEqual | DbSqlCondition::cfAnd); } // Non mostrare le entit virtuali. Dato che le entit virtuali hanno insertDate==null, select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::INSERT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(DateTime::EMPTY), DbSqlCondition::cfDifferent | DbSqlCondition::cfAnd); // Se non deve mostrare gli oggetti cancellati forza la visibilit ad 1 if(query->getShowDeleted() == false) // visible == 1 select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::VISIBLE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), Convert::toSQL(true)); StringList types; for(SearchQuery::Options::const_iterator i = query->getOptions().begin(); i != query->getOptions().end(); ++i) { shared_ptr<ISearchOptions> options = i->second; if(options->getActive()) { types.push_back(Convert::toString(options->getType())); } } if(types.empty() == false) // type in (...) select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::TYPE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), types); // Ricerca in determinati padri StringList parentsList; query->computeParentsList(database, portal, parentsList); // Ci metto gli apici... for(StringList::iterator i = parentsList.begin(); i != parentsList.end(); ++i) { *i = _S("'") + String(*i) + _S("'"); } if(!parentsList.empty()) { select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::PARENT, DBTABLES::SNAPSHOT_OBJECTS_TABLE), parentsList); } // 0.14 RC3 if(query->getText().substr(0,17) == _S("title-start-with:")) { select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::TITLE), Convert::toSQL(query->getText().substr(17) + _S("%")), DbSqlCondition::cfLike | DbSqlCondition::cfAnd); } // 0.14 RC3 if(query->getText().substr(0,17) == _S("title-start-with:")) { select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::TITLE), query->getText().substr(17) + _S("%"), DbSqlCondition::cfLike | DbSqlCondition::cfAnd); } // 0.12 if( (query->getByUser().empty() == false) && (query->getByUser() != _S("*")) ) { String user = query->getByUser(); // MySelf test if(user == _S("myself")) { // VERYURGENTRAZOR: Commentata perchè ho piallato il loggedUser come parametro... rivalutare la cosa /* if( (loggedUser != nullptr) && loggedUser->isLogged() && (loggedUser->getGuestMode() == false)) { user = loggedUser->getUser()->id->toUTF16(); } else { user == String::EMPTY; } */ user == String::EMPTY; } else { // VERYURGENT, un test migliore per verificare se è un ID? // VERYURGENT, l'sql potrebbe essere Sqlite-specifica. // Fatto meglio, dovrei avere un'altro combo "localizza per" "nick/id" // Inoltre qui ci sarebbe da supportare una ricerca "non exact-match" per farlo stile Invision. if(user.length() != 48) { algorithms::to_upper(user); String sql = String::format(_S("select rs.id from os_snapshot_users ts, os_users tu where ts.id=tu.id and upper(tu.name) = %S").c_str(), Convert::toSQL(user).c_str()); DataTable result; if(database->execute(sql, result)) { if(result.rows() > 0 && result.columns() > 0) user = static_cast<String>(*result[0][0]); } } } // Come autore della primaria select->tables.add(DbSqlTable(DBTABLES::ENTRIES_TABLE)); // Crea la join select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField(DBTABLES::ENTRIES::ID, DBTABLES::ENTRIES_TABLE)); select->where.add(DbSqlField(DBTABLES::ENTRIES::AUTHOR, DBTABLES::ENTRIES_TABLE), Convert::toSQL(user)); // Come editore dell'oggetto // where DBTABLES::SNAPSHOT_OBJECTS::REFERENCE in (select id from os_entries where revision= getByUser(); } } else { // Se objectsList valorizzata, allora il secondo passo di una ricerca in groupMode, gli passo l'elenco oggetti. StringList objectsList; for(StringMap::const_iterator i = objectsMap.begin(); i != objectsMap.end(); ++i) { objectsList.push_back(_S("'") + i->first + _S("'")); } //select->where.add(String(DBTABLES::SNAPSHOT_OBJECTS_TABLE) + _S(".") + DBTABLES::SNAPSHOT_OBJECTS::REFERENCE, objectsList); select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE), objectsList); } // Se il primo passaggio di una groupMode, l'ordinamento inutile. // 0.12 RC5, tolto, con un limite nel primo giro di group-mode, devono essere ordinati. //if( (!query->getGroupMode()) || (!objectsMap.empty()) ) { DbSqlField::Order order = DbSqlField::foNone; switch(query->getOrderDirection()) { case searchOrderDirectionDescendent: order = DbSqlField::foDesc; break; case searchOrderDirectionAscendent: order = DbSqlField::foAsc; break; default: OS_ASSERTFALSE(); break; } SearchOrderMode orderMode = query->getOrderMode(); // 0.12 RC5, da testare // Se è il primo passaggio di una group-mode, l'ordinamento "omLastObject" deve essere un "omSubmitDate". if( (query->getGroupMode()) && (objectsMap.empty()) ) { if(orderMode == searchOrderModeLastObject) orderMode = searchOrderModeSubmitDate; else if(orderMode == searchOrderModeVotesTotal) orderMode = searchOrderModeSubmitDate; else if(orderMode == searchOrderModeVotesAverage) orderMode = searchOrderModeSubmitDate; } switch(orderMode) { case searchOrderModeRelevance: // Non c' la rilevanza nelle search non basate sul Lucene. break; // TOCLEAN_SNAPSHOT_SCORE /* Per riprestinarlo, devo fare una join con la snapshot::users. case searchOrderModeReputation: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SCORE, DBTABLES::SNAPSHOT_OBJECTS_TABLE)); select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::POSITION, DBTABLES::SNAPSHOT_OBJECTS_TABLE), order); break; */ case searchOrderModeSubmitDate: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::SUBMIT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), order); break; case searchOrderModeInsertDate: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::INSERT_DATE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), order); break; case searchOrderModePosition: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::POSITION, DBTABLES::SNAPSHOT_OBJECTS_TABLE), order); break; case searchOrderModeTitle: select->orderBy.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::TITLE, DBTABLES::SNAPSHOT_OBJECTS_TABLE), order); break; case searchOrderModeLastObject: { select->tables.add(DbSqlTable(DBTABLES::DISCUSSIONS_STATS_TABLE)); // Crea la join select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField(DBTABLES::DISCUSSIONS_STATS::REFERENCE, DBTABLES::DISCUSSIONS_STATS_TABLE)); DbSqlField last_entry_date(DBTABLES::DISCUSSIONS_STATS::LAST_ENTRY_DATE, DBTABLES::DISCUSSIONS_STATS_TABLE); last_entry_date.setOrder(order); select->orderBy.fields.add(last_entry_date); } break; case searchOrderModeVotesTotal: { select->tables.add(DbSqlTable(DBTABLES::DISCUSSIONS_STATS_TABLE)); // Crea la join select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField(DBTABLES::DISCUSSIONS_STATS::REFERENCE, DBTABLES::DISCUSSIONS_STATS_TABLE)); DbSqlField last_entry_date(DBTABLES::DISCUSSIONS_STATS::VOTES, DBTABLES::DISCUSSIONS_STATS_TABLE); last_entry_date.setOrder(order); select->orderBy.fields.add(last_entry_date); } break; case searchOrderModeVotesAverage: { select->tables.add(DbSqlTable(DBTABLES::DISCUSSIONS_STATS_TABLE)); // Crea la join select->where.add(DbSqlField(DBTABLES::SNAPSHOT_OBJECTS::ENTITY, DBTABLES::SNAPSHOT_OBJECTS_TABLE), DbSqlField(DBTABLES::DISCUSSIONS_STATS::REFERENCE, DBTABLES::DISCUSSIONS_STATS_TABLE)); DbSqlField last_entry_date(DBTABLES::DISCUSSIONS_STATS::VOTES_AVERAGE, DBTABLES::DISCUSSIONS_STATS_TABLE); last_entry_date.setOrder(order); select->orderBy.fields.add(last_entry_date); } break; default: OS_EXCEPTION("Unknown query order mode"); break; } } uint32 searchLimit = Options::instance()->getOption<uint32>(Options::portals_options::search_limit); if(searchLimit != 0) { // 0.12 RC5 - Altrimenti, setto cmq un massimo di risultati, come limite massimo ragionevole. select->limit.setCount(searchLimit); } if(query->hasMaxResults()) { // 0.13 RC2 select->limit.setCount(query->getMaxResults()); } // Fa prima una "count" per sapere quanti risultati totali, ad esempio per calcolare le pagine del pager. // Poi, vengono estratti solo i record che servono alla pagina attuale (se non è il primo giro di un group-mode). select->count = true; DataTable result; if(database->execute(select, result)) { OS_ASSERT(result.hasRow(0)); m_totalResults = result.get(0, 0); if(m_totalResults > 0) // In teoria non serve rieseguire la query se non ci sono risultati... { // Se è in groupMode, ed è il primo passaggio, controllo se è parziale. if( (query->getGroupMode()) && (objectsMap.empty()) ) if( (searchLimit != 0) && (m_totalResults >= searchLimit) ) m_partial = true; // Se non è in groupMode, o è il secondo passaggio, allora filtro per il pager. if( (!query->getGroupMode()) || (!objectsMap.empty()) ) { uint32 limit=0; if( (query->hasLimit()) && (query->hasMaxResults()) ) limit = std::min<uint32>(query->getLimit(), query->getMaxResults()); else if(query->hasLimit()) limit = query->getLimit(); else if(query->hasMaxResults()) limit = query->getMaxResults(); if(limit != 0) { select->limit.setCount(limit); select->limit.setOffset(query->getOffset()); } } else { if(searchLimit != 0) { // 0.12 RC5 - Altrimenti, setto cmq un massimo di risultati, come limite massimo ragionevole. select->limit.setCount(searchLimit); } } select->count = false; result.clear(); if(database->execute(select, result)) { for(uint32 r = 0; r < result.rows(); r++) { EntityID reference = static_cast<String>(result.get(r, DBTABLES::SNAPSHOT_OBJECTS::ENTITY)).to_ascii(); if(query->getGroupMode()) { shared_ptr<EntitiesEntity> entity = database->getPortal()->getEntity(database, reference); if( (entity != NULL) && (entity->getCurrent() != NULL) ) { if(ObjectsSystem::instance()->getDescriptor(entity->getObjectType())->isGroupable()) reference = static_cast<String>(result.get(r, DBTABLES::SNAPSHOT_OBJECTS::PARENT)).to_ascii(); } } add(reference); } } } } }