/**************************************************************************** ** (c) Copyright IBM Corp. 2007 All rights reserved. ** ** The following sample of source code ("Sample") is owned by International ** Business Machines Corporation or one of its subsidiaries ("IBM") and is ** copyrighted and licensed, not sold. You may use, copy, modify, and ** distribute the Sample in any form without payment to IBM, for the purpose of ** assisting you in the development of your applications. ** ** The Sample code is provided to you on an "AS IS" basis, without warranty of ** any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR ** IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Some jurisdictions do ** not allow for the exclusion or limitation of implied warranties, so the above ** limitations or exclusions may not apply to you. IBM shall not be liable for ** any damages you suffer as a result of using, copying, modifying or ** distributing the Sample, even if IBM has been advised of the possibility of ** such damages. ***************************************************************************** ** ** SOURCE FILE NAME: tbsavept.sqc ** ** SAMPLE: How to use external savepoints ** Also demonstrates how to change the default value of a column ** ** SQL STATEMENTS USED: ** ALTER TABLE ** CLOSE ** COMMIT ** CREATE TABLE ** CREATE USER TEMPORARY TABLESPACE ** DECLARE GLOBAL TEMPORARLY TABLE ** DECLARE CURSOR ** DROP TABLE ** DROP TABLESPACE ** EXECUTE IMMEDIATE ** FETCH ** INCLUDE ** INSERT ** OPEN ** PREPARE ** RELEASE SAVEPOINT ** ROLLBACK TO SAVEPOINT ** SAVEPOINT ** SELECT ** SET INTEGRITY ** ** STRUCTURES USED: ** sqlca ** ** ***************************************************************************** ** ** For more information on the sample programs, see the README file. ** ** For information on developing C applications, see the Application ** Development Guide. ** ** For information on using SQL statements, see the SQL Reference. ** ** For the latest information on programming, building, and running DB2 ** applications, visit the DB2 Information Center: ** http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp ****************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <sqlenv.h> #include <sqlutil.h> #include <string.h> #include "utilemb.h" #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif EXEC SQL INCLUDE SQLCA; EXEC SQL BEGIN DECLARE SECTION; char firstName[20 + 1]; char lastName[20 + 1]; char major[20 + 1]; char university[20 + 1]; char mystmnt[150 + 1]; char tablename[20 + 1]; char status[5 + 1]; float GPA; short stuID; short empID; EXEC SQL END DECLARE SECTION; /* Function declarations */ int DropTablespaceAndTables(void); int CreateTablespaceAndTablesWithData(void); int TbContentDisplay(char, char, char, char, char, char); int TbIntegrityInfoDisplay(); int SavepointWithAlterTable(void); int SavepointWithGlobalTemporaryTable(void); int NestedSavepoints(void); int SavepointWithSetIntegrity(void); int SavepointWithNotLoggedInitiallyTable(void); int main(int argc, char *argv[]) { int rc = 0; char dbAlias[SQL_ALIAS_SZ + 1]; char user[USERID_SZ + 1]; char pswd[PSWD_SZ + 1]; /* Clear host variables */ memset(firstName, '\0', 21); memset(lastName, '\0', 21); memset(major, '\0', 21); memset(university,'\0', 21); memset(mystmnt, '\0', 151); memset(tablename, '\0', 21); memset(status, '\0', 6); /* Check command line arguments */ rc = CmdLineArgsCheck1(argc, argv, dbAlias, user, pswd); if (rc != 0) { return rc; } printf("\nTHIS SAMPLE SHOWS HOW TO USE EXTERNAL SAVEPOINTS\n"); /* Connect to sample database */ rc = DbConn(dbAlias, user, pswd); if (rc != 0) { return rc; } /* Set up the initial state of the database */ CreateTablespaceAndTablesWithData(); SavepointWithAlterTable(); SavepointWithGlobalTemporaryTable(); NestedSavepoints(); SavepointWithSetIntegrity(); SavepointWithNotLoggedInitiallyTable(); /* Drop the tables and temporary table space */ rc = DropTablespaceAndTables(); if (rc != 0) { return rc; } /* Disconnect from sample database */ rc = DbDisconn(dbAlias); if (rc != 0) { return rc; } return rc; } /* main */ /*** Helper functions ***/ /* The following function drops the temporary tablespace as well as the tables created and used by this sample. */ int DropTablespaceAndTables(void) { printf("\n-----------------------------------------------------------\n"); printf("USE THE SQL STATEMENTS:\n"); printf(" DROP TABLE\n"); printf(" DROP TABLESPACE\n"); printf("TO DROP TABLE AND TABLE SPACE\n"); printf("\n DROP TABLESPACE tempspace\n"); EXEC SQL DROP TABLESPACE tempspace; EMB_SQL_CHECK("Tablespace: drop"); printf("\n DROP TABLE students\n"); EXEC SQL DROP TABLE students; EMB_SQL_CHECK("Table: drop"); printf("\n DROP TABLE studentrecords\n"); EXEC SQL DROP TABLE studentrecords; EMB_SQL_CHECK("Table: drop"); return 0; } /* DropTablespaceAndTables */ /* The following function creates a temporary tablespace as well as two tables, studentrecords and students. The tables are populated with initial values. */ int CreateTablespaceAndTablesWithData(void) { printf("\n-----------------------------------------------------------\n"); printf("USE THE SQL STATEMENTS:\n"); printf(" COMMIT\n"); printf(" CREATE TABLE\n"); printf(" CREATE USER TEMPORARY TABLESPACE\n"); printf(" INSERT\n"); printf("TO CREATE TABLES WITH DATA AND A USER TEMPORARY TABLE SPACE FOR\n"); printf("THE TEMP TABLE\n"); /* Create the tables and temporary table space */ /* Create a temporary table space */ printf("\n CREATE USER TEMPORARY TABLESPACE tempspace\n"); printf(" MANAGED BY SYSTEM USING('temp')\n"); EXEC SQL CREATE USER TEMPORARY TABLESPACE tempspace MANAGED BY SYSTEM USING('temp'); EMB_SQL_CHECK("Temporary Table Space: create"); /* Create studentrecords table */ printf("\n CREATE TABLE studentrecords(\n"); printf(" studentid SMALLINT\n"); printf(" gpa FLOAT(3)\n"); printf(" university CHAR(20))\n\n"); EXEC SQL CREATE TABLE studentrecords( studentid smallint, gpa float(3), university char(20)); EMB_SQL_CHECK("Table: create"); /* Create students table */ printf(" CREATE TABLE students(\n"); printf(" firstname CHAR(20)\n"); printf(" lastname CHAR(20)\n"); printf(" studentid SMALLINT\n"); printf(" employeeid SMALLINT)\n"); printf(" NOT LOGGED INITIALLY\n\n"); EXEC SQL CREATE TABLE students( firstname char(20), lastname char(20), studentid smallint, employeeid smallint) NOT LOGGED INITIALLY; EMB_SQL_CHECK("Table: create"); /* This is for deactivating NOT LOGGED INITIALLY mode for the table STUDENTS. */ printf(" COMMIT\n"); EXEC SQL COMMIT; EMB_SQL_CHECK("Commit"); /* Populate the tables just created */ /* Insert 2 records into students table */ printf("\n INSERT INTO students(firstname, lastname, studentid,"); printf(" emplyeeid)\n"); printf(" VALUES('Jack', 'Hill', 1, 3),\n"); printf(" ('Jill', 'Hill', 5, 7)\n\n"); strcpy(mystmnt, "INSERT INTO students(firstname, lastname, studentid,"); strcat(mystmnt, " employeeid) VALUES('Jack', 'Hill', 1, 3),"); strcat(mystmnt, " ('Jill', 'Hill', 5, 7)"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Insert 2 records into studentrecords table */ printf(" INSERT INTO studentrecords(studentid, gpa, university)\n"); printf(" VALUES(1, 2.5, 'Crown'),\n"); printf(" (5, 3.99, 'Tumbling')\n"); strcpy(mystmnt, "INSERT INTO studentrecords(studentid, gpa, university)"); strcat(mystmnt, " VALUES(1, 2.5, 'Crown'),"); strcat(mystmnt, " (5, 3.99, 'Tumbling')"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Prepare SELECT statements */ strcpy(mystmnt, "SELECT * FROM studentrecords"); EXEC SQL PREPARE selectstmnt1 FROM :mystmnt; EMB_SQL_CHECK("Prepare"); strcpy(mystmnt, "SELECT * FROM students"); EXEC SQL PREPARE selectstmnt2 FROM :mystmnt; EMB_SQL_CHECK("Prepare"); strcpy(mystmnt, "SELECT TABNAME, STATUS"); strcat(mystmnt, " FROM syscat.tables WHERE TABNAME='STUDENTRECORDS'"); EXEC SQL PREPARE selectstmnt3 FROM :mystmnt; EMB_SQL_CHECK("Prepare"); /* Declare cursors for the prepared SELECT statements */ EXEC SQL DECLARE stuRecCursor CURSOR FOR selectstmnt1; EMB_SQL_CHECK("Cursor: declare"); EXEC SQL DECLARE studentsCursor CURSOR FOR selectstmnt2; EMB_SQL_CHECK("Cursor: declare"); EXEC SQL DECLARE syscatCursor CURSOR FOR selectstmnt3; EMB_SQL_CHECK("Cursor: declare"); /* Display the contents of the tables to show that the records were inserted successfully */ TbContentDisplay(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE); return 0; } /* CreateTablespaceAndTablesWithData */ /* The following function displays data in two tables, studentrecords and students. The parameters passed to this function should be either TRUE or FALSE. If the parameter is true, a corresponding section of code is executed. For example: TbContentDisplay(TRUE, TRUE, TRUE, FALSE, TRUE, TRUE); will show the major column, show the data in both tables, will not expect failure, and will open and close all cursors used for displaying the tables. */ int TbContentDisplay(char useMajor, char showSTUDENTRECORDS, char showSTUDENTS, char expectStuRecFail, char openCursors, char closeCursors) { /* If the cursors are to be opened, open them */ if(openCursors) { EXEC SQL OPEN stuRecCursor; EMB_SQL_CHECK("Cursor: open"); EXEC SQL OPEN studentsCursor; EMB_SQL_CHECK("Cursor: open"); } /* Display the contents of the studentrecords table */ if (showSTUDENTRECORDS) { printf("\n SELECT * FROM studentrecords\n"); /* Print out header for the student record table */ if(useMajor) { printf(" Student ID GPA University Major\n"); printf(" ---------- ---- -------------------- "); printf("--------------------\n"); } else { printf(" Student ID GPA University \n"); printf(" ---------- ---- --------------------\n"); } /* Display all of the records in the studentrecords table */ while(sqlca.sqlcode != 100) { /* Check whether to retrieve the 'major' column */ if(useMajor) { EXEC SQL FETCH stuRecCursor INTO :stuID, :GPA, :university, :major; if (sqlca.sqlcode == 0) printf(" %10d %.2f %20s %s\n", stuID, GPA, university, major); } else { EXEC SQL FETCH stuRecCursor INTO :stuID, :GPA, :university; if (sqlca.sqlcode == 0) printf(" %10d %.2f %20s\n", stuID, GPA, university); } /* Check whether an error has occured before reaching the end of the table */ if (sqlca.sqlcode != 100) { /* Check whether the failure of the fetch is expected */ if (expectStuRecFail) { EXPECTED_ERR_CHECK("Cursor:fetch"); break; } else { EMB_SQL_CHECK("Cursor:fetch"); } } } } /* Display the contents of the students table */ if (showSTUDENTS) { printf("\n SELECT * FROM students\n"); /* Print out header for the student record table */ printf(" First Name Last Name "); printf("Student ID Employee ID\n"); printf(" -------------------- -------------------- "); printf("---------- -----------\n"); /* Fetch the first record. This is to reset the value of the sqlcode */ EXEC SQL FETCH studentsCursor INTO :firstName, :lastName, :stuID, :empID; /* Print out all of the records in the studentrecords table */ while(sqlca.sqlcode != 100) { /* Check whether an error has occured before reaching the end of the table */ if (sqlca.sqlcode != 100) EMB_SQL_CHECK("Cursor:fetch"); if (sqlca.sqlcode == 0) printf(" %20s %20s %10d %11d\n", firstName, lastName, stuID, empID); EXEC SQL FETCH studentsCursor INTO :firstName, :lastName, :stuID, :empID; } } /* Check whether to close the cursors */ if (closeCursors) { EXEC SQL CLOSE stuRecCursor; EMB_SQL_CHECK("Cursor: close"); EXEC SQL CLOSE studentsCursor; EMB_SQL_CHECK("Cursor: close"); } return 0; } /* TbContentDisplay */ /* The following function displays the integrity status for the studentrecords table. */ int TbIntegrityInfoDisplay() { /* Get the status of the studentrecords table from syscat.tables */ printf(" SELECT tabname, status\n"); printf(" FROM syscat.tables WHERE tabname='STUDENTRECORDS'\n"); EXEC SQL OPEN syscatCursor; EMB_SQL_CHECK("Cursor: open"); EXEC SQL FETCH syscatCursor INTO :tablename, :status; EMB_SQL_CHECK("Cursor: fetch"); EXEC SQL CLOSE syscatCursor; EMB_SQL_CHECK("Cursor: close"); /* Print out the status for the studentrecords table */ printf("\n Integrity status:\n"); printf(" Table Status\n"); printf(" -------------------- ------\n"); printf(" %20s %5s\n", tablename, status); return 0; } /* TbIntegrityInfoDisplay */ /*** Main functions ***/ /* The following function demonstrates the use of a savepoint. A savepoint is set, after which the studentrecords table is modified. A rollback is issued, invalidating the cursor for the studentrecords table. All changes that occurred after the savepoint was set are also undone. */ int SavepointWithAlterTable() { printf("\n-----------------------------------------------------------\n"); printf("USE THE SQL STATEMENTS:\n"); printf(" ALTER TABLE\n"); printf(" INSERT\n"); printf(" RELEASE SAVEPOINT\n"); printf(" ROLLBACK TO SAVEPOINT\n"); printf(" SAVEPOINT\n"); printf(" SELECT\n"); printf("TO ADD A 'MAJOR' COLUMN TO THE STUDENTRECORDS TABLE\n"); printf("AND ATTEMPT TO SELECT ROWS FROM THE STUDENTRECORDS TABLE \n"); printf("BEFORE AND AFTER ROLLING BACK TO THE SAVEPOINT.\n"); /* Set savepoint */ printf("\n SAVEPOINT sp ON ROLLBACK RETAIN CURSORS\n"); EXEC SQL SAVEPOINT sp ON ROLLBACK RETAIN CURSORS; EMB_SQL_CHECK("Savepoint: set"); /* Add a column to the studentrecords table */ printf("\n ALTER TABLE studentrecords\n"); printf(" ADD COLUMN major char(20) WITH DEFAULT 'Computer Science'\n"); strcpy(mystmnt, "ALTER TABLE studentrecords"); strcat(mystmnt, " ADD COLUMN major char(20) WITH DEFAULT 'Computer Science'"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Alter table: add column"); /* Show that the column has successfully been added to the studentrecords table, and that the cursors successfully retrieve the records from the studentrecords and students tables. */ TbContentDisplay(TRUE, TRUE, TRUE, FALSE, TRUE, TRUE); printf("\n NOTE:\n"); printf(" The 'major' column has successfully been added to the\n"); printf(" studentrecords table. As well, cursors for the\n"); printf(" studentrecords and students tables were able to\n"); printf(" successfully retrieve their records.\n"); /* Update the default value of the new column to 'Computer Science' */ printf("\n ALTER TABLE studentrecords\n"); printf(" ALTER COLUMN major SET DEFAULT 'English Lit'\n"); strcpy(mystmnt, "ALTER TABLE studentrecords"); strcat(mystmnt, " ALTER COLUMN major SET DEFAULT 'English Lit'"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Alter table: alter default value"); /* Insert a row to the table with the new default value */ printf("\n INSERT INTO students(firstname, lastname, studentid,"); printf(" emplyeeid)\n"); printf(" VALUES('Rolling', 'Hill', 9, 22))\n"); strcpy(mystmnt, "INSERT INTO students(firstname, lastname, studentid,"); strcat(mystmnt, " employeeid) VALUES('Rolling', 'Hill', 9, 22)"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Insert the new record into studentrecords table */ printf(" INSERT INTO studentrecords(studentid, gpa, university, major)\n"); printf(" VALUES(9, 3.0, 'After', default))\n"); strcpy(mystmnt, "INSERT INTO studentrecords(studentid, gpa, university, major)"); strcat(mystmnt, " VALUES(9, 3.0, 'After', default)"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Display the results, to see that only the new column has the */ /* modified default value */ TbContentDisplay(TRUE, TRUE, TRUE, FALSE, TRUE, FALSE); printf("\n NOTE:\n"); printf(" The 'major' column has successfully been altered \n"); printf(" for the record just inserted. Old rows retained their \n"); printf(" original values\n"); /* Rollback to the savepoint */ printf("\n ROLLBACK TO SAVEPOINT sp\n"); EXEC SQL ROLLBACK TO SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: rollback"); /* Show that the cursor for the studentrecords table has been invalidated while the cursor for the students table is still valid. */ TbContentDisplay(FALSE, TRUE, TRUE, TRUE, FALSE, TRUE); printf("\n NOTE:\n"); printf(" Due to the ROLLBACK TO SAVEPOINT, the ALTER TABLE\n"); printf(" statement applied to the studentrecords table has been\n"); printf(" undone and it's cursor has been invalidated, thus the\n"); printf(" SELECT statement for the studentrecords table fails.\n"); printf(" However, the student table was not modified within\n"); printf(" this savepoint, so it's cursor is valid and it's SELECT\n"); printf(" statement is successful.\n"); /* Set the savepoint */ printf("\n RELEASE SAVEPOINT sp\n"); EXEC SQL RELEASE SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: point"); return 0; } /* SavepointWithAlterTable */ /* The following function demonstrates the use of a savepoint. A savepoint is set, after which a global temporary table is declared. A rollback is issued, implicitly dropping the global temporary table. */ int SavepointWithGlobalTemporaryTable() { printf("\n-----------------------------------------------------------\n"); printf("USE THE SQL STATEMENTS:\n"); printf(" DECLARE GLOBAL TEMPORARY TABLE\n"); printf(" RELEASE SAVEPOINT\n"); printf(" ROLLBACK TO SAVEPOINT\n"); printf(" SAVEPOINT\n"); printf(" SELECT\n"); printf("TO CREATE AND INSERT DATA INTO A DECLARED GLOBAL TEMPORARY\n"); printf("TABLE, WITH THE SAME COLUMNS AS THE STUDENTS TABLE, WITHIN\n"); printf("A SAVEPOINT AND SHOW THAT THE TEMPORARY TABLE IS REMOVED AFTER\n"); printf("ROLLING BACK TO THE SAVEPOINT.\n"); /* Set savepoint */ printf("\n SAVEPOINT sp ON ROLLBACK RETAIN CURSORS\n"); EXEC SQL SAVEPOINT sp ON ROLLBACK RETAIN CURSORS; /* Create a temporary table */ printf("\n DECLARE GLOBAL TEMPORARY TABLE temptbl\n"); printf(" LIKE students NOT LOGGED\n"); EXEC SQL DECLARE GLOBAL TEMPORARY TABLE temptbl LIKE students NOT LOGGED; EMB_SQL_CHECK("Temporary Table: declare"); /* Insert a record into the temporary table using the required "session" qualifier */ printf("\n INSERT INTO session.temptbl\n"); printf(" (SELECT firstname, lastname, studentid, employeeid\n"); printf(" FROM students)\n"); EXEC SQL INSERT INTO session.temptbl (SELECT firstname, lastname, studentid, employeeid FROM students); EMB_SQL_CHECK("Temporary Table: insert"); /* Rollback to the savepoint */ printf("\n ROLLBACK TO SAVEPOINT sp\n"); EXEC SQL ROLLBACK TO SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: rollback"); /* Attempt to retrieve records from the temporary table after rolling back to the savepoint. */ printf("\n SELECT * FROM session.temptbl\n"); EXEC SQL DECLARE tmpselect CURSOR FOR SELECT * FROM session.temptbl; EMB_SQL_CHECK("Cursor: declare"); EXEC SQL OPEN tmpselect; EXPECTED_ERR_CHECK("Cursor: open"); printf("\n NOTE:\n"); printf(" The temporary table that was created within this savepoint\n"); printf(" was implicitly dropped during the ROLLBACK TO SAVEPOINT.\n"); printf(" So an attempt to select some records from the temporary\n"); printf(" table fails.\n"); /* Release the savepoint */ printf("\n RELEASE SAVEPOINT sp\n"); EXEC SQL RELEASE SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: release"); return 0; } /* SavepointWithGlobalTemporaryTable */ /* The following function demonstrates nested savepoints. */ int NestedSavepoints() { printf("\n-----------------------------------------------------------\n"); printf("USE THE SQL STATEMENTS:\n"); printf(" SAVEPOINT sp\n"); printf(" INSERT\n"); printf(" SAVEPOINT nestedsp\n"); printf(" INSERT\n"); printf(" SELECT\n"); printf(" ROLLBACK TO SAVEPOINT nestedsp\n"); printf(" SELECT\n"); printf(" INSERT\n"); printf(" ROLLBACK TO SAVEPOINT sp\n"); printf(" SELECT\n"); printf(" RELEASE SAVEPOINT nestedsp\n"); printf(" RELEASE SAVEPOINT sp\n"); printf("TO ATTEMPT TO CREATE A SAVEPOINT WITHIN A SAVEPOINT\n"); printf("(NESTED SAVEPOINTS) TO SHOW SAVEPOINTS CAN BE NESTED.\n"); /* Set savepoint */ printf("\n SAVEPOINT sp ON ROLLBACK RETAIN CURSORS\n"); EXEC SQL SAVEPOINT sp ON ROLLBACK RETAIN CURSORS; EMB_SQL_CHECK("Savepoint"); /* Insert 2 records into students table */ printf("\n INSERT INTO students(firstname, lastname, studentid,"); printf(" emplyeeid)\n"); printf(" VALUES('Evelyn', 'Moore', 10, 30),\n"); printf(" ('Jamie', 'Mak', 7, 75)\n"); strcpy(mystmnt, "INSERT INTO students(firstname, lastname, studentid,"); strcat(mystmnt, " employeeid) VALUES('Evelyn', 'Moore', 10, 30),"); strcat(mystmnt, " ('Jamie', 'Mak', 7, 75)"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Set a nested savepoint */ printf("\n SAVEPOINT nestedsp ON ROLLBACK RETAIN CURSORS\n"); EXEC SQL SAVEPOINT nestedsp on rollback retain cursors; EMB_SQL_CHECK("Savepoint"); /* Insert 2 records into students table */ printf("\n INSERT INTO students(firstname, lastname, studentid,"); printf(" emplyeeid)\n"); printf(" VALUES('Aaron', 'Johnson', 11, 31),\n"); printf(" ('Karl', 'Johnson', 8, 76)\n"); strcpy(mystmnt, "INSERT INTO students(firstname, lastname, studentid,"); strcat(mystmnt, " employeeid) VALUES('Aaron', 'Johnson', 11, 31),"); strcat(mystmnt, " ('Karl', 'Johnson', 8, 76)"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Show that the record has been added to the students table */ TbContentDisplay(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE); /* Rollback the nested savepoint */ printf("\n RELEASE SAVEPOINT nestedsp\n"); EXEC SQL ROLLBACK TO SAVEPOINT nestedsp; EMB_SQL_CHECK("Savepoint: rollback"); /* Show that the records inserted to the students table after savepoint nestedsp was rollback */ TbContentDisplay(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE); strcpy(mystmnt, "INSERT INTO students(firstname, lastname, studentid,"); strcat(mystmnt, " employeeid) VALUES('Erin', 'Tory', 11, 31)"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Rollback to the savepoint */ printf("\n ROLLBACK TO SAVEPOINT sp\n"); EXEC SQL ROLLBACK TO SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: rollback"); /* Show that the records inserted to the students table after savepoint sp was rollback */ TbContentDisplay(FALSE, TRUE, TRUE, FALSE, TRUE, TRUE); /* Release the nested savepoint */ printf("\n RELEASE SAVEPOINT nestedsp\n"); EXEC SQL RELEASE SAVEPOINT nestedsp; EXPECTED_ERR_CHECK("Savepoint: release"); printf("\n NOTE:\n"); printf(" Since the savepoint nestedsp is started after savepoint sp, the\n"); printf(" rollback of savepoint sp has removed the savepoints nested inside.\n"); /* Release the nested savepoint */ printf("\n RELEASE SAVEPOINT sp\n"); EXEC SQL RELEASE SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: release"); return 0; } /* NestedSavepoints */ /* The following function demonstrates that table integrity is retained by a savepoint. If a ROLLBACK TO SAVEPOINT is issued and a savepoint exists before a table's integrity is modified, changes to the table's integrity are rolled back. */ int SavepointWithSetIntegrity() { printf("\n-----------------------------------------------------------\n"); printf("USE THE SQL STATEMENTS:\n"); printf(" RELEASE SAVEPOINT\n"); printf(" ROLLBACK TO SAVEPOINT\n"); printf(" SAVEPOINT\n"); printf(" SELECT\n"); printf(" SET INTEGRITY\n"); printf("WITHIN A SAVEPOINT, TO SET INTEGRITY FOR STUDENTRECORDS\n"); printf("(CHANGE THE STATUS OF THE STUDENTRECORDS TABLE, IN\n"); printf("SYSCAT.TABLES, FROM 'N', FOR NORMAL to 'C' FOR CHECK PENDING\n"); printf("STATE), THEN ROLLBACK TO THE SAVEPOINT AND SHOW THAT THE\n"); printf("STATUS OF THE STUDENTRECORDS TABLE IS RETURNED TO 'N'\n"); printf(" (NORMAL STATE).\n"); /* Set savepoint */ printf("\n SAVEPOINT sp ON ROLLBACK RETAIN CURSORS\n\n"); EXEC SQL SAVEPOINT sp ON ROLLBACK RETAIN CURSORS; EMB_SQL_CHECK("Savepoint"); /* Print out the current integrity status for the studentrecords table */ TbIntegrityInfoDisplay(); /* Set the integrity for the studentrecords table to CHECK PENDING NO ACCESS state, and IMMEDIATELY CASCADE. */ printf("\n SET INTEGRITY FOR studentrecords\n"); printf(" OFF NO ACCESS CASCADE IMMEDIATE\n\n"); strcpy(mystmnt, "SET INTEGRITY FOR studentrecords OFF NO ACCESS CASCADE IMMEDIATE"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Set integrity"); /* Print out the update integrity status for the studentrecords table */ TbIntegrityInfoDisplay(); /* Rollback to the savepoint */ printf("\n ROLLBACK TO SAVEPOINT sp\n\n"); EXEC SQL ROLLBACK TO SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: rollback"); /* Print out the current integrity status for the studentrecords table to show it has been reset to it's original status. */ TbIntegrityInfoDisplay(); /* Release the savepoint */ printf("\n RELEASE SAVEPOINT sp\n"); EXEC SQL RELEASE SAVEPOINT sp; EMB_SQL_CHECK("Savepoint: release"); return 0; } /* SavepointWithSetIntegrity */ /* The following function demonstrates a special case of savepoints. If a table is set to NOT LOG INITIALLY mode and a new row is inserted, rolling back to a savepoint fails. This failure causes the entire unit of work to rollback. This means that the initially created savepoint is no longer available. */ int SavepointWithNotLoggedInitiallyTable() { printf("\n-----------------------------------------------------------\n"); printf("USE THE SQL STATEMENTS:\n"); printf(" ALTER TABLE\n"); printf(" COMMIT\n"); printf(" INSERT\n"); printf(" RELEASE SAVEPOINT\n"); printf(" ROLLBACK TO SAVEPOINT\n"); printf(" SAVEPOINT\n"); printf(" SELECT\n"); printf("TO ALTER THE STUDENTS TABLE TO BE NOT LOGGED INITIALLY,\n"); printf("SET A SAVEPOINT, INSERT A RECORD INTO THE STUDENTS\n"); printf("TABLE, THEN ATTEMPT TO ROLLBACK TO THE SAVEPOINT\n"); /* Prevent the loss of the earlier work done by this sample */ printf("\n COMMIT\n"); EXEC SQL COMMIT; EMB_SQL_CHECK("Commit"); /* Activate the students table's NOT LOGGED INITIALLY mode */ printf("\n ALTER TABLE students ACTIVATE NOT LOGGED INITIALLY\n"); strcpy(mystmnt, "ALTER TABLE students ACTIVATE NOT LOGGED INITIALLY"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Alter table: not logged initially"); /* Set savepoint */ printf("\n SAVEPOINT sp ON ROLLBACK RETAIN CURSORS\n"); EXEC SQL SAVEPOINT sp ON ROLLBACK RETAIN CURSORS; EMB_SQL_CHECK("Savepoint"); /* Insert another record into the students table */ printf("\n INSERT INTO students(firstname, lastname, studentid,"); printf(" emplyeeid)\n"); printf(" VALUES('Jack', 'Benimble', 9, 11)\n"); strcpy(mystmnt, "INSERT INTO students(firstname, lastname, studentid,"); strcat(mystmnt, " employeeid) VALUES('Jack', 'Benimble', 9, 11)"); EXEC SQL EXECUTE IMMEDIATE :mystmnt; EMB_SQL_CHECK("Table: insert"); /* Show that the record has been added to the students table */ TbContentDisplay(TRUE, FALSE, TRUE, FALSE, TRUE, TRUE); /* Rollback to the savepoint */ printf("\n ROLLBACK TO SAVEPOINT sp\n"); EXEC SQL ROLLBACK TO SAVEPOINT sp; EXPECTED_ERR_CHECK("Savepoint: rollback"); printf("\n NOTE:\n"); printf(" Since the students table is in NOT LOGGED INITIALLY\n"); printf(" mode rolling back to a savepoint fails with an error. The\n"); printf(" result is the entire unit of work being rolled back and\n"); printf(" the students table being put into a 'drop only' state.\n"); /* Release the savepoint */ printf("\n RELEASE SAVEPOINT sp\n"); EXEC SQL RELEASE SAVEPOINT sp; EXPECTED_ERR_CHECK("Savepoint: release"); printf("\n NOTE:\n"); printf(" Since the entire unit of work has been rolled back, the\n"); printf(" savepoint no longer exists. So the RELEASE SAVEPOINT\n"); printf(" statement fails.\n"); return 0; } /* SavepointWithNotLoggedInitiallyTable */