SAVEPOINT文を使用すると、トランザクション処理中にカレント・ポイントにマークを付けて名前を指定できます。マークを設定したそれぞれの点をセーブポイントと呼びます。たとえば、次の文によりstart_deleteというセーブポイントを設定します。
EXEC SQL SAVEPOINT start_delete;
セーブポイントによってロング・トランザクションを分割できるため、より複雑なプロシージャを制御できるようになります。たとえば、単一のトランザクションが複数のファンクションを実行しているときに、それぞれのファンクションの前にセーブポイントを設定できます。これにより、ある関数が失敗した場合も、Oracleデータを前の状態に簡単にリストアし、リカバリしてから、その関数を再実行できます。
トランザクションの一部を取り消すには、ROLLBACK文とそのTO SAVEPOINT句を使用してセーブポイントを指定します。次の例は、MAIL_LIST表にアクセスして、新しいリストの挿入、古いリストの更新、(少数の)使用されていないリストの削除を行います。削除後に、SQLCA内のsqlerrdの3番目の要素で、削除された行数を調べます。削除された行数が予想以上に多い場合は、セーブポイントstart_deleteまでロールバックして、その削除のみを取り消します。
...
for (;;)
{
printf("Customer number? ");
gets(temp);
cust_number = atoi(temp);
printf("Customer name? ");
gets(cust_name);
EXEC SQL INSERT INTO mail_list (custno, cname, stat)
VALUES (:cust_number, :cust_name, 'ACTIVE');
...
}
for (;;)
{
printf("Customer number? ");
gets(temp);
cust_number = atoi(temp);
printf("New status? ");
gets(new_status);
EXEC SQL UPDATE mail_list
SET stat = :new_status
WHERE custno = :cust_number;
}
/* mark savepoint */
EXEC SQL SAVEPOINT start_delete;
EXEC SQL DELETE FROM mail_list
WHERE stat = 'INACTIVE';
if (sqlca.sqlerrd[2] < 25) /* check number of rows deleted */
printf("Number of rows deleted is %d\n", sqlca.sqlerrd[2]);
else
{
printf("Undoing deletion of %d rows\n", sqlca.sqlerrd[2]);
EXEC SQL WHENEVER SQLERROR GOTO sql_error;
EXEC SQL ROLLBACK TO SAVEPOINT start_delete;
}
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL COMMIT WORK RELEASE;
exit(0);
sql_error:
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK RELEASE;
printf("Processing error\n");
exit(1);
あるセーブポイントまでロールバックすると、そのセーブポイント以降のすべてのセーブポイントが消去されます。ただし、ロールバックしたセーブポイントはそのまま残ります。たとえば、セーブポイントを5つマークし、3番目のセーブポイントまでロールバックすると、4番目と5番目のセーブポイントのみが消去されます。
2つのセーブポイントに同じ名前を付けると、最初のセーブポイントが消去されます。COMMIT文またはROLLBACK文では、すべてのセーブポイントが消去されます。