यह प्रोजेक्ट सामान्यीकरण में एक बग है , जो एक गैर-निर्धारक समारोह के साथ एक मामले की अभिव्यक्ति के अंदर एक उप-वर्ग का उपयोग करके उजागर किया गया है।
समझाने के लिए, हमें दो बातों पर ध्यान देना होगा:
- SQL सर्वर सीधे सबक्वेरी को निष्पादित नहीं कर सकता है, इसलिए वे हमेशा अनियंत्रित होते हैं या एक आवेदन में परिवर्तित होते हैं ।
- इस प्रकार के शब्दार्थ
CASE
ऐसे होते हैं कि THEN
यदि WHEN
खंड सही निकलता है तो एक अभिव्यक्ति का मूल्यांकन किया जाना चाहिए ।
समस्याग्रस्त मामले में पेश किया गया (तुच्छ) उपशम इसलिए परिणामी ऑपरेटर (नेस्टेड लूप्स जॉइन) होता है। दूसरी आवश्यकता को पूरा करने के लिए, SQL सर्वर शुरू में एक्सप्रेशन dbo.test6(1) + dbo.test6(2)
के अंदरूनी हिस्से को रखता है :
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
... शामिल होने पर विधेय के माध्यम सेCASE
सम्मानित किए गए शब्दार्थ के साथ :
[@i]=(1) OR [@i]=(2) OR IsFalseOrNull [@i]=(3)
लूप के आंतरिक पक्ष का मूल्यांकन केवल तभी किया जाता है जब पास-थ्रू स्थिति झूठी (अर्थ @i = 3
) का मूल्यांकन करती है । यह सब अब तक सही है। नेस्टेड लूप्स के बाद होने वाली गणना स्केलर भी CASE
शब्दार्थ को सही ढंग से सम्मानित करती है:
[Expr1001] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
समस्या यह है कि क्वेरी संकलन का प्रोजेक्ट सामान्यीकरण चरण Expr1000
असंबद्ध है और यह निर्धारित करता है कि यह सुरक्षित होगा ( कथावाचक: यह नहीं है ) इसे लूप के बाहर ले जाने के लिए:
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
यह * पास-थ्रू विधेय द्वारा लागू किए गए शब्दार्थ को तोड़ता है, इसलिए फ़ंक्शन का मूल्यांकन तब किया जाता है जब यह नहीं होना चाहिए, और एक अनंत लूप परिणाम।
आपको इस बग की रिपोर्ट करनी चाहिए। एक वर्कअराउंड अभिव्यक्ति को सहसंबद्ध (यानी @i
अभिव्यक्ति में शामिल) करके इसे बाहर ले जाने से रोकने के लिए है, लेकिन यह निश्चित रूप से हैक है। प्रोजेक्ट सामान्यीकरण को अक्षम करने का एक तरीका है, लेकिन मुझे सार्वजनिक रूप से साझा नहीं करने के लिए कहा गया है, इसलिए मैं नहीं करूंगा।
यह समस्या SQL सर्वर 2019 में उत्पन्न नहीं होती है जब स्केलर फ़ंक्शन इनलाइन होता है , क्योंकि इनलाइनिंग तर्क सीधे पार्स किए गए पेड़ पर संचालित होता है (अच्छी तरह से परियोजना के सामान्य होने से पहले)। गैर-पुनरावर्ती को inlining तर्क द्वारा प्रश्न में सरल तर्क को सरल बनाया जा सकता है:
[Expr1019] = (Scalar Operator((1)))
[Expr1045] = Scalar Operator(CONVERT_IMPLICIT(int,CONVERT_IMPLICIT(int,[Expr1019],0)+(2),0))
... जो 3 लौटाता है।
मुख्य मुद्दे को स्पष्ट करने का एक और तरीका है:
-- Not schema bound to make it non-det
CREATE OR ALTER FUNCTION dbo.Error()
RETURNS integer
-- WITH INLINE = OFF -- SQL Server 2019 only
AS
BEGIN
RETURN 1/0;
END;
GO
DECLARE @i integer = 1;
SELECT
CASE
WHEN @i = 1 THEN 1
WHEN @i = 2 THEN 2
WHEN @i = 3 THEN (SELECT dbo.Error()) -- 'subquery'
ELSE NULL
END;
2008 R2 से 2019 CTP 3.0 तक सभी संस्करणों के नवीनतम बिल्ड पर पुन: प्रस्तुत करता है।
मार्टिन स्मिथ द्वारा प्रदान किया गया एक और उदाहरण (स्केलर फ़ंक्शन के बिना) :
SELECT IIF(@@TRANCOUNT >= 0, 1, (SELECT CRYPT_GEN_RANDOM(4)/ 0))
इसके लिए आवश्यक सभी प्रमुख तत्व हैं:
CASE
(आंतरिक रूप से कार्यान्वित ScaOp_IIF
)
- एक गैर-नियतात्मक कार्य (
CRYPT_GEN_RANDOM
)
- शाखा पर एक उपश्रेणी जिसे निष्पादित नहीं किया जाना चाहिए (
(SELECT ...)
)
* कड़ाई से, उपरोक्त परिवर्तन अभी भी सही हो सकता है यदि मूल्यांकन को Expr1000
सही ढंग से स्थगित किया गया था, क्योंकि यह केवल सुरक्षित निर्माण द्वारा संदर्भित है:
[Expr1002] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
... लेकिन इसके लिए एक आंतरिक ForceOrder ध्वज (क्वेरी संकेत नहीं) की आवश्यकता होती है, जो या तो सेट नहीं है। किसी भी मामले में, परियोजना के सामान्यीकरण द्वारा लागू किए गए तर्क का कार्यान्वयन गलत या अपूर्ण है।
SQL सर्वर के लिए Azure फ़ीडबैक साइट पर बग रिपोर्ट ।