CTE के साथ SQL फ़ंक्शंस पर PostgreSQL 9.6 कॉलम छोड़ने और साइड-इफेक्ट्स


15

यदि मेरे पास 3 कॉलम वाली तालिका है - A, B और D कहें - और मुझे एक नया परिचय देना था - D की वर्तमान स्थिति को बदलने के लिए C कहें, तो मैं निम्नलिखित विधि का उपयोग करूंगा:

  1. C और D2 के रूप में 2 नए कॉलम पेश करें।
  2. D से D2 की सामग्री की प्रतिलिपि बनाएँ।
  3. डी हटाएं।
  4. D2 से D का नाम बदलें

नया आदेश ए, बी, सी और डी होगा।

मैंने सोचा कि यह (अभी तक) एक वैध अभ्यास था क्योंकि इसमें कोई समस्या नहीं थी।

हालाँकि, आज मुझे एक समस्या आई, जब एक ही टेबल पर एक स्टेटमेंट देने वाले एक फ़ंक्शन ने निम्नलिखित त्रुटि वापस की:

table row type and query-specified row type do not match

और निम्नलिखित विवरण:

Query provides a value for a dropped column at ordinal position 13

मैं PostgreSQL को पुन: प्रारंभ की कोशिश की, एक कर VACUUM FULLऔर अंत में हटाने और के रूप में सुझाव समारोह फिर से बनाने के लिए यहाँ और यहाँ (तथ्य यह है कि वे एक स्थिति है जहाँ एक प्रणाली तालिका बदल दिया गया है से निपटने की कोशिश को छोड़ कर), लेकिन इन समाधान नहीं किया काम करते हैं।

एक बहुत छोटे डेटाबेस के साथ काम करने की विलासिता के कारण मैंने इसे निर्यात किया, इसे हटा दिया और फिर इसे फिर से आयात किया और इसने मेरे फ़ंक्शन के साथ समस्या को ठीक किया


मैं इस तथ्य से अवगत था कि किसी को सिस्टम तालिकाओं को संशोधित करके स्तंभों के प्राकृतिक क्रम के साथ खिलवाड़ नहीं करना चाहिए (जैसे हाथों को गंदा करना pg_attribute, आदि) जैसा कि यहां देखा गया है:

क्या Postgres में स्तंभों के प्राकृतिक क्रम को बदलना संभव है?

मेरे कार्य द्वारा फेंकी गई त्रुटि को देखते हुए मुझे अब पता चला है कि मेरी विधि के साथ स्तंभों के क्रम को बदलना भी एक नहीं-नहीं है। क्या कोई इस बात पर प्रकाश डाल सकता है कि मैं जो कर रहा हूं वह भी गलत क्यों है?


पोस्टग्रेज का संस्करण 9.6.0 है।

यहाँ समारोह है:

CREATE OR REPLACE FUNCTION "public"."__post_users" ("facebookid" text, "useremail" text, "username" text) RETURNS TABLE (authentication_code text, id integer, key text, stripe_id text) AS '

-- First, select the user:
WITH select_user AS
(SELECT
users.id
FROM
users
WHERE
useremail = users.email),

-- Second, update the user (if user exists):
update_user AS
(UPDATE
users
SET
authentication_code = GEN_RANDOM_UUID(),
authentication_date = current_timestamp,
facebook_id = facebookid
WHERE EXISTS (SELECT * FROM select_user)
AND
useremail = users.email
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id),

-- Third, insert the user (if user does not exist):
insert_user AS
(INSERT INTO
users (authentication_code, authentication_date, email, key, name, facebook_id)
SELECT
GEN_RANDOM_UUID(),
current_timestamp,
useremail,
GEN_RANDOM_UUID(),
COALESCE(username, SUBSTRING(useremail FROM ''([^@]+)'')),
facebookid
WHERE NOT EXISTS (SELECT * FROM select_user)
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id)

-- Finally, select the authentication code, ID, key and Stripe ID:
SELECT
*
FROM
update_user
UNION ALL
SELECT
*
FROM
insert_user' LANGUAGE "sql" COST 100 ROWS 1
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER

