//! recreate html page if something changes void MetadataItemPropertiesPanel::update() { Database* db = dynamic_cast<Database*>(objectM); if (db && !db->isConnected()) { objectM = 0; if (MetadataItemPropertiesFrame* f = getParentFrame()) f->Close(); // MB: This code used to use: //f->removePanel(this); // which would allow us to mix property pages from different // databases in the same Frame, but there are some mysterious // reasons why it causes heap corruption with MSVC return; } // if table or view columns change, we need to reattach if (objectM->getType() == ntTable || objectM->getType() == ntView) // also observe columns { Relation* r = dynamic_cast<Relation*>(objectM); if (!r) return; SubjectLocker locker(r); r->ensureChildrenLoaded(); for (ColumnPtrs::iterator it = r->begin(); it != r->end(); ++it) (*it)->attachObserver(this, false); } // if description of procedure params change, we need to reattach if (objectM->getType() == ntProcedure) { Procedure* p = dynamic_cast<Procedure*>(objectM); if (!p) return; SubjectLocker locker(p); p->ensureChildrenLoaded(); for (ParameterPtrs::iterator it = p->begin(); it != p->end(); ++it) (*it)->attachObserver(this, false); } // with this set to false updates to the same page do not show the // "Please wait while the data is being loaded..." temporary page // this results in less flicker, but may also seem less responsive if (!htmlReloadRequestedM) requestLoadPage(false); }
SqlStatement::SqlStatement(const wxString& sql, Database *db, const wxString& terminator) :actionM(actNONE), objectTypeM(ntUnknown), databaseM(db), objectM(0), identifierTokenIndexM(0), isAlterColumnM(false), isDatatypeM(false), terminatorM(terminator), statementM(sql) { // use the tokenizer to split the statements into a vector of tokens // also keep the token strings for identifiers and strings SqlTokenizer tokenizer(sql); // first get the tokens up to the first identifier SqlTokenType stt; while (true) { stt = tokenizer.getCurrentToken(); if (stt == tkEOF) break; if (stt != tkCOMMENT && stt != tkWHITESPACE) { tokensM.add(stt); wxString ts(tokenizer.getCurrentTokenString()); tokenStringsM[tokensM.size() - 1] = ts; if (stt == tkIDENTIFIER) { nameM.setFromSql(ts); tokenizer.jumpToken(false); // break here since we don't want name to be overwritten if (tokensM[0] != kwGRANT && tokensM[0] != kwREVOKE && tokensM[0] != kwCOMMENT) { break; } } } tokenizer.jumpToken(false); } // needs at least action if (tokensM.size() < 1) return; // true; if (nameM.get().IsEmpty()) // non-reserved keyword used as identifier? { nameM.setFromSql(tokenStringsM[2]); // we take a lucky guess identifierTokenIndexM = 2; // ex.: CREATE DOMAIN CASCADE } else identifierTokenIndexM = tokensM.size() - 1; size_t typeTokenIndex = 1; // get action switch (tokensM[0]) { case kwALTER: actionM = actALTER; break; case kwCOMMENT: actionM = actCOMMENT; break; case kwCREATE: actionM = actCREATE; break; case kwDECLARE: actionM = actDECLARE; break; case kwDROP: actionM = actDROP; break; case kwGRANT: case kwREVOKE: actionM = actGRANT; break; case kwRECREATE: actionM = actRECREATE; break; case kwSET: actionM = actSET; break; case kwUPDATE: // it's the only statement we care for which has implicit type actionM = actUPDATE; objectTypeM = ntTable; break; default: return; // true; } // special handling for "CREATE OR ALTER" if (actionM == actCREATE && tokensM[1] == kwOR && tokensM[2] == kwALTER) { actionM = actCREATE_OR_ALTER; typeTokenIndex = 3; if (identifierTokenIndexM == 2) { identifierTokenIndexM = 4; nameM.setFromSql(tokenStringsM[4]); } } // GRANT blah, blah blah ON [PROCEDURE] object_name TO ... // REVOKE blah, blah, .. ON [PROCEDURE] object_name FROM ... if (actionM == actGRANT) { size_t idx = 1; while (idx < tokensM.size()) { if (tokensM[idx++] == kwON) { if (tokensM[idx] == kwPROCEDURE) { idx++; objectTypeM = ntProcedure; } nameM.setFromSql(tokenStringsM[idx]); if (objectTypeM == ntProcedure) { objectM = databaseM->findByNameAndType(ntProcedure, nameM.get()); if (!objectM) objectTypeM = ntUnknown; } else if (objectTypeM == ntUnknown) // find relation { if (!databaseM) return; objectM = databaseM->findRelation(nameM); if (objectM) objectTypeM = objectM->getType(); } return; } } // GRANT role_name TO ... nameM.setFromSql(tokenStringsM[1]); objectM = databaseM->findByNameAndType(ntRole, nameM.get()); if (objectM) objectTypeM = ntRole; return; } // COMMENT ON COLUMN table.column IS // COMMENT ON PARAMETER procedure.parameter IS if (actionM == actCOMMENT && tokensM[1] == kwON) { Identifier parent(tokenStringsM[3]); Identifier child(tokenStringsM[4]); if (tokensM[2] == kwCOLUMN) { if (Relation* r = databaseM->findRelation(parent)) { r->ensureChildrenLoaded(); if (ColumnPtr c = r->findColumn(child.get())) { objectTypeM = ntColumn; objectM = c.get(); return; } } } if (tokensM[2] == kwPARAMETER) { Procedure* p = dynamic_cast<Procedure *>( databaseM->findByNameAndType(ntProcedure, parent.get())); if (p) { p->ensureChildrenLoaded(); if (ParameterPtr par = p->findParameter(child.get())) { objectTypeM = ntParameter; objectM = par.get(); return; } } } } // get object type while (objectTypeM == ntUnknown && typeTokenIndex < tokensM.size()) { switch (tokensM[typeTokenIndex]) { case kwDATABASE: objectTypeM = ntDatabase; break; case kwDOMAIN: objectTypeM = ntDomain; break; case kwEXCEPTION: objectTypeM = ntException; break; case kwFUNCTION: objectTypeM = ntFunction; break; case kwGENERATOR: objectTypeM = ntGenerator; break; case kwINDEX: objectTypeM = ntIndex; break; case kwPROCEDURE: objectTypeM = ntProcedure; break; case kwROLE: objectTypeM = ntRole; break; case kwTABLE: objectTypeM = ntTable; break; case kwTRIGGER: objectTypeM = ntTrigger; break; case kwVIEW: objectTypeM = ntView; break; default: // this will scan over things like "EXTERNAL", "UNIQUE", // "ASCENDING", "STATISTICS" etc., until object type is found typeTokenIndex++; break; } } if (objectTypeM == ntUnknown || !databaseM) return; // false; objectM = databaseM->findByNameAndType(objectTypeM, nameM.get()); // map "CREATE OR ALTER" and "RECREATE" to correct action if (actionM == actCREATE_OR_ALTER || actionM == actRECREATE) actionM = (objectM ? actALTER : actCREATE); // -------------- STEP 2 ------------------------------------------------ // if we decide to have a two-step evaluation, this is the breaking point // get remaining tokens, and token content for identifiers + strings while (tkEOF != (stt = tokenizer.getCurrentToken())) { if (stt != tkCOMMENT && stt != tkWHITESPACE) { tokensM.add(stt); if (stt == tkIDENTIFIER || stt == tkSTRING) { wxString ts(tokenizer.getCurrentTokenString()); tokenStringsM[tokensM.size() - 1] = ts; } } tokenizer.nextToken(); } // check for "UPDATE RDB$RELATION_FIELDS SET RDB$NULL_FLAG" // convert this change in NULL flag to "ALTER TABLE" and act accordingly if (actionM == actUPDATE && nameM.equals("RDB$RELATION_FIELDS") && tokensM[2] == kwSET && tokensM[3] == tkIDENTIFIER) { Identifier id; id.setFromSql(tokenStringsM[3]); if (!id.equals("RDB$NULL_FLAG")) return; // true; actionM = actALTER; objectTypeM = ntTable; objectM = 0; // find "RDB$RELATION_NAME" in map for (std::map<int, wxString>::const_iterator mit = tokenStringsM.begin(); mit != tokenStringsM.end(); mit++) { if ((*mit).second.CmpNoCase("RDB$RELATION_NAME") == 0) { size_t i = (*mit).first; if (tokensM[i + 1] == tkEQUALS && tokensM[i + 2] == tkSTRING) { nameM.setFromSql(tokenStringsM[i + 2]); objectM = databaseM->findByNameAndType(ntTable, nameM.get()); break; } } } if (!objectM) return; // true; } if (actionM == actALTER) // check for alter column { // handle "ALTER TABLE xyz ALTER [COLUMN] fgh TYPE {domain or datatype} if (objectTypeM == ntTable && tokensM[identifierTokenIndexM + 1] == kwALTER) { size_t fieldNameIndex = identifierTokenIndexM + 2; if (tokensM[fieldNameIndex] == kwCOLUMN) fieldNameIndex++; if (tokensM[fieldNameIndex + 1] == kwTYPE) { isAlterColumnM = true; fieldNameM.setFromSql(tokenStringsM[fieldNameIndex]); stt = tokensM[fieldNameIndex + 2]; isDatatypeM = (stt == kwCHAR || stt == kwVARCHAR || stt == kwINTEGER || stt == kwSMALLINT || stt == kwBIGINT || stt == kwDECIMAL || stt == kwNUMERIC || stt == kwDATE || stt == kwTIME || stt == kwTIMESTAMP || stt == kwFLOAT || stt == kwBLOB) || (stt == kwDOUBLE && tokensM[fieldNameIndex + 3] == kwPRECISION); } } } }