यदि यह मौजूद नहीं है तो PostgreSQL ROLE (उपयोगकर्ता) बनाएँ


122

मैं PostgreSQL 9.1 में एक ROLE बनाने के लिए SQL स्क्रिप्ट कैसे लिखूं, लेकिन अगर यह पहले से मौजूद है तो कोई त्रुटि उठाए बिना?

वर्तमान स्क्रिप्ट में बस है:

CREATE ROLE my_user LOGIN PASSWORD 'my_password';

यह विफल रहता है यदि उपयोगकर्ता पहले से मौजूद है। मुझे कुछ पसंद है:

IF NOT EXISTS (SELECT * FROM pg_user WHERE username = 'my_user')
BEGIN
    CREATE ROLE my_user LOGIN PASSWORD 'my_password';
END;

... लेकिन यह काम नहीं करता है - IFसादे एसक्यूएल में समर्थित नहीं लगता है।

मेरे पास एक बैच फ़ाइल है जो एक PostgreSQL 9.1 डेटाबेस, भूमिका और कुछ अन्य चीजें बनाती है। इसे psql.exe कहते हैं, जिसे चलाने के लिए SQL स्क्रिप्ट के नाम से जाना जाता है। अब तक ये सभी स्क्रिप्ट सादा एसक्यूएल हैं और मैं पीएल / पीजीसीक्यूएल से बचना चाहूंगा और यदि संभव हो तो।

जवाबों:


156

मन में आपके लिए एक समान फैशन सरल करें:

DO
$do$
BEGIN
   IF NOT EXISTS (
      SELECT FROM pg_catalog.pg_roles  -- SELECT list can be empty for this
      WHERE  rolname = 'my_user') THEN

      CREATE ROLE my_user LOGIN PASSWORD 'my_password';
   END IF;
END
$do$;

( @A_horse_with_no_name के उत्तर पर बिल्डिंग और @ ग्रेगरी की टिप्पणी के साथ सुधार हुआ है ।)

इसके विपरीत, उदाहरण के लिए, CREATE TABLEकोई IF NOT EXISTSखंड नहीं है CREATE ROLE(कम से कम पेज 12 तक)। और आप सादे SQL में गतिशील DDL कथनों को निष्पादित नहीं कर सकते

"PL / pgSQL से बचने" के लिए आपका अनुरोध किसी अन्य PL का उपयोग करने के अलावा असंभव है। DOबयान का उपयोग करता है डिफ़ॉल्ट प्रक्रियात्मक भाषा के रूप में plpgsql। वाक्यविन्यास स्पष्ट घोषणा को छोड़ देता है:

DO [ LANGUAGE lang_name ] code
... प्रक्रियात्मक भाषा का नाम कोड में लिखा गया है। यदि छोड़ा गया है, तो डिफ़ॉल्ट है ।
lang_name
plpgsql


1
@Alberto: pg_user और pg_roles दोनों सही हैं। अभी भी वर्तमान संस्करण 9.3 में मामला है और यह जल्द ही किसी भी समय बदलने वाला नहीं है।
इरविन ब्रान्डेसटेटर

2
@Ken: यदि $आपके क्लाइंट में कोई विशेष अर्थ है तो आपको अपने क्लाइंट के सिंटैक्स नियमों के अनुसार इसे बचाना होगा । भागने की कोशिश करो $के साथ \$लिनक्स खोल में। या एक नया प्रश्न शुरू करें - टिप्पणियां जगह नहीं हैं। आप संदर्भ के लिए इसे हमेशा लिंक कर सकते हैं।
एरविन ब्रान्डेसटेटर

1
मैं 9.6 का उपयोग कर रहा हूं, और यदि कोई उपयोगकर्ता NOLOGIN के साथ बनाया गया था, तो वे pg_user तालिका में नहीं दिखाते हैं, लेकिन pg_roles तालिका में दिखाते हैं। क्या pg_roles यहां एक बेहतर समाधान होगा?
जेस

2
@ErwinBrandstetter यह उन भूमिकाओं के लिए काम नहीं करता है जिनमें NOLOGIN है। वे pg_roles में दिखाई देते हैं लेकिन pg_user में नहीं।
ग्रेगरी एरेन्सियस

