I have to merge path directory and actual time in psql command line.
This absolute path works well:
\copy (select row_to_json(row(column1,column5)) from testdatabase.test) TO 'C:\Users\path\file.json';
But when I want merge two string like this:
\copy (select row_to_json(row(column1,column5)) from testdatabase.test) TO 'C:\Users\path\' || to_char(now(), 'YYYY_MM_DD');
I get Permission denied. This is permission issue i think because first request works well on same path. I tried also CONCATE statement but it fails.
If using COPY directly is an option, you could dynamically build the command, including your custom path logic and EXECUTE it. For example in an anonymous DO block:
DO
$$
BEGIN
EXECUTE 'COPY (SELECT row_to_json(...) ...) TO ''C:\...\' || to_char(now(), 'YYYY_MM_DD') || '.json''';
END;
$$
LANGUAGE plpgsql;
While executing scripts in SQLPlus I've encountered a problem:
script.sql contains the following lines
#some_pkg.pks
#some_pkg.pkb
drop package some_pkg;
/
After calling
> sqlplus user/password#dbname #script.sql
the following messages are in console:
Package created.
Package body created.
Package dropped.
drop package some_pkg;
*
ERROR at line 1:
ORA-04043: object SOME_PKG does not exist
Please, explain what's happening here. Looks like the package is being dropped twice. Is it possible to avoid the error?
The rules of SQLplus command execution basically are:
Execute the current text when you encounter a semi-colon. Thus if a line doesn't end with a semi-colon, the current text continues to be collected.
If you encounter DECLARE or BEGIN, collect all the text and do not execute on semi-colons
If you encounter a slash (/), execute the collected text.
So what happens in your cases is, that both the semi-colon and the slash execute the DROP statements.
To fix it, remove the slash.
You only need the slash if you have a block of PL/SQL, which always with an END statement. Use semicolons for everything else.
Note: the above rules are simplified. It's more complex in practice.
Some examples will help to understand rules:
Below code will be executed once
begin
dbms_output.put_line('executed');
end;
/
Below code will not execute (missing semicolon)
begin
dbms_output.put_line('executed')
end
/
Below code will be executed twice
begin
dbms_output.put_line('executed');
end;
/
/
Below code will be executed once
select 1 from dual
/
Below code will be executed once
select 1 from dual;
Below code will be executed twice
select 1 from dual;
/
I have created a Stored Procedure in Oracle (Via TOAD).
I need to deliver this procedure to some other developers. All is need to do is Wrap the procedure so that at basic level he/she should not be able to view the code.
At the same time, the developer should be able to create the procedure and execute/test it.
my procedure is saved in say FileName.sql whose content are like:
Create or Replace Procedure
IS
Declaration
Begin
Code
End;
Now i want this FileName.sql to be wrapped. So that the encrypted file can be send to other to check in different environment.
Please help me in how to wrap, and then how the other guy will be able to create the procedure, and execute the same.
Thanks in advance.
The Oracle WRAP utility does exactly that - allows you to encode a stored procedure in a form suitable for shipping.
The wrapped code is as portable as source code, but cannot be examined as plain text.
You will need access to the command line using a suitable system user (i.e. one that has access to the Oracle binary commands e.g. sqlplus etc.) and the basic syntax is:
wrap iname=input_file [ oname=output_file ]
If you do not specify any extensions, the default for input is .sql and output is .plb
Once you have generated a .plb file, you can execute it against the database to create your stored procedure.
See:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/wrap.htm#LNPLS016
http://docs.oracle.com/cd/B10500_01/appdev.920/a96624/c_wrap.htm
WARNING
WRAPencoded procedures are not completely secure - it is possible to "unwrap" them.
A better way of using WRAP is to put the procedure(s) you wish to protect in a package and WRAP the package definition.
Let's say your procedure is:
CREATE OR REPLACE PROCEDURE PR_OUTPUT_TEXT(
P_TYPE IN CHAR,
P_TEXT IN VARCHAR2
) IS
V_TYPE CHAR(3) := UPPER(P_TYPE);
BEGIN
IF V_TYPE = 'LOG' THEN
APPS.FND_FILE.PUT_LINE(APPS.FND_FILE.LOG, TO_CHAR(SYSDATE,'HH24:MI:SS')||' - '||P_TEXT);
ELSE
APPS.FND_FILE.PUT_LINE(APPS.FND_FILE.OUTPUT, P_TEXT);
END IF;
END PR_OUTPUT_TEXT;
you would normally call it using:
EXECUTE PR_OUTPUT_TEXT('LOG', 'Kittehz!!!')
In a package you would define the package body and the procedure thus:
CREATE OR REPLACE PACKAGE BODY USER.MYPACKAGE AS
PROCEDURE PR_OUTPUT_TEXT(
P_TYPE IN CHAR,
P_TEXT IN VARCHAR2
) IS
BEGIN
IF V_TYPE = 'LOG' THEN
APPS.FND_FILE.PUT_LINE(APPS.FND_FILE.LOG, TO_CHAR(SYSDATE,'HH24:MI:SS')||' - '||P_TEXT);
ELSE
APPS.FND_FILE.PUT_LINE(APPS.FND_FILE.OUTPUT, P_TEXT);
END IF;
END PR_OUTPUT_TEXT;
END MYPACKAGE;
and you would call the package using:
EXECUTE USER.MYPACKAGE.PR_OUTPUT_TEXT('LOG', 'ERMAHGERD KERTERNS!!!')
From documentation, to be precise, this is what you need to do :
Running the wrap Utility
For example, assume that the wrap_test.sql file contains the following:
CREATE PROCEDURE wraptest IS
TYPE emp_tab IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
all_emps emp_tab;
BEGIN
SELECT * BULK COLLECT INTO all_emps FROM employees;
FOR i IN 1..10 LOOP
DBMS_OUTPUT.PUT_LINE('Emp Id: ' || all_emps(i).employee_id);
END LOOP;
END;
/
To wrap the file, run the following from the operating system prompt:
wrap iname=wrap_test.sql
The output of the wrap utility is similar to the following:
PL/SQL Wrapper: Release 10.2.0.0.0 on Tue Apr 26 16:47:39 2005
Copyright (c) 1993, 2005, Oracle. All rights reserved.
Processing wrap_test.sql to wrap_test.plb
If you view the contents of the wrap_test.plb text file, the first line is CREATE PROCEDURE wraptest wrapped and the rest of the file contents is hidden.
You can run wrap_test.plb in SQL*Plus to execute the SQL statements in the file:
SQL> #wrap_test.plb
After the wrap_test.plb is run, you can execute the procedure that was created:
SQL> CALL wraptest();
More information in docs
i have a tricky SQL Problem:
We have a huge SQL Script which installs our application on the DB server.
We want to skip the database installation if the latest update didn't change anything in the DB.
I have implemented following check, which is executed before the other SQL commands:
check_current_version_delta.sql:
DECLARE
v_deploy_version VARCHAR2(30) := '&db_deploy_version';
v_check BOOLEAN := FALSE;
BEGIN
DBMS_OUTPUT.PUT_LINE( '--------------------------------------------------------------------------------');
DBMS_OUTPUT.PUT_LINE( 'Check if we have a new DB version');
DBMS_OUTPUT.PUT_LINE( '--------------------------------------------------------------------------------');
FOR cu_version IN (SELECT version FROM &DB_CURRENT_USER..db_deployment WHERE version = v_deploy_version AND ROWNUM = 1) LOOP
v_check := TRUE;
END LOOP;
IF v_check THEN
RAISE_APPLICATION_ERROR( -20001, 'DB Version: '||v_deploy_version||' is already installed');
END IF;
END;
/
This is working very well but our installation team complains about the ORA-XXXXX Error in the log, because they have automated error checks this installation is marked as FAIL (though there was no actual error)
So now the actual problem:
I need to cancel the execution of the SQL without any errors in the LOG. Is this even possible?
Alternative would be to make the rest of the installation dependent on the outcome of the script above. But i'm not sure how to accomplish that.
Have you some suggestions on how to handle it the good way?
One possible way is to create three scripts: The first checks for the condition and either calls the second or the third. The second does the real job. The third is an empty dummy, to avoid the error message cause by calling a non-existent script
In code, it looks like this:
col SCRIPT1_COL new_val SCRIPT1
SELECT case
when version = '&db_deploy_version' then 'dummy.sql'
else 'upgrade_db.sql'
end as SCRIPT1_COL
FROM &DB_CURRENT_USER..db_deployment;
#&SCRIPT1
Alternatively, you could use the method shown above to load either a script "dummy.sql" that does nothing or a script "exit.sql" that just contains the exit command, and execute it before doing the real job.
Presumably you're already using whenever sqlerror so make it terminate when you raise that exception, and you're redirecting the output to your log file. If so you can just hide the text of the exception with set termout off:
whenever sqlerror exit success
set termout off
DECLARE
...
BEGIN
...
IF v_check THEN
RAISE_APPLICATION_ERROR( -20001,
'DB Version: '||v_deploy_version||' is already installed');
END IF;
END;
/
set termout on
whenever sqlerror exit failure
... the rest of your script
The script will stop if the exception is raised but produce no output. The success means anything that runs this won't decide it has errored independently of the log; the exit code from sqlplus will be zero.
You may be spooling to output instead; in which case just don't start the spool until after your check. Or if you have things before this that you do have to spool, turn the spool off and then on again afterwards with append.
How do I write an SQL script to create a ROLE in PostgreSQL 9.1, but without raising an error if it already exists?
The current script simply has:
CREATE ROLE my_user LOGIN PASSWORD 'my_password';
This fails if the user already exists. I'd like something like:
IF NOT EXISTS (SELECT * FROM pg_user WHERE username = 'my_user')
BEGIN
CREATE ROLE my_user LOGIN PASSWORD 'my_password';
END;
... but that doesn't work - IF doesn't seem to be supported in plain SQL.
I have a batch file that creates a PostgreSQL 9.1 database, role and a few other things. It calls psql.exe, passing in the name of an SQL script to run. So far all these scripts are plain SQL and I'd like to avoid PL/pgSQL and such, if possible.
Simple script (question asked)
Building on #a_horse_with_no_name's answer and improved with #Gregory's comment:
DO
$do$
BEGIN
IF EXISTS (
SELECT FROM pg_catalog.pg_roles
WHERE rolname = 'my_user') THEN
RAISE NOTICE 'Role "my_user" already exists. Skipping.';
ELSE
CREATE ROLE my_user LOGIN PASSWORD 'my_password';
END IF;
END
$do$;
Unlike, for instance, with CREATE TABLE there is no IF NOT EXISTS clause for CREATE ROLE (up to at least Postgres 14). And you cannot execute dynamic DDL statements in plain SQL.
Your request to "avoid PL/pgSQL" is impossible except by using another PL. The DO statement uses PL/pgSQL as default procedural language:
DO [ LANGUAGE lang_name ] code
...
lang_name
The name of the procedural language the code is written in. If
omitted, the default is plpgsql.
No race condition
The above simple solution allows for a race condition in the tiny time frame between looking up the role and creating it. If a concurrent transaction creates the role in between we get an exception after all. In most workloads, that will never happen as creating roles is a rare operation carried out by an admin. But there are highly contentious workloads like #blubb mentioned.
#Pali added a solution trapping the exception. But a code block with an EXCEPTION clause is expensive. The manual:
A block containing an EXCEPTION clause is significantly more
expensive to enter and exit than a block without one. Therefore, don't
use EXCEPTION without need.
Actually raising an exception (and then trapping it) is comparatively expensive on top of it. All of this only matters for workloads that execute it a lot - which happens to be the primary target audience. To optimize:
DO
$do$
BEGIN
IF EXISTS (
SELECT FROM pg_catalog.pg_roles
WHERE rolname = 'my_user') THEN
RAISE NOTICE 'Role "my_user" already exists. Skipping.';
ELSE
BEGIN -- nested block
CREATE ROLE my_user LOGIN PASSWORD 'my_password';
EXCEPTION
WHEN duplicate_object THEN
RAISE NOTICE 'Role "my_user" was just created by a concurrent transaction. Skipping.';
END;
END IF;
END
$do$;
Much cheaper:
If the role already exists, we never enter the expensive code block.
If we enter the expensive code block, the role only ever exists if the unlikely race condition hits. So we hardly ever actually raise an exception (and catch it).
Some answers suggested to use pattern: check if role does not exist and if not then issue CREATE ROLE command. This has one disadvantage: race condition. If somebody else creates a new role between check and issuing CREATE ROLE command then CREATE ROLE obviously fails with fatal error.
To solve above problem, more other answers already mentioned usage of PL/pgSQL, issuing CREATE ROLE unconditionally and then catching exceptions from that call. There is just one problem with these solutions. They silently drop any errors, including those which are not generated by fact that role already exists. CREATE ROLE can throw also other errors and simulation IF NOT EXISTS should silence only error when role already exists.
CREATE ROLE throw duplicate_object error when role already exists. And exception handler should catch only this one error. As other answers mentioned it is a good idea to convert fatal error to simple notice. Other PostgreSQL IF NOT EXISTS commands adds , skipping into their message, so for consistency I'm adding it here too.
Here is full SQL code for simulation of CREATE ROLE IF NOT EXISTS with correct exception and sqlstate propagation:
DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
Test output (called two times via DO and then directly):
$ sudo -u postgres psql
psql (9.6.12)
Type "help" for help.
postgres=# \set ON_ERROR_STOP on
postgres=# \set VERBOSITY verbose
postgres=#
postgres=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=#
postgres=# DO $$
postgres$# BEGIN
postgres$# CREATE ROLE test;
postgres$# EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE: 42710: role "test" already exists, skipping
LOCATION: exec_stmt_raise, pl_exec.c:3165
DO
postgres=#
postgres=# CREATE ROLE test;
ERROR: 42710: role "test" already exists
LOCATION: CreateRole, user.c:337
Or if the role is not the owner of any db objects one can use:
DROP ROLE IF EXISTS my_user;
CREATE ROLE my_user LOGIN PASSWORD 'my_password';
But only if dropping this user will not make any harm.
Bash alternative (for Bash scripting):
psql -h localhost -U postgres -tc \
"SELECT 1 FROM pg_user WHERE usename = 'my_user'" \
| grep -q 1 \
|| psql -h localhost -U postgres \
-c "CREATE ROLE my_user LOGIN PASSWORD 'my_password';"
(isn't the answer for the question! it is only for those who may be useful)
Here is a generic solution using plpgsql:
CREATE OR REPLACE FUNCTION create_role_if_not_exists(rolename NAME) RETURNS TEXT AS
$$
BEGIN
IF NOT EXISTS (SELECT * FROM pg_roles WHERE rolname = rolename) THEN
EXECUTE format('CREATE ROLE %I', rolename);
RETURN 'CREATE ROLE';
ELSE
RETURN format('ROLE ''%I'' ALREADY EXISTS', rolename);
END IF;
END;
$$
LANGUAGE plpgsql;
Usage:
posgres=# SELECT create_role_if_not_exists('ri');
create_role_if_not_exists
---------------------------
CREATE ROLE
(1 row)
posgres=# SELECT create_role_if_not_exists('ri');
create_role_if_not_exists
---------------------------
ROLE 'ri' ALREADY EXISTS
(1 row)
The same solution as for Simulate CREATE DATABASE IF NOT EXISTS for PostgreSQL? should work - send a CREATE USER … to \gexec.
Workaround from within psql
SELECT 'CREATE USER my_user'
WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec
Workaround from the shell
echo "SELECT 'CREATE USER my_user' WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec" | psql
See accepted answer there for more details.
Building off of the other answers here, I wanted the ability to execute psql once against a .sql file to have it perform a set of initialization operations. I also wanted the ability to inject the password at the time of execution to support CI/CD scenarios.
-- init.sql
CREATE OR REPLACE FUNCTION pg_temp.create_myuser(theUsername text, thePassword text)
RETURNS void AS
$BODY$
DECLARE
duplicate_object_message text;
BEGIN
BEGIN
EXECUTE format(
'CREATE USER %I WITH PASSWORD %L',
theUsername,
thePassword
);
EXCEPTION WHEN duplicate_object THEN
GET STACKED DIAGNOSTICS duplicate_object_message = MESSAGE_TEXT;
RAISE NOTICE '%, skipping', duplicate_object_message;
END;
END;
$BODY$
LANGUAGE 'plpgsql';
SELECT pg_temp.create_myuser(:'vUsername', :'vPassword');
Invoking with psql:
NEW_USERNAME="my_new_user"
NEW_PASSWORD="password with 'special' characters"
psql --no-psqlrc --single-transaction --pset=pager=off \
--tuples-only \
--set=ON_ERROR_STOP=1 \
--set=vUsername="$NEW_USERNAME" \
--set=vPassword="$NEW_PASSWORD" \
-f init.sql
This will allow init.sql to be run either locally or by the CI/CD pipeline.
Notes:
I did not find a way to reference a file variable (:vPassword) directly in a DO anonymous function, hence the full FUNCTION to pass the arg. (see #Clodoaldo Neto's answer)
#Erwin Brandstetter's answer explains why we must use an EXECUTE and cannot use CREATE USER directly.
#Pali's answer explains the need for the EXCEPTION to prevent race conditions (which is why the \gexec approach is not recommended).
The function must be invoked in a SELECT statement. Use the -t/--tuples-only attribute in the psql command to clean up log output, as pointed out in #villy393's answer.
The function is created in a temporary schema, so it will be deleted automatically.
Quoting is handled properly, so no special character in password can cause errors or worse, security vulnerability.
My team was hitting a situation with multiple databases on one server, depending on which database you connected to, the ROLE in question was not returned by SELECT * FROM pg_catalog.pg_user, as proposed by #erwin-brandstetter and #a_horse_with_no_name. The conditional block executed, and we hit role "my_user" already exists.
Unfortunately we aren't sure of exact conditions, but this solution works around the problem:
DO
$body$
BEGIN
CREATE ROLE my_user LOGIN PASSWORD 'my_password';
EXCEPTION WHEN others THEN
RAISE NOTICE 'my_user role exists, not re-creating';
END
$body$
It could probably be made more specific to rule out other exceptions.
As you are on 9.x, you can wrap that into a DO statement:
do
$body$
declare
num_users integer;
begin
SELECT count(*)
into num_users
FROM pg_user
WHERE usename = 'my_user';
IF num_users = 0 THEN
CREATE ROLE my_user LOGIN PASSWORD 'my_password';
END IF;
end
$body$
;
If you have access to a shell, you can do this.
psql -tc "SELECT 1 FROM pg_user WHERE usename = 'some_use'" | grep -q 1 || psql -c "CREATE USER some_user"
For those of you who would like an explanation:
-c = run command in database session, command is given in string
-t = skip header and footer
-q = silent mode for grep
|| = logical OR, if grep fails to find match run the subsequent command
You can do it in your batch file by parsing the output of:
SELECT * FROM pg_user WHERE usename = 'my_user'
and then running psql.exe once again if the role does not exist.
I needed this in a Makefile to not fail the job when the user exists:
initdb:
psql postgres -c "CREATE USER foo CREATEDB PASSWORD 'bar'" || true
...