Is there a command for connecting to a database within an SQL file?
I want to create a database and then immediately begin populating it with tables and data. All contained within a single .sql file. I'm not aware of anything that replaces the \c shell command?
Example SQL file:
CREATE DATABASE mydb;
CREATE USER myusername WITH PASSWORD 'mypassword';
GRANT ALL PRIVILEGES ON DATABASE mydb TO myusername;
CONNECT TO mydb USER myusername; --<-- something like this?
CREATE TABLE administrator (
id integer NOT NULL,
admin_name character varying(150),
password character varying(255),
active boolean,
"timestamp" timestamp without time zone,
email character varying(255)
);
ALTER TABLE administrator OWNER TO myusername;
CONNECT is the closest thing I've found in the documentation, but it's encountering syntax errors. I couldn't find a wrapper that specifies where the tables should go either?
ERROR: syntax error at or near "CONNECT"
Add the below statement instead of connect to db
\c mydb;
The reorder statements of execution should be
CREATE DATABASE mydb;
\c mydb;
CREATE USER myusername WITH PASSWORD 'mypassword';
GRANT ALL PRIVILEGES ON DATABASE mydb TO myusername;
CREATE TABLE administrator (
id integer NOT NULL,
admin_name character varying(150),
password character varying(255),
active boolean,
"timestamp" timestamp without time zone,
email character varying(255)
);
ALTER TABLE administrator OWNER TO myusername;
Related
Background
I'm working these days on organizing the Postgres database of a project in my work.
EDIT:
This database is connected to a NodeJS server that runs Postgraphile on it, in order to expose the GraphQL interface for the client. Therefore, I have to use RLS in order to forbid the user to query and manipulate rows that he/she doesn't have permission.
One of the tasks that I've got is to add a deleted field for each table, and using RLS to hide the records that deleted = true.
Code Example
To explain my problem, I'll add an SQL code for building a fake database:
Roles
For this example, I'll use these roles:
Superuser role named admin
Role called app_users
2 Users inherit from app_users:
bob
alice
CREATE ROLE admin WITH
LOGIN
SUPERUSER
INHERIT
CREATEDB
CREATEROLE
NOREPLICATION
ENCRYPTED PASSWORD 'md5f6fdffe48c908deb0f4c3bd36c032e72'; -- password: admin
GRANT username TO admin;
CREATE ROLE app_users WITH
NOLOGIN
NOSUPERUSER
NOINHERIT
NOCREATEDB
CREATEROLE
NOREPLICATION;
CREATE ROLE bob WITH
LOGIN
NOSUPERUSER
INHERIT
NOCREATEDB
NOCREATEROLE
NOREPLICATION
ENCRYPTED PASSWORD 'md5e8557d12f6551b2ddd26bbdd0395465c';
GRANT app_users TO bob;
CREATE ROLE alice WITH
LOGIN
NOSUPERUSER
INHERIT
NOCREATEDB
NOCREATEROLE
NOREPLICATION
ENCRYPTED PASSWORD 'md5579e43b423b454623383471aeb85cd87';
GRANT app_users TO alice;
Database
This example will hold a database named league for a mock database for an American football league.
CREATE DATABASE league
WITH
OWNER = admin
ENCODING = 'UTF8'
LC_COLLATE = 'en_US.utf8'
LC_CTYPE = 'en_US.utf8'
TABLESPACE = pg_default
CONNECTION LIMIT = -1;
GRANT CREATE, CONNECT ON DATABASE league TO admin;
GRANT TEMPORARY ON DATABASE league TO admin WITH GRANT OPTION;
GRANT TEMPORARY, CONNECT ON DATABASE league TO PUBLIC;
Scheme: public
I've added some minor changes in the scheme, so in default, role app_users allow any command, type, execute function, etcetera.
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT ALL ON TABLES TO app_users;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT ALL ON SEQUENCES TO app_users;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT EXECUTE ON FUNCTIONS TO app_users;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE ON TYPES TO app_users;
Creating Tables
Table: TEAMS
CREATE TABLE IF NOT EXISTS public."TEAMS"
(
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
deleted boolean NOT NULL DEFAULT false,
name text COLLATE pg_catalog."default" NOT NULL,
owner text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT "TEAMS_pkey" PRIMARY KEY (id)
)
TABLESPACE pg_default;
ALTER TABLE public."TEAMS"
OWNER to admin;
ALTER TABLE public."TEAMS"
ENABLE ROW LEVEL SECURITY;
GRANT ALL ON TABLE public."TEAMS" TO admin;
GRANT ALL ON TABLE public."TEAMS" TO app_users;
CREATE POLICY teams_deleted
ON public."TEAMS"
AS RESTRICTIVE
FOR SELECT
TO app_users
USING (deleted = false);
CREATE POLICY teams_owner
ON public."TEAMS"
AS PERMISSIVE
FOR ALL
TO app_users
USING (owner = CURRENT_USER);
Table: PLAYERS
CREATE TABLE IF NOT EXISTS public."PLAYERS"
(
id text COLLATE pg_catalog."default" NOT NULL,
deleted boolean NOT NULL DEFAULT false,
first_name text COLLATE pg_catalog."default" NOT NULL,
last_name text COLLATE pg_catalog."default" NOT NULL,
team_id integer NOT NULL,
jersey_number integer NOT NULL,
CONSTRAINT "PLAYERS_pkey" PRIMARY KEY (id),
CONSTRAINT fkey_team_id FOREIGN KEY (team_id)
REFERENCES public."TEAMS" (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT check_player_number CHECK (jersey_number > 0 AND jersey_number < 100)
)
TABLESPACE pg_default;
ALTER TABLE public."PLAYERS"
OWNER to admin;
ALTER TABLE public."PLAYERS"
ENABLE ROW LEVEL SECURITY;
GRANT ALL ON TABLE public."PLAYERS" TO admin;
GRANT ALL ON TABLE public."PLAYERS" TO app_users;
CREATE POLICY players_deleted
ON public."PLAYERS"
AS RESTRICTIVE
FOR SELECT
TO app_users
USING (deleted = false);
CREATE POLICY players_owner
ON public."PLAYERS"
AS PERMISSIVE
FOR ALL
TO app_users
USING ((( SELECT "TEAMS".owner
FROM "TEAMS"
WHERE ("TEAMS".id = "PLAYERS".team_id)) = CURRENT_USER));
Test Case (Edited for better understanding)
Run this code using user bob:
INSERT INTO "TEAMS" (name, owner)
VALUES ('Jerusalem Lions', 'bob')
RETURNING id; -- We'll save this id for the next command
INSERT INTO "PLAYERS" (id, first_name, last_name, jersey_number, role, team_id)
VALUES ('99999', 'Eric', 'Cohen', 29, 'linebacker', 888) -- Replace 888 with the returned id from the previous command
RETURNING *;
-- These commands will work
SELECT * FROM "PLAYERS";
UPDATE "PLAYERS"
SET last_name = 'Levi'
WHERE id = '99999'
RETURNING *;
-- This is the command that won't work. I can't change the deleted.
UPDATE "PLAYERS"
SET deleted = true
WHERE id = '99999'
RETURNING *;
EDIT: Now, it's important to understand that The policies as defined above works when I do any query, as long as:
INSERT INTO doesn't include deleted = true (that's ok).
UPDATE that includes SET deleted = true. (This is the main issue).
I want to:
Allow bob to delete a record using deleted = true on an UPDATE command.
Hide in SELECT commands all records that deleted = true.
What should I do? 🤷♂️
From the documentation:
When a mix of permissive and restrictive policies are present, a record is only accessible if at least one of the permissive policies passes, in addition to all the restrictive policies.
It means that you cannot permit in one policy something restricted in another policy. For this reason, restrictive policies should be used with extreme caution. When there is no policy defined, everything is restricted, so you should focus on permitting what should be allowed.
Simplified example:
create table my_table(
id int primary key,
user_name text,
deleted bool);
alter table my_table enable row level security;
create policy rule_for_select
on my_table
as permissive
for select
to app_users
using (not deleted);
create policy rule_for_all
on my_table
as permissive
for all
to app_users
using (user_name = current_user and not deleted);
insert into my_table(id, user_name, deleted) values
(1, 'alice', false),
(2, 'bob', true),
(3, 'celine', true)
The user bob will see row 1. He would be able to update row 2 if deleted is false.
What I can gather is, that
UPDATE "PLAYERS" SET deleted=true
will not fail for "Bob". But:
UPDATE "PLAYERS" SET deleted=true RETURNING *
will. Postgraphile will usually "return" something when doing mutations. After updating the row - you basically forbid the select statement.
The usual hack (feature) in Postgraphile land is usually creating a custom mutation and define it as security definer.
Maybe there are other ways around this - but this is the hammer->nail approach in Postgraphile, in my experience.
Similar problem can be seen here: Unable to update row of table with Row Level Security where I proposed a workaround by implementing a grace period.
I have a table like:
CREATE TABLE public.pagos
(
condominio character varying(12) NOT NULL,
id_unidad character varying(10) NOT NULL,
fechapago date NOT NULL,
montopago integer NOT NULL
)
A user:
CREATE USER user1 WITH PASSWORD 'user1';
I give permissions to it user:
GRANT SELECT ON pagos TO user1;
How I can Revoke all permissions (in this case only select) of user1 in a specific field of the table (like the montopago field) ?
Thanks !
Permissions in SQL are additive, so you can never make exceptions of the kind "grant select on the whole table, but revoke it from one specific column". What you need to do is to revoke the permission you granted on the whole table and grant column permissions instead:
REVOKE SELECT ON pagos FROM user1;
GRANT SELECT (condominio, id_unidad, fechapago) ON pagos TO user1;
I'm learning DDL to create and define an SQL database with Postgresql 10.
I have the something like the following SQL code in an .sql file, and I want to input it in psql or PgAdmin 4, just to test the syntax and see the database structure:
CREATE DATABASE database;
CREATE TYPE t_name AS
( first VARCHAR(30),
last VARCHAR(60)
);
CREATE TABLE telephone_m
( tnumber VARCHAR(15) NOT NULL UNIQUE
);
CREATE TABLE people
( curp CHAR(18) NOT NULL PRIMARY KEY,
pname t_name NOT NULL,
birth_date DATE NOT NULL,
telephone_m VARCHAR(15) REFERENCES telephone_m
);
CREATE TABLE clients
( curp CHAR(18) NOT NULL PRIMARY KEY,
cid SERIAL NOT NULL REFERENCES cards,
clocation VARCHAR(29)
) INHERITS (people);
CREATE TABLE cards
( cid BIGSERIAL NOT NULL PRIMARY KEY,
curp CHAR(18) NOT NULL REFERENCES clients,
trips SMALLINT,
distance NUMERIC,
points NUMERIC
);
CREATE TABLE drivers
( curp CHAR(18) NOT NULL PRIMARY KEY,
rfc CHAR(22) NOT NULL UNIQUE,
adress t_adress NOT NULL
) INHERITS (people);
I've tried in PgAdmin 4 making right-click on a new database -> CREATE Script, it opens Query Editor, I copy paste my code and execute it, but it returns:
ERROR: CREATE DATABASE cannot be executed from a function or multi-command string
SQL state: 25001
I've also tried using Query Tool directly from the PgAdmin tools menu with the same results.
The database is created just fine. But if you want to create object in the new DB, you have to connect to it. In any client, including pgAdmin4.
And you cannot run CREATE DATABASE inside of a transaction. Must be committed on it's own. Executing multiple commands at once is automatically wrapped into a single transaction in pgAdmin.
You have to execute CREATE DATABASE mydb; on its own (for instance by selecting only that line and pressing F5 while being connected to any DB, even the maintenance db "postgres". Then click on "Databases" in the object browser in the pgadmin4 main window / tab, hit F5 to refresh the view, click on the new DB, open up a new query tool with the flash icon (in a new window / tab) and execute the rest of your script there.
psql scripts manage by using the meta-command \c to connect to the new db after creating it, within the same session.
Asides:
"database" is no good name for a database.
CREATE TYPE AS (...), but just CREATE TABLE (...). No AS.
And you typically don't want to use the data type CHAR(18). See:
Any downsides of using data type "text" for storing strings?
Get sum of integers for UNIQUE ids
What is the overhead for varchar(n)?
Should I add an arbitrary length limit to VARCHAR columns?
There is the ; missing after the CREATE DATABASE database (and perhaps give the db a better name).
This is my init.sql file :
DROP DATABASE IF EXISTS my_data;
CREATE DATABASE my_data;
DROP USER IF EXISTS u;
CREATE USER u WITH PASSWORD 'secret';
GRANT ALL ON ALL TABLES IN SCHEMA public TO u;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO u;
GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO u;
\c my_data;
CREATE TABLE users (
ID SERIAL PRIMARY KEY NOT NULL,
email VARCHAR NOT NULL DEFAULT '',
password VARCHAR NOT NULL DEFAULT '',
active SMALLINT NOT NULL DEFAULT '1',
created TIMESTAMP NOT NULL,
modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
salt VARCHAR NOT NULL DEFAULT ''
);
Then if I :
psql -f init.sql
And..
psql -d my_data -U u
my_data=> select * from users;
ERROR: permission denied for relation users
Why would this permission be denied if I just granted it?
More info
my_data=> \z users
Access privileges
Schema | Name | Type | Access privileges | Column access privileges
--------+-------+-------+-------------------+--------------------------
public | users | table | |
(1 row)
You only gave permission on the schema, which is separate from the tables. If you want to give permissions on the tables also you can use
GRANT ALL ON ALL TABLES IN SCHEMA public TO u;
Note that if you have sequences or other objects they also need separate permissions.
This has to be set after the tables have been created since permissions are set for existing objects.
If you want to set default permissions for new objects you can use
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO u;
I'm trying to create a table (based off of a .csv file I've been working with) within SQL, but I'm getting the following message:
SQL Error: ORA-01031: insufficient privileges
01031. 00000 - "insufficient privileges"
*Cause: An attempt was made to perform a database operation without the necessary privileges.
What do I need to change to create the table?
Here is the my code to create the table--then I have a bunch of INSERT INTO lines.
CREATE TABLE Water_Birth_Consent
(
NAME VARCHAR(24) NOT NULL
,MRN INTEGER NOT NULL
,DATE_SIGNED VARCHAR(10) NOT NULL
,EDD VARCHAR(10) NOT NULL
,DEL DATE
,WB VARCHAR(5)
,study_id VARCHAR(17)
,comments VARCHAR(42)
,EMPI INTEGER
);
Another user with sufficient privileges (DBA) needs to grant your user the create table privilege. This is the command they would run:
grant create table to <your-user>;
Once you are able to create the table, you won't need any additional special permissions to insert data into the table.