Subject Re: [Firebird-Architect] Re: Replace the statement REPLACE
Author Jim Starkey
Lester Caine wrote:
> Adam wrote:
>
>> Running an update which affects 0 rows would not fire any update trigger.
>>
>> So if it tried insert then update:
>>
>> Before insert trigger -> attempt insert -> fails -> before update
>> trigger -> update -> after update trigger.
>>
>> Or if it tried update and insert:
>>
>> The update wouldn't affect any row, so no update triggers are fired,
>> therefore the insert is attempted.
>>
>> So depending on the methodology chosen, the before insert triggers may
>> be fired.
>>
>
> Good pickup Adam - I knew there was a reason I try the update first ;)
>
>
Rather than trying either statement, the internal Falcon engine
(inaccessible from MySQL) first fetches the record. If it finds a
record, it updates it, otherwise it does an insert. Code follows (note:
the insert inside of the catch statement is purely for debugging):

void NReplace::evalStatement(Statement *statement)
{
if (select)
{
select->evalStatement (statement);
ResultSet *resultSet = statement->getResultSet();
while (resultSet->next())
doReplace (statement, resultSet);
}
else
doReplace (statement, NULL);
}

void NReplace::doReplace(Statement *statement, ResultSet *resultSet)
{
statement->updateStatements = true;
Value **values = new Value* [numberFields];

try
{
Transaction *transaction = statement->transaction;
int n;

// Compute values for fields

for (n = 0; n < numberFields; ++n)
if (resultSet)
values [n] = resultSet->getValue (n + 1);
else
values [n] = children [n]->eval (statement);

// See if we fetch a record. If not, do an insert

stream->open (statement);

if (!stream->fetch (statement))
{
stream->close (statement);

try
{
table->insert (statement->transaction, numberFields,
fields, values);
}
catch (...)
{
stream->open(statement);
stream->fetch(statement);
stream->close(statement);
table->insert (statement->transaction, numberFields,
fields, values);
}

++statement->stats.inserts;
++statement->stats.replaces;
++statement->recordsUpdated;
delete [] values;

return;
}

Context *context = statement->getContext (contextId);
Record *record = context->record;
bool diff = false;

// Check for any differences. If not, it's a no-op

for (n = 0; n < numberFields; ++n)
if (!diff)
{
Value value;
record->getValue (fields [n]->id, &value);
if (value.compare (values [n]) != 0)
{
diff = true;
break;
}
}

// If anything changed, update the record

if (diff)
{
table->update (transaction, context->record, numberFields,
fields, values);
++statement->stats.updates;
++statement->stats.replaces;
++statement->recordsUpdated;
}

stream->close (statement);
delete [] values;

return;
}
catch (...)
{
delete [] values;
throw;
}
}