I'm new in postgres, I'm creating a procedure to rename table constraint names using the REPLACE(). If I test all the variables in this procedure the data is there and if I replace it manually it can. The problem is when this procedure is running the constraint name doesn't change.
create or replace procedure public.rename_existing_constraint_table(in table_name text, in date_now text, in list_constraint text[])
as $$ declare
const text;
table_rename text;
begin
table_rename := (select concat(table_name, '_', date_now));
if array_length(list_constraint, 1) >= 1 then
foreach const in array list_constraint loop
execute 'alter table if exists ' || table_name || ' RENAME CONSTRAINT ' || const || ' to ' || replace(const, table_name, table_rename);
end loop;
end if;
end $$
language plpgsql;
duplicate error because the data was not successfully renamed, it should be
app_devlogdetail_pkey to app_devlogdetail_20221214_pkey
psycopg2.errors.DuplicateTable: relation "app_devlogdetail_pkey" already exists
CONTEXT: SQL statement "alter table if exists app_devlogdetail_20221214 RENAME CONSTRAINT app_devlogdetail_pkey to app_devlogdetail_pkey"
PL/pgSQL function rename_existing_constraint_table(text,text,text[]) line 10 at EXECUTE
I have tried running the REPLACE() outside the procedure and it runs normally and the data can be changed, but when it is run inside the procedure to be executed the REPLACE() can't changed data. How to make the REPLACE() can run and the data can be changed or is there some other way around it?
Related
I am facing a problem I have a function which used to create schema and tables inside that schema after table creation I am calling a function which supposed to populate this schema however feels like the second function doesn't set the working schema and throws the error that object doesn't exits (
ERROR: relation "table" does not exist
LINE 1: INSERT INTO table
here is what the function looks like.
CREATE OR REPLACE FUNCTION create_schema(
t_shema character varying,
t_country TEXT
)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
DECLARE
tname text := t_shema;
tschem_name text := tname||'_work';
tsql_dyn text ;
tschema_check numeric := 0 ;
BEGIN
SELECT 1
INTO TSCHEMA_CHECK
FROM PG_NAMESPACE
WHERE NSPNAME = TSCHEM_NAME;
IF TSCHEMA_CHECK = 1 THEN
RETURN 'Schema '||tschem_name ||' Already exists';
ELSE
tsql_dyn := 'CREATE SCHEMA '||tschem_name||';';
raise notice 'EXECUTE %', tsql_dyn;
EXECUTE tsql_dyn;
tsql_dyn := 'SET search_path TO '||tschem_name ||';';
raise notice 'EXECUTE %', tsql_dyn;
EXECUTE tsql_dyn;
--other DDLs
---execute of function which populates freshly created schema
SELECT public.populate_empty_schema(tname, t_country);
RETURN tname ||' created';
END IF;
END;
$BODY$;
The second function also has a statement as first which sets the working schema.
Both functions work fine if get called separately, trows error only if second get called from the first
Your function is vulnerable to SQL injection.
Instead of
tsql_dyn := 'CREATE SCHEMA '||tschem_name||';';
write
tsql_dyn := format('CREATE SCHEMA %I', tschem_name);
To set the search_path in populate_empty_schema, pass the schema name to the function and have it execute
PERFORM set_config('search_path', v_schema, TRUE);
feels like Postgres cannot set working schema from the second function, my solution. was just deleting the set search path from the second function and it is working. feels like it couldn't set it as shcema is not committed at the point when the 2-nd function gets called.
I have the following code to create a function that truncates all rows from the table web_channel2 if the table is not empty:
create or replace function truncate_if_exists(tablename text)
returns void language plpgsql as $$
begin
select
from information_schema.tables
where table_name = tablename;
if found then
execute format('truncate %I', tablename);
end if;
end $$;
Unfortunately I don't know how should I continue ...
How to execute the function?
TLDR
To execute a Postgres function (returning void), call it with SELECT:
SELECT truncate_if_exists('web_channel2');
Proper solution
... how should I continue?
Delete the function again.
DROP FUNCTION truncate_if_exists(text);
It does not offer any way to schema-qualify the table. Using it might truncate the wrong table ...
Looks like you are trying to avoid an exception if the table is not there.
And you only want to truncate ...
if the table is not empty
To that end, I might use a safe function like this:
CREATE OR REPLACE FUNCTION public.truncate_if_exists(_table text, _schema text DEFAULT NULL)
RETURNS text
LANGUAGE plpgsql AS
$func$
DECLARE
_qual_tbl text := concat_ws('.', quote_ident(_schema), quote_ident(_table));
_row_found bool;
BEGIN
IF to_regclass(_qual_tbl) IS NOT NULL THEN -- table exists
EXECUTE 'SELECT EXISTS (SELECT FROM ' || _qual_tbl || ')'
INTO _row_found;
IF _row_found THEN -- table is not empty
EXECUTE 'TRUNCATE ' || _qual_tbl;
RETURN 'Table truncated: ' || _qual_tbl;
ELSE -- optional!
RETURN 'Table exists but is empty: ' || _qual_tbl;
END IF;
ELSE -- optional!
RETURN 'Table not found: ' || _qual_tbl;
END IF;
END
$func$;
To execute, call it with SELECT:
SELECT truncate_if_exists('web_channel2');
If no schema is provided, the function falls back to traversing the search_path - like your original did. If that's unreliable, or generally, to be safe (which seems prudent when truncating tables!) provide the schema explicitly:
SELECT truncate_if_exists('web_channel2', 'my_schema');
db<>fiddle here
When providing identifiers as strings, you need to use exact capitalization.
Why the custom variable _row_found instead of FOUND? See:
Dynamic SQL (EXECUTE) as condition for IF statement
Basics:
Table name as a PostgreSQL function parameter
How to check if a table exists in a given schema
PL/pgSQL checking if a row exists
How does the search_path influence identifier resolution and the "current schema"
Are PostgreSQL column names case-sensitive?
I created procedure in oracle that drops my table and creates same table from my view. But I have some problems with running this procedure. First step with drop table works but copying it doesn't work.
It this a good procedure ?
create or replace PROCEDURE transfer_table (table_name IN VARCHAR2,tableFrom IN VARCHAR2) IS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
EXECUTE IMMEDIATE 'CREATE TABLE ' || table_name || ' AS SELECT * FROM ' || tableFrom;
commit;
END transfer_table;
Next I click on this procedure and change the variable then view and click ok, but only the first step of dropping the table is working. What am I doing wrong ?
I think you need to catch the error if the table you are creating doesn't exist.
create or replace PROCEDURE transfer_table (table_name IN VARCHAR2,tableFrom IN VARCHAR2) IS
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
EXCEPTION WHEN OTHERS THEN
NULL;
END;
EXECUTE IMMEDIATE 'CREATE TABLE ' || table_name || ' AS SELECT * FROM ' || tableFrom;
commit;
END transfer_table;
I am trying to build a package that will take in a table of table names and either drop from or delete those tables. I am using dynamic sql, and dropping or deleting the tables works, but I need both the procedures to loop through all of the table names passed back to it.
I've tried mulitple ways - including trying to create a FOR Loop and a cursor. Here is a similar function I wrote in PostgreSQL that works but I'm having trouble translating it to Oracle.
Here is my function in PostgreSQL that works:
CREATE OR REPLACE FUNCTION drop_tables_for_stnd_mod_build(tablenames text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
tab_name RECORD;
BEGIN
FOR tab_name IN EXECUTE 'SELECT table_name FROM ' || tablenames
LOOP
EXECUTE 'DROP TABLE ' || tab_name.table_name || ' CASCADE';
END LOOP;
END;
$function$
;
And the procedure I'm writing as part of a package in Oracle
CREATE OR REPLACE PACKAGE BODY stnd_build_table_cleanup
AS
PROCEDURE drop_tables(table_in CLOB)
IS
TYPE cur_type is REF CURSOR;
c cur_type;
query_string VARCHAR(300);
loop_string VARCHAR(300);
table_name VARCHAR(100);
BEGIN
loop_string := 'SELECT tablenames FROM :table';
OPEN c FOR loop_string USING table_in;
LOOP
FETCH c INTO table_name;
query_string := 'DROP TABLE ' || table_name || ' CASCADE CONSTRAINTS';
-- dbms_output.PUT_LINE (query_string);
EXECUTE IMMEDIATE query_string;
EXIT WHEN c%NOTFOUND;
END LOOP ;
CLOSE c;
END drop_tables;
Here is the error I get when I try to call my function: Error report -
ORA-00903: invalid table name
ORA-06512: at "AMS_NYS.STND_BUILD_TABLE_CLEANUP", line 13
ORA-06512: at line 2
00903. 00000 - "invalid table name"
*Cause:
*Action:
Thanks!
Here's one possibility. Note that I coded this as a standalone procedure for simplicity.
CREATE OR REPLACE TYPE table_type IS TABLE OF VARCHAR2(128);
CREATE OR REPLACE PROCEDURE drop_tables(tables_to_drop_in table_type)
IS
BEGIN
FOR i IN tables_to_drop_in.FIRST .. tables_to_drop_in.LAST LOOP
--DBMS_OUTPUT.PUT_LINE(tables_to_drop_in(i));
EXECUTE IMMEDIATE 'DROP TABLE ' || tables_to_drop_in(i) || ' CASCADE CONSTRAINTS';
END LOOP;
END drop_tables;
DECLARE
tables_to_drop table_type;
BEGIN
tables_to_drop := table_type('TBL1','TBL2', 'TBL3');
drop_tables(tables_to_drop);
END;
I'm trying to create a procedure that given a table name, it will create a sequence and auto incrementing trigger, all using variables based on the table name.
Code :
CREATE OR REPLACE procedure CREATE_SEQUENTIAL_TR(table_name VARCHAR)
is -- Tried using declare but it wouldn't accept
coluna_cod varchar(100 char);
begin
--Finding the cod column name for this table first
--They start with "PK_CD"
select
COLUMN_NAME
into
coluna_cod
from
ALL_TAB_COLUMNS
where
TABLE_NAME=table_name
and COLUMN_NAME like "PK_CD%";
--Creating the sequence obj
drop sequence "cod" || table_name;
create sequence "cod" || table_name;
--Now creating the trigger
create or replace trigger "cod" || table_name || "tr"
before
UPDATE or INSERT on table_name
for each row
declare
cod number := coluna_cod;
tr_name varchar(100 char) := "cod" || table_name
begin
if UPDATING then
if :new.cod != :old.cod then
:new.cod := :old.cod;
end if;
else -- inserting
:new.cod := tr_name.nextval();
end if;
end;
end;
The complexity of this ended up quite out of the scope of my knowledge.
At the moment it is giving an error on drop sequence "cod" || table_name (Unexpected DROP symbol found) but I'm sure I have made other errors.
Can someone help me figure this logic out?
You can't put DDL statements (like drop or create or alter) directly inside a PL/SQL block. If you want to do DDL inside PL/SQL, you can do an execute immediate:
declare
begin
drop sequence X; -- error
execute immediate 'drop sequence X'; -- works fine
end;
/