Beispiel #1
0
Relation* SqlStatement::getCreateTriggerRelation() const
{
    if (objectTypeM == ntTrigger && databaseM
        && tokensM[identifierTokenIndexM + 1] == kwFOR
        && tokensM[identifierTokenIndexM + 2] == tkIDENTIFIER)
    {
        Identifier id;
        std::map<int, wxString>::const_iterator ci =
            tokenStringsM.find(identifierTokenIndexM+2);
        id.setFromSql((*ci).second);
        return databaseM->findRelation(id);
    }
    return 0;
}
// UDD = user defined domain
// AGD = auto generated domain (those starting with RDB$)
bool FieldPropertiesDialog::getStatementsToExecute(wxString& statements,
    bool justCheck)
{
    wxString colNameSql(Identifier::userString(textctrl_fieldname->GetValue()));
    Identifier selDomain(choice_domain->GetStringSelection());
    bool newDomain = getIsNewDomainSelected();
    wxString selDatatype = choice_datatype->GetStringSelection();
    wxString dtSize = textctrl_size->GetValue();
    wxString dtScale = textctrl_scale->GetValue();
    bool isNullable = !checkbox_notnull->IsChecked();

    int n = choice_datatype->GetSelection();
    if (n >= 0 && n < datatypescnt)
    {
        if (!datatypes[n].hasSize)
            dtSize.Clear();
        if (!datatypes[n].hasScale)
            dtScale.Clear();
    }

    wxString alterTable = "ALTER TABLE " + tableM->getQuotedName() + " ";
    enum unn { unnNone, unnBefore, unnAfter } update_not_null = unnNone;

    // detect changes to existing field, create appropriate SQL actions
    if (columnM)
    {
        // field name changed ?
        // compare regardless of active quoting rules, so that name
        // will remain unchanged if edit field contents haven't changed
        // OR if the altered name would result in same SQL identifier
        if (textctrl_fieldname->GetValue() == columnM->getName_()
            || colNameSql == columnM->getQuotedName())
        {
            // no changes -> use original name for all other statements
            colNameSql = columnM->getQuotedName();
        }
        else
        {
            statements += alterTable + "ALTER " + columnM->getQuotedName()
                + " TO " + colNameSql + ";\n\n";
        }

        // domain changed ?
        wxString type, size, scale, charset;
        if (!getDomainInfo(columnM->getSource(), type, size, scale, charset))
        {
            ::wxMessageBox(_("Can not get domain info - aborting."),
                _("Error"), wxOK | wxICON_ERROR);
            return false;
        }
        if (columnM->getSource() != selDomain.get() && !newDomain)
        {   // UDD -> other UDD  or  AGD -> UDD
            statements += alterTable + "ALTER " + colNameSql +
                " TYPE " + selDomain.getQuoted() + ";\n\n";
        }
        else if (newDomain
            || type.CmpNoCase(selDatatype) || size != dtSize || scale != dtScale)
        {   // UDD -> AGD  or  AGD -> different AGD
            statements += alterTable + "ALTER " + colNameSql +
                " TYPE ";
            statements += selDatatype;
            if (!dtSize.IsEmpty())
            {
                statements += "(" + dtSize;
                if (!dtScale.IsEmpty())
                    statements += "," + dtScale;
                statements += ")";
            }
            statements += ";\n\n";
        }

        // not null option changed ?
        if (isNullable != columnM->isNullable(CheckDomainNullability))
        {
            if (!isNullable) // change from NULL to NOT NULL
                update_not_null = unnBefore;

            statements += "UPDATE RDB$RELATION_FIELDS SET RDB$NULL_FLAG = ";
            if (isNullable)
                statements += "NULL";
            else
                statements += "1";
            // direct change in RDB$RELATION_FIELDS needs unquoted field name
            Identifier id;
            id.setFromSql(colNameSql);
            wxString fnm = id.get();
            fnm.Replace("'", "''");
            wxString tnm = tableM->getName_();
            statements += "\nWHERE RDB$FIELD_NAME = '" + fnm
                + "' AND RDB$RELATION_NAME = '" + tnm
                + "';\n\n";

            if (isNullable) // change from NOT NULL to NULL
            {
                wxString constraintName;
                if (getNotNullConstraintName(fnm, constraintName))
                {
                    statements += alterTable + "DROP CONSTRAINT "
                        + constraintName + ";\n\n";
                }
            }
        }
    }
    else // create new field
    {
        wxString addCollate;
        statements += alterTable + "ADD \n" + colNameSql + " ";
        if (newDomain)
        {
            statements += selDatatype;
            if (!dtSize.IsEmpty())
            {
                statements += "(" + dtSize;
                if (!dtScale.IsEmpty())
                    statements += "," + dtScale;
                statements += ")";
            }
            if (datatypes[n].isChar)
            {
                wxString charset = choice_charset->GetStringSelection();
                wxString collate = choice_collate->GetStringSelection();
                if (!charset.IsEmpty())
                {
                    statements += " CHARACTER SET " + charset;
                    if (!collate.IsEmpty())
                        addCollate = " COLLATE " + collate;
                }
            }
        }
        else
            statements += selDomain.getQuoted();

        if (!isNullable)
        {
            statements += " NOT NULL";
            update_not_null = unnAfter;
        }
        statements += addCollate + ";\n\n";
    }

    if (update_not_null != unnNone && !justCheck)
    {
        wxString s = ::wxGetTextFromUser(
            _("Enter value for existing fields containing NULL"),
            _("Update Existing NULL Values"), "", this);
        if (update_not_null == unnBefore)
        {
            wxString origColumnName = columnM->getQuotedName();
            statements = "UPDATE " + tableM->getQuotedName()
                + " \nSET " + origColumnName + " = '" + s
                + "' \nWHERE " + origColumnName + " IS NULL;\n"
                + statements;
        }
        else
        {
            statements = statements + "COMMIT;\n"
                + "UPDATE " + tableM->getQuotedName()
                + " \nSET " + colNameSql + " = '" + s
                + "' \nWHERE " + colNameSql + " IS NULL;\n";
        }
    }
    statements += textctrl_sql->GetValue();
    return !statements.IsEmpty();
}
Beispiel #3
0
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);
            }
        }
    }
}