Can I use sqitch-rework for non-idempotent changes? - sqitch

After reading the sqitch-rework documentation and the Altering Table with Sqitch Rework command stackoverflow question, I am aware that sqitch-rework was intended for idempotent changes.
But why?
It seems that my use of sqitch-rework works perfectly fine with a non-idempotent change.
Here is how I used it with PostgreSQL:
original deploy:
-- Deploy myproject:database/facility/schema/public/table/mytable to pg
-- requires: database/facility/schema/public
SET ROLE :"owner_role";
BEGIN;
CREATE TABLE public."MyTable"(
"Date" timestamp without time zone NOT NULL
);
COMMIT;
original revert:
-- Revert myproject:database/facility/schema/public/table/mytable from pg
SET ROLE :"owner_role";
BEGIN;
DROP TABLE public."MyTable";
COMMIT;
reworked deploy:
-- Deploy myproject:database/facility/schema/public/table/mytable to pg
-- requires: database/facility/schema/public
SET ROLE :"owner_role";
BEGIN;
LOCK TABLE public."MyTable" IN ACCESS EXCLUSIVE MODE;
ALTER TABLE public."MyTable" RENAME COLUMN "Date" TO "Complete time";
ALTER TABLE public."MyTable" ADD COLUMN "Start time" timestamp without time zone;
UPDATE public."MyTable" SET "Start time" = "Complete time";
ALTER TABLE public."MyTable" ALTER COLUMN "Start time" SET NOT NULL;
COMMIT;
reworked revert:
-- Revert myproject:database/facility/schema/public/table/mytable from pg
SET ROLE :"owner_role";
BEGIN;
LOCK TABLE public."MyTable" IN ACCESS EXCLUSIVE MODE;
ALTER TABLE public."MyTable" DROP COLUMN "Start time";
ALTER TABLE public."MyTable" RENAME COLUMN "Complete time" TO "Date";
COMMIT;
Am I not understanding something? Isn't this a valid use of sqitch-rework?

Related

different date format to one date format in table