2
यह समाधान एक दौड़-हालत से ग्रस्त है। इस उत्तर में एक सुरक्षित संस्करण प्रलेखित है
ब्लूब

60

स्वीकृत उत्तर एक दौड़ की स्थिति से ग्रस्त है यदि दो ऐसी लिपियों को समान रूप से एक ही पोस्टग्रेज क्लस्टर (DB सर्वर) पर निष्पादित किया जाता है, जैसा कि निरंतर-एकीकरण वातावरण में आम है

आम तौर पर भूमिका बनाने और इसे बनाते समय समस्याओं से इज्जत से निपटने का प्रयास करना अधिक सुरक्षित होता है:

DO $$
BEGIN
  CREATE ROLE my_role WITH NOLOGIN;
  EXCEPTION WHEN DUPLICATE_OBJECT THEN
  RAISE NOTICE 'not creating role my_role -- it already exists';
END
$$;

2
मुझे यह पसंद है कि यह मौजूद नहीं है।
मत्ती बारोन

2
DUPLICATE_OBJECTइस मामले में सटीक स्थिति है, अगर आप सभी शर्तों के बारे में नहीं पकड़ना चाहते हैं OTHERS
दनक दुवैल

43

या यदि भूमिका किसी भी db वस्तुओं के मालिक नहीं है जिसका कोई उपयोग कर सकता है:

DROP ROLE IF EXISTS my_user;
CREATE ROLE my_user LOGIN PASSWORD 'my_password';

लेकिन केवल अगर इस उपयोगकर्ता को छोड़ने से कोई नुकसान नहीं होगा।


10

बैश विकल्प ( बैश स्क्रिप्टिंग के लिए ):

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';"

(सवाल का जवाब नहीं है! यह केवल उन लोगों के लिए है जो उपयोगी हो सकते हैं)


3
इसे FROM pg_roles WHERE rolnameFROM pg_user WHERE usename
बर्थ

8

यहाँ 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;

उपयोग:

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)

8

पैटर्न का उपयोग करने के लिए सुझाए गए कुछ उत्तर: जांचें कि क्या भूमिका मौजूद नहीं है और यदि नहीं तो CREATE ROLEकमांड जारी करें । इसका एक नुकसान है: दौड़ की स्थिति। यदि कोई अन्य व्यक्ति चेक और जारी करने वाले CREATE ROLEकमांड के बीच एक नई भूमिका बनाता है, तो CREATE ROLEस्पष्ट रूप से घातक त्रुटि के साथ विफल हो जाता है।

उपरोक्त समस्या को हल करने के लिए, पहले से उपयोग किए गए PL/pgSQL, CREATE ROLEबिना किसी शर्त के , और फिर उस कॉल के अपवादों को पकड़ने के लिए पहले से वर्णित अन्य उत्तरों का उपयोग किया गया है। इन समाधानों के साथ सिर्फ एक समस्या है। वे चुपचाप किसी भी त्रुटि को छोड़ देते हैं, जिसमें वे इस तथ्य से उत्पन्न नहीं होते हैं कि भूमिका पहले से मौजूद है। CREATE ROLEअन्य त्रुटियों को भी फेंक सकते हैं और सिमुलेशन IF NOT EXISTSको केवल त्रुटि को चुप करना चाहिए जब भूमिका पहले से मौजूद है।

CREATE ROLEduplicate_objectभूमिका पहले से मौजूद होने पर त्रुटि फेंकें । और अपवाद हैंडलर को केवल इस एक त्रुटि को पकड़ना चाहिए। जैसा कि अन्य उत्तरों ने उल्लेख किया है कि घातक त्रुटि को सरल नोटिस में बदलना एक अच्छा विचार है। अन्य PostgreSQL IF NOT EXISTSकमांड , skippingउनके संदेश में जोड़ता है , इसलिए स्थिरता के लिए मैं इसे यहां भी जोड़ रहा हूं।

CREATE ROLE IF NOT EXISTSसही अपवाद और sqlstate प्रसार के अनुकरण के लिए यहां पूर्ण SQL कोड है :

DO $$
BEGIN
CREATE ROLE test;
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;

टेस्ट आउटपुट (दो बार के माध्यम से और फिर सीधे कहा जाता है):

$ 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

2
धन्यवाद। कोई दौड़ की स्थिति, तंग अपवाद को पकड़ने, अपने स्वयं के पुनर्लेखन के बजाय पोस्टग्रेज के अपने संदेश को लपेटना।
स्टेफानो टासचिनी

1
वास्तव में! यह वर्तमान में यहां एकमात्र सही उत्तर है, जो दौड़ की स्थिति से ग्रस्त नहीं है, और आवश्यक चयनात्मक त्रुटि से निपटने का उपयोग करता है। यह वास्तव में अफ़सोस की बात है कि यह उत्तर (पूरी तरह से सही नहीं) के बाद दिखाई दिया शीर्ष उत्तर ने अधिक 100 अंक एकत्र किए।
वोग

1
आपका स्वागत है! मेरा समाधान SQLSTATE को भी प्रचारित करता है इसलिए यदि आप अन्य PL / SQL स्क्रिप्ट या अन्य भाषा में SQL कनेक्टर के साथ कॉल कर रहे हैं तो आपको सही SQLSTATE प्राप्त होगा।
पाली

6

जैसा कि आप 9.x पर हैं, आप उसे DO स्टेटमेंट में लपेट सकते हैं:

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$
;

का चयन करें 'का चयन करें संख्या (*) num_users में से pg_roles से जहां rolname =' data_rw '; `अन्यथा यह काम नहीं करेगा
Miro

6

मेरी टीम एक सर्वर पर कई डेटाबेस के साथ एक स्थिति मार रही थी, जिसके आधार पर आप जिस डेटाबेस से जुड़े थे, उस प्रश्न में ROLE द्वारा वापस नहीं किया गया था SELECT * FROM pg_catalog.pg_user, जैसा कि @ erwin-brandstetter और @a_horse_with_no_name द्वारा प्रस्तावित किया गया था । सशर्त ब्लॉक निष्पादित किया गया, और हमने मारा role "my_user" already exists

दुर्भाग्य से हम सटीक परिस्थितियों के बारे में सुनिश्चित नहीं हैं, लेकिन यह समाधान समस्या के आसपास काम करता है:

        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$

इसे शायद अन्य अपवादों को खारिज करने के लिए और अधिक विशिष्ट बनाया जा सकता है।


3
Pg_user तालिका में केवल वही भूमिकाएँ शामिल हैं, जिनमें LOGIN है। यदि किसी भूमिका में NOLOGIN है, तो यह pg_user में नहीं दिखाई देता है, कम से कम PostgreSQL 10 में
ग्रेगरी एरेनीस

2

आप इसे अपने बैच फ़ाइल में कर सकते हैं:

SELECT * FROM pg_user WHERE usename = 'my_user'

और फिर psql.exeएक बार चल रहा है अगर भूमिका मौजूद नहीं है।


2
"उपयोगकर्ता नाम" कॉलम मौजूद नहीं है। यह "usename" होना चाहिए।
मौहम्मद सोइदाने

3
"usename" वह है जो अस्तित्व में नहीं है। :)
गेरन डे 8'13

1
कृपया pg_user देखें दस्तावेज़ देखें। संस्करण 7.4-9.6 में कोई "उपयोगकर्ता नाम" कॉलम नहीं है, "usename" सही है।
शेव

1

PostgreSQL के लिए कोई समस्या नहीं है, तो सिमुलेट निर्माण के लिए समान समाधान ?काम करना चाहिए - एक भेजने CREATE USER …के लिए \gexec

Psql के भीतर से समाधान

SELECT 'CREATE USER my_user'
WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec

शेल से वर्कअराउंड

echo "SELECT 'CREATE USER my_user' WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'my_user')\gexec" | psql

देखें वहाँ स्वीकार किए जाते हैं जवाब अधिक जानकारी के लिए।


आपका समाधान अभी भी एक दौड़ शर्त है जो मैं अपने जवाब में बताया गया है stackoverflow.com/a/55954480/7878845 भूमिका "my_user" पहले से मौजूद: यदि आप समानांतर अधिक समय में अपने खोल स्क्रिप्ट चलाते हैं तो आप त्रुटि मिलती है
पाली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.