मैंने दोनों कॉलम पर नाम बदलने / पुन: व्यवस्थित किया facebook_idऔर stripe_id(इससे पहले एक नया कॉलम जोड़ा गया था, जो नाम बदलने का कारण है, लेकिन इस क्वेरी द्वारा छुआ नहीं गया है)।

एक निश्चित क्रम में कॉलम होने से ऑर्डर के लिए विशुद्ध रूप से ब्याज से बाहर है। हालांकि, इस सवाल को पूछने का कारण यह चिंताजनक है कि किसी कॉलम का सरल नामकरण और डिलीट करना उत्पादन मोड में फ़ंक्शन का उपयोग करके किसी व्यक्ति के लिए वास्तविक मुद्दों को ट्रिगर कर सकता है (जैसा कि मेरे साथ हुआ था)।


एक नज़र डालें कि मैं PostgreSQL डेटाबेस तालिका में कॉलम की स्थिति कैसे बदल सकता हूं? स्टैक ओवरफ्लो पर जो मदद का हो सकता है, हालांकि वे ज्यादातर आपके कॉलम को फिर से व्यवस्थित करने का सुझाव नहीं देते हैं।
जोनलो

जवाबों:


16

9.6 और 9.6.1 को संभावित बग

यह पूरी तरह से मेरे लिए एक बग की तरह लग रहा है ...

मुझे नहीं पता कि ऐसा क्यों होता है, लेकिन मैं पुष्टि कर सकता हूं कि ऐसा होता है। यह सबसे सरल पाया गया सेटअप है जो समस्या को पुन: उत्पन्न करता है (संस्करण 9.6.0 और 9.6.1 में)।

CREATE TABLE users
(
    id SERIAL PRIMARY KEY,
    email TEXT NOT NULL,
    column_that_we_will_drop TEXT
) ;

-- Function that uses the previous table, and that has a CTE
CREATE OR REPLACE FUNCTION __post_users
    (_useremail text) 
RETURNS integer AS
$$
-- Need a CTE to produce the error. A 'constant' one suffices.
WITH something_even_if_useless(a) AS
(
    VALUES (1)
)
UPDATE
    users
SET
    id = id
WHERE 
    -- The CTE needs to be referenced, if the next
    -- condition were not in place, the problem is not reproduced
    EXISTS (SELECT * FROM something_even_if_useless)
    AND email = _useremail
RETURNING
    id
$$
LANGUAGE "sql" ;

इस सेटअप के बाद, अगला कथन केवल काम करता है

SELECT * FROM __post_users('a@b.com');

इस बिंदु पर, हम एक स्तंभ को छोड़ देते हैं:

ALTER TABLE users 
    DROP COLUMN column_that_we_will_drop ;

यह परिवर्तन त्रुटि उत्पन्न करने के लिए अगला विवरण देता है

SELECT * FROM __post_users('a@b.com');

जो @Andy द्वारा उल्लिखित है:

ERROR: table row type and query-specified row type do not match
SQL state: 42804
Detail: Query provides a value for a dropped column at ordinal position 3.
Context: SQL function "__post_users" statement 1
    SELECT * FROM __post_users('a@b.com');

गिराने और फ़ंक्शन को फिर से बनाने से समस्या हल नहीं होती है।
रिक्त स्थान (तालिका या संपूर्ण डेटाबेस) समस्या का समाधान नहीं करता है।


बग रिपोर्ट को उचित PostgreSQL मेलिंग सूची में पारित किया गया था और हमारे पास बहुत तेज़ प्रतिक्रिया थी :

मैं इसे हेड या 9.6 ब्रांच टिप में पुन: पेश नहीं कर सकता। मेरा मानना ​​है कि यह पहले से ही इस पैच द्वारा तय किया गया था, जो 9.6.1 के बाद थोड़ा बढ़ गया:

https://git.postgresql.org/gitweb/?p=postgresql.git&a=commitdiff&h=f4d865f22

लेकिन रिपोर्ट के लिए धन्यवाद!

सादर, टॉम लेन


संस्करण 9.6.2

2017-03-06 पर, मैं पुष्टि कर सकता हूं कि मैं 9.6.2 संस्करण पर इस व्यवहार को पुन: पेश नहीं कर सकता। यही है, इस रिलीज़ पर बग को ठीक किया गया है।

अपडेट करें

@ जना की टिप्पणी के अनुसार: "मैं पुष्टि कर सकता हूं कि बग 9.6.1 में मौजूद है और 9.6.2 में तय किया गया था। पोस्टग्रेज रिलीज वेबसाइट पर भी फिक्स को सूचीबद्ध किया गया है : फिक्स स्प्यूरियस" क्वेरी एक गिराए गए कॉलम के लिए एक मूल्य प्रदान करता है "त्रुटियों के दौरान एक स्तंभ पर एक तालिका के साथ सम्मिलित करें या अद्यतन करें "



4
मैं 9.6.0 का उपयोग कर रहा हूं और @joanolo सही है, मैं उसकी विधि के साथ बग को पुन: पेश कर सकता हूं। यदि टॉम इसे पुन: पेश नहीं कर सकता है, तो शायद यह विशिष्ट संस्करण के साथ एक अलग बग था (और 9.6.1 मुझे लगता है?)। के रूप में इस सवाल का जवाब में उल्लेख किया है, इस मुद्दे को प्रकट होता है जब छोड़ने के एक तालिका में किसी स्तंभ और CTE को प्रभावित करने के साथ एक समारोह का उपयोग कर कि मेज। इस प्रकार, समस्या केवल पुनरावृत्ति के साथ नहीं है और यही मैं अपने प्रश्न के साथ प्राप्त करने की कोशिश कर रहा था। मैं इसे दर्शाने के लिए शीर्षक संपादित करूंगा।
एंडी

मैं पुष्टि कर सकता हूं कि बग 9.6.1 में मौजूद है और 9.6.2 में तय किया गया था। फिक्स को पोस्टग्रेज रिलीज वेबसाइट पर भी सूचीबद्ध किया गया है : फिक्स स्प्यूरियस "क्वेरी एक गिराए गए कॉलम के लिए एक मान प्रदान करता है" INSERT या UPDATE के दौरान एक ड्रॉप किए गए कॉलम के साथ त्रुटियां
Jana

0

मुझे पता है कि आपने शायद यह पहले सुना है, लेकिन यह एक भयानक विचार है।

  • लॉजिकल ऑर्डरिंग केवल चीजों को प्रभावित करता है जैसे SELECT *
  • तार्किक आदेश का प्रभाव उपस्थिति तक सीमित है।

तो अगर कोई फर्क नहीं पड़ता है , तो आप को मना नहीं करते हैं और हम स्वीकार करते हैं कि हम केवल पंक्ति संरचना के साथ फ़ोटोशॉप खेल रहे हैं और प्रदर्शन पर ध्यान दे रहे हैं, आइए कुछ और चीजों की व्याख्या करते हैं।

  • PostgreSQL में तार्किक आदेश मौजूद नहीं है
  • भौतिक आदेश के वास्तविक लाभ हैं (तालिका पैकिंग)
  • PostgreSQL आपको CREATE TABLEया तो बाद में फिजिकल ऑर्डरिंग पर कोई नियंत्रण नहीं देता है (हालांकि यह बहुत अधिक प्राथमिकता होगी)

तो PostgreSQL एक खराब डिस्प्ले लेयर है। सब ALTERठीक है, जबकि ठीक काम करता है तुम अजगर अस्थायी नहीं होना चाहिए। दोनों ALTER TABLEऔर CAVEAT अनुभाग इसका उल्लेख करते हैं,

कुछ DDL कमांड, वर्तमान में केवल TRUNCATEऔर टेबल-पुनर्लेखन के रूप ALTER TABLEMVCC- सुरक्षित नहीं हैं। इसका मतलब यह है कि ट्रंकेशन या फिर से लिखे जाने के बाद, तालिका समवर्ती लेनदेन के लिए खाली दिखाई देगी, यदि वे DDL कमांड के प्रतिबद्ध होने से पहले लिए गए स्नैपशॉट का उपयोग कर रहे हैं। यह केवल एक लेनदेन के लिए एक मुद्दा होगा जिसने डीडीएल कमांड शुरू होने से पहले तालिका में सवाल का उपयोग नहीं किया था - ऐसा किया गया कोई भी लेनदेन कम से कम एक एसीसी शेयर टेबल लॉक होगा, जो उस लेनदेन के पूरा होने तक डीडीएल कमांड को अवरुद्ध कर देगा। इसलिए ये कमांड्स लक्ष्य तालिका पर क्रमिक प्रश्नों के लिए तालिका सामग्री में कोई स्पष्ट असंगतता पैदा नहीं करेंगे, लेकिन वे लक्ष्य तालिका की सामग्री और डेटाबेस में अन्य तालिकाओं के बीच दृश्य असंगतता का कारण बन सकते हैं।

और, अगर यह सब पर्याप्त नहीं है, और आप अभी भी यह दिखावा करना चाहते हैं कि यह एक अच्छा विचार है, बल्कि एक भयानक विचार है। तो यह कोशिश करो,

  1. के साथ तालिका डंप pg_dump -t
  2. डंप में हाथ से कॉलम को फिर से व्यवस्थित करें।
  3. सामान को TEMP तालिका में लोड करें।
  4. BEGIN एक लेन-देन
  5. DROP पूरी तरह से पुरानी तालिका,
  6. RENAME अस्थायी तालिका को ठेस तालिका।
  7. COMMIT

अगर वह सब अत्यधिक लगता है, तो ध्यान रखें कि डेटाबेस में पंक्तियों को अपडेट करने के लिए पंक्तियों को फिर से लिखना होगा (यह मानते हुए कि वे टोस्ट नहीं हैं । आपको डेटा को पार्स करना होगा और टेबल स्कीमा को फिर से बनाना होगा, लेकिन दोनों तरह से आपको फिर से लिखना होगा। पंक्ति। अगर मुझे यह कार्य करना था , तो मैं इसे कैसे करूंगा।

लेकिन, यह सब आम तौर पर बोल रहा है। किसी ने भी आपके परिणामों को पुन: पेश नहीं किया है।

आपने एक परीक्षण-मामला दिया है जिसे हम नहीं चला सकते

ERROR:  column users.authentication_code does not exist
LINE 24: users.authentication_code,

और, आपने हमें उस सटीक संस्करण के बारे में नहीं बताया है, जिस पर आप हैं।


पूरी तरह से स्पष्टीकरण के लिए धन्यवाद, मैंने विशिष्ट संस्करण को शामिल करने के लिए प्रश्न को संपादित किया है।
एंडी

4
बग 9.6.1.0
ypercube

0

मैं अपने बग का बैकअप लेकर और अपने डेटाबेस को पुनर्स्थापित करके इस बग के आसपास पहुँच गया।

हरोकू के लिए कदम

  • रखरखाव मोड चालू करें: heroku maintenance:on
  • डेटाबेस का बैकअप लें: heroku pg:backups:capture
  • डेटाबेस पुनर्स्थापित करें: heroku pg:backups:restore
  • एप्लिकेशन पुनरारंभ करें: heroku restart
  • रखरखाव मोड बंद करें: heroku maintenance:off

0

मैं इस बग में भाग गया। उन लोगों के लिए जो अपने DB को पूरी तरह से बैकअप / रिस्टोर नहीं करना चाहते हैं। पता है कि बस तालिका काम करता है नकल। हालाँकि किसी तालिका की प्रतिलिपि बनाने का कोई "जादुई" तरीका नहीं है। मैंने इसका उपयोग करके किया:

SELECT * INTO mytable_copy FROM mytable;
ALTER TABLE mytable RENAME TO mytable_backup; -- just in case. you never know
ALTER TABLE mytable_copy RENAME TO mytable;

उसके बाद, आपको अभी भी मैन्युअल रूप से अपने अनुक्रमित, विदेशी कुंजी और चूक को फिर से बनाना होगा। इस तरह तालिका को फिर से बनाने से बग गायब हो गया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.