I have two DATE column in table oracle database(12c).sysdate format is:
SQL> select sysdate from dual;
SYSDATE
---------
25-NOV-17
SQ>desc test_table
id NUMBER(10)
LAST_CREATED_DATE DATE
IS_CREATED_DATE DATE
where LAST_CREATED_DATE has different format to IS_CREATED_DATE(sysdate).
because LAST_CREATED_DATE is fixed and i'm reading from file(date format:20100330) where as IS_CREATED_DATE am inserting as sysdate(current date).
insert into test_table (id,LAST_CREATED_DATE,IS_CREATED_DATE) values (123,20100930,sysdate);
but with this insert statement am facing errors.
I tried ALTER SESSION SET NLS_DATE_FORMAT = 'yyyymmdd';.This works fine in current session,but looking for by which i can change ORACLE database date format to yyyymmdd(linux).
You should change your statement like this:
insert into test_table (id,LAST_CREATED_DATE,IS_CREATED_DATE)
values (123,to_date('20100930','yyyymmdd'),sysdate);
There are three ways to accomplish this task :
1)
SQL>conn myschema/mypwd
SQL>insert into test_table values(123,to_date('20100930','yyyymmdd'),sysdate);
2) as you mentioned
SQL>alter session set nls_date_format = 'yyyymmdd';
SQL>insert into test_table values(123,'20100930',sysdate); -- notice that 20100930 is quoted
3) globally(along with the db, needs restart, maybe dangerous on a production system in coordination with existing applications' date format model)
SQL>conn / as sysdba
SQL>alter system set nls_date_format = 'yyyymmdd' scope=spfile;
SQL>shutdown immediate;
SQL>startup;
SQL>conn myschema/mypwd
SQL>insert into test_table values(123,'20100930',sysdate); -- from now on, you don't need to alter your date parameter for every session

ORA-02437: "primary key violated" - why can't I see duplicate ID in SQL Developer?

I would receive an error:
ORA-02437: cannot validate (%s.%s) - primary key violated
Cause: attempted to validate a primary key with duplicate values or null values
I found it was because I have a stored procedure that increments the ID, but it had failed to do so when it re-ran and had an error related to one of my datatypes. I found I now had a duplicate ID in my database table. All this made sense and I was able to easily rectify it with a DELETE FROM MyTable WHERE ID = x, where x was the offending duplicate ID. The problem I have is the only way I was able to even find the IDs that were duplicated is in the first place is because I did a SELECT * FROM MyTable WHERE ID = x -- where x was one greater than the last ID I could actually see. I found it just by an educated guess. So:
Why can't I see these duplicate IDs when I open the table in Oracle SQL Developer? It only shows the last row as the ID before the duplicates. I don't think it is because of my primary key constraint, since the first line in my stored procedure is to remove that (and put it back, at the end - probably when I got my error), and it was not present when I looked at my table.
Is there some way to make these last IDs that got inserted into the table visible, so I wouldn't have to guess or assume that the duplicate IDs are "hiding" as one greater than the last ID I have in my table, in the future? There is a commit; in my stored procedure, so they should have appeared -- unless, of course, the procedure got hung up before it could run that line of code (highly probable).
Stored procedure that runs:
create or replace
PROCEDURE PRC_MYTABLE_INTAKE(
, EMPLOYEE_ID IN NVARCHAR2
, TITLE_POSITION IN NVARCHAR2
, CREATED_DATE IN DATE
, LAST_MODIFIED IN DATE
) AS
myid integer := 0;
appid integer := 0;
BEGIN
-- disable PK constraint so it can be updated
EXECUTE IMMEDIATE 'ALTER TABLE MYTABLE DROP CONSTRAINT MYTABLE_PK';
COMMIT;
-- assign ID to myid
SELECT ID INTO myid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
-- increment
myid := myid + 1;
-- assign APPLICATION_ID to appid
SELECT APPLICATION_ID INTO appid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
-- increment
appid := appid + 1;
-- use these ids to insert with
INSERT INTO MYTABLE (ID, APPLICATION_ID,
, EMPLOYEE_ID
, TITLE_POSITION
, CREATED_DATE
, LAST_MODIFIED
) VALUES(myid, appid,
, EMPLOYEE_ID
, TITLE_POSITION
, CREATED_DATE
, LAST_MODIFIED
);
COMMIT;
-- re-enable the PK constraint
EXECUTE IMMEDIATE 'ALTER TABLE PASS ADD CONSTRAINT MYTABLE_PK PRIMARY KEY (ID)';
COMMIT;
END;
Here's one problem:
SELECT ID
INTO myid
FROM MYTABLE
WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE)
There is no correlation between ID and ROWID, so you're not getting the maximum current ID, you're just getting the one that happens to be on the row that is furthest from the start of a datafile with a high number.
The code you need is:
SELECT COALESCE(MAX(ID),0)
FROM MYTABLE;
Or better yet, just use a sequence.
No idea why you're dropping the PK either.
Furthermore, when you issue the query:
SELECT APPLICATION_ID INTO appid ...
... that could be for a different row than the one you already got the id for, because a change could have been committed to the table.
Of course another issue is that you can't run two instances of this procedure at the same time either.
For David Aldridge, since he wants to look at code instead of the real reason I posted my question, run this ---
CREATE TABLE YOURSCHEMA.TESTING
(
TEST_ID NVARCHAR2(100) NOT NULL
, TEST_TYPE NVARCHAR2(100) NOT NULL
, CONSTRAINT TEST_PK PRIMARY KEY
(
TEST_ID
)
ENABLE
);
create or replace
PROCEDURE PRC_TESTING_INSERT(
TEST_TYPE IN NVARCHAR2
) AS
testid integer := 0;
BEGIN
-- disable PK constraint so it can be updated
EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK';
COMMIT;
-- assign TEST_ID to testid
SELECT TEST_ID INTO testid FROM TESTING WHERE ROWID IN (SELECT MAX(ROWID) FROM TESTING);
-- increment
testid := testid + 1;
-- use this id to insert with
INSERT INTO TESTING (TEST_ID, TEST_TYPE) VALUES(testid, TEST_TYPE);
COMMIT;
-- re-enable the PK constraint
EXECUTE IMMEDIATE 'ALTER TABLE TESTING ADD CONSTRAINT TEST_PK PRIMARY KEY (TEST_ID)';
COMMIT;
END;
SET serveroutput on;
DECLARE
test_type varchar(100);
BEGIN
test_type := 'dude';
YOURSCHEMA.PRC_TESTING_INSERT(test_type);
-- to verify the variable got set and procedure ran, could do:
--dbms_output.enable;
--dbms_output.put_line(test_type);
END;
Now, because there is no data in the table, the stored procedure will fail with ORA-06512: no data found. If you then try and run it again, you will get ORA-02443: cannot drop constraint - nonexistent constraint, because the EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK'; successfully dropped it, and the procedure never ran the command at the end to re-add it. This is what made me think I needed the commits, but even without them, it still will not complete the whole procedure.
To prove that the procedure DOES run, if given proper data, run this after creating the table, but before creating/running the stored procedure:
INSERT INTO TESTING (TEST_ID, TEST_TYPE)
VALUES ('1', 'hi');
And if you run the proc from a new table (not one with its constraint dropped), it will run fine.
Since mathguy didn't post this as the answer, though I'll credit him for the information...
Answer to why I can't see the duplicates is because the COMMIT does not occur in the procedure when it failed due to a datatype mismatch (which we found was actually in the application's code that sent the variable's values into this procedure, not in the stored procedure, itself). (It's also why I'll mark down anyone that says you don't have to add so many COMMIT lines in this procedure.) The commands were run in the session of the user that starts it - in my case, another session of the same DB user I was logged in with, but started from my application, instead of my SQL Developer session. It also explains why I could do a COMMIT, myself, but it did not affect the application's session - I could not commit any actions ran from another session. Had I ran a COMMIT as an OracleCommand and did an .ExecuteNonQuery on my OracleConnection right after the failure within the catch of my application, I would have seen the rows in SQL Developer without having to do a special query.
So, in short, the only way to see the items was with a direct query using WHERE ID =, find the last ID and increment it, and put it in the query.

SELECT AS OF a version before column drop

1/ Does FLASHBACK and SELECT AS OF/ VERSION BETWEEN use the same source of history to fall back to ? This question is related to the second question.
2/ I am aware that FLASHBACK cannot go back before a DDL change.
My question is for SELECT AS OF, would it be able to select something before a DDL change.
Take for example
CREATE TABLE T
(col1 NUMBER, col2 NUMBER)
INSERT INTO T(col1, col2) VALUES('1', '1')
INSERT INTO T(col1, col2) VALUES('2', '2')
COMMIT;
SLEEP(15)
ALTER TABLE T DROP COLUMN col2;
SELECT * FROM T
AS OF SYSTIMESTAMP - INTERVAL '10' SECOND;
Would the select return 2 columns or 1 ?
Pardon me I do not have a database at hand to test.
Any DDL that alter the structure of a table invalidates any existing undo data for the table. So you will get the error 'ORA-01466' unable to read data - table definition has changed.
Here is a simple test
CREATE TABLE T
(col1 NUMBER, col2 NUMBER);
INSERT INTO T(col1, col2) VALUES('1', '1');
INSERT INTO T(col1, col2) VALUES('2', '2');
COMMIT;
SLEEP(15)
ALTER TABLE T DROP COLUMN col2;
SELECT * FROM T
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '60' SECOND);
ERRROR ORA-01466 upon executing the above select statement.
However DDL operations that alter the storage attributes of a table do no invalidate undo data so that you can still use flashback query.
1) FLASHBACK TABLE and SELECT .. AS OF use the same source, UNDO. There is also FLASHBACK DATABASE - although it uses the same mechanism it uses a separate source, flashback logs that must be optionally configured.
2) Flashback table and flashback queries can go back before a DDL change if you enable a flashback archive.
To use that feature, add a few statements to the sample code:
CREATE FLASHBACK ARCHIVE my_flashback_archive TABLESPACE users RETENTION 10 YEAR;
...
ALTER TABLE t FLASHBACK ARCHIVE my_flashback_archive;
Now this statement will return 1 column:
SELECT * FROM T;
And this statement will return 2 columns:
SELECT * FROM T AS OF SYSTIMESTAMP - INTERVAL '10' SECOND;

How to alter the data type of a column from varchar2 to number in Oracle database

When I ran the alter query, I get error saying that column to be modified must me empty.
Table : Monthly_Result (Id Number(38,0), dealer_ID varchar2, sales_revenue Number(38,2))
dealer_Id should be changed to Number(38,0)
Please help
As Alex mentioned in his comment you will need to add new column; update it and check values were converted correctly; then drop the old column when you're ready.
-- step 1
alter table monthly_result add tmp number(38, 0);
update monthly_result set tmp = to_number(dealer_id);
-- step 2
-- check values are set correctly in tmp
-- step 3
alter table monthly_result rename column dealer_id to dealer_id_old;
alter table monthly_result rename column tmp to dealer_id;
-- step 4
alter table monthly_result drop column dealer_id_old;
Please try this it would be help the update add column things you will loss your primary key and position of the table:
--COPY THE DATA TO TEMPORARY TABLE
CREATE TABLE _TMP AS SELECT * FROM ;
--TRUNCATE OLD DATA
TRUNCATE TABLE ;
--ALTER SPECIFIC COLUMN TYPE
alter table MODIFY number(38, 0);
--BRING THE DATA FROM WHICH COPY PREVIOUS TEMPORARY TABLE
INSERT INTO SELECT * FROM _TMP;
--CHECK YOUR DATA
SELECT * FROM ;
--DROP YOUR TEMPORARY TABLE
DROP TABLE _TMP;
I believe it will work for you.

Oracle APEX don't create triggers when import sql file

I have a SQL file exported from Oracle Express (most probably with Navicat Lite), and there are queries of creating triggers.
But after I imported the file with APEX (SQL Workshop -> SQL Script), the triggers are not created successfully, with no errors.
The queries are like this, they are definitely valid, and I can successfully create the triggers when I manually run these queries with SQL Command.
create or replace TRIGGER "RESENV"."AREA_ID_AUTOINCREMENT" BEFORE INSERT ON "RESENV"."AREA" REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW WHEN (new.id is null)
begin
select area_id_seq.nextval into :new.id from dual;
end;;
-- ----------------------------
-- Checks structure for table "RESENV"."AREA"
-- ----------------------------
ALTER TABLE "RESENV"."AREA" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "RESENV"."AREA" ADD CHECK ("NAME" IS NOT NULL);
-- ----------------------------
-- Checks structure for table "RESENV"."DATASET"
-- ----------------------------
ALTER TABLE "RESENV"."DATASET" ADD CHECK ("SYSTEM_ID" IS NOT NULL);
ALTER TABLE "RESENV"."DATASET" ADD CHECK ("AREA_ID" IS NOT NULL);
ALTER TABLE "RESENV"."DATASET" ADD CHECK ("TABLE_NAME" IS NOT NULL);
ALTER TABLE "RESENV"."DATASET" ADD CHECK ("CREATED" IS NOT NULL);
-- ----------------------------
-- Triggers structure for table "RESENV"."IDX"
-- ----------------------------
CREATE OR REPLACE TRIGGER "RESENV"."IDX_ID_AUTOINCREMENT" BEFORE INSERT ON "RESENV"."IDX" REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW ENABLE WHEN (new.id is null)
begin
select idx_id_seq.nextval into :new.id from dual;
end;;
-- ----------------------------
-- Checks structure for table "RESENV"."IDX"
-- ----------------------------
ALTER TABLE "RESENV"."IDX" ADD CHECK ("ID" IS NOT NULL);
-- ----------------------------
-- Triggers structure for table "RESENV"."SYSTEM"
-- ----------------------------
CREATE OR REPLACE TRIGGER "RESENV"."SYSTEM_ID_AUTOINCREMENT" BEFORE INSERT ON "RESENV"."SYSTEM" REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW ENABLE WHEN (new.id is null)
begin
select SYSTEM_ID_SEQ.nextval into :new.id from dual;
end;;
-- ----------------------------
-- Checks structure for table "RESENV"."SYSTEM"
-- ----------------------------
ALTER TABLE "RESENV"."SYSTEM" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "RESENV"."SYSTEM" ADD CHECK ("SYSTEM_CREATED" IS NOT NULL);
ALTER TABLE "RESENV"."SYSTEM" ADD CHECK ("SYSTEM_MODIFIED" IS NOT NULL);
ALTER TABLE "RESENV"."SYSTEM" ADD CHECK ("USER_ID" IS NOT NULL);
-- ----------------------------
-- Checks structure for table "RESENV"."UNIT"
-- ----------------------------
ALTER TABLE "RESENV"."UNIT" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "RESENV"."UNIT" ADD CHECK ("NAME" IS NOT NULL);
ALTER TABLE "RESENV"."UNIT" ADD CHECK ("GD_ID" IS NOT NULL);
ALTER TABLE "RESENV"."UNIT" ADD CHECK ("AREA_ID" IS NOT NULL);
-- ----------------------------
-- Checks structure for table "RESENV"."USERS"
-- ----------------------------
ALTER TABLE "RESENV"."USERS" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "RESENV"."USERS" ADD CHECK ("NAME" IS NOT NULL);
ALTER TABLE "RESENV"."USERS" ADD CHECK ("PASSWORD" IS NOT NULL);
-- ----------------------------
-- Checks structure for table "RESENV"."VLUE"
-- ----------------------------
ALTER TABLE "RESENV"."VLUE" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "RESENV"."VLUE" ADD CHECK ("UNIT_ID" IS NOT NULL);
ALTER TABLE "RESENV"."VLUE" ADD CHECK ("IDX_ID" IS NOT NULL);
ALTER TABLE "RESENV"."VLUE" ADD CHECK ("IDX_VALUE" IS NOT NULL);
​
I have tried many times, and same result.
Why this happen? Is this a bug of APEX?
UPDATE
I have these lines shown above at the end of the SQL file (before those are the lines of creating tables and inserting data), but APEX treated all these lines as ONE statement, and created only one trigger whose code is as above (of course there is an error in the code of the trigger, but APEX didn't mention that when run the SQL script).
I have tried with Navicat to import the dump file (with Execute SQL file function). And the errors are as listed, which I think it should be OK.
Update 2
It turns out I have to add a slash / at the end of each create trigger statement, otherwise there can only be one trigger created in each script file.
see this questin
But the triggers still have errors with the above statements and a slash.
The code is:
create or replace TRIGGER "RESENV"."SYSTEM_ID_AUTOINCREMENT" BEFORE INSERT ON "RESENV"."SYSTEM" REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW WHEN (new.id is null)
begin
select SYSTEM_ID_SEQ.nextval into :new.id from dual;
end;;
​
and the error is: Line 3 Position 5, PLS-00103: Encountered the symbol ";"
I don't know why navcat exported like this.
Update
It turns out that the double semicolons caused the problem. After I removed one, there is no error any more.

Resources