2009-04-22 16 views
6

मुझे एक IronPython स्क्रिप्ट मिली है जो SQL सर्वर डेटाबेस के विरुद्ध SQL कथन का एक गुच्छा निष्पादित करता है। बयान बड़े तार होते हैं जिनमें वास्तव में "जाओ" कीवर्ड से अलग कई कथन होते हैं। यह तब काम करता है जब वे एसक्यूएल प्रबंधन स्टूडियो और कुछ अन्य टूल्स से चलते हैं, लेकिन एडीओ में नहीं। तो मैं तो जैसे 2.5 "फिर" मॉड्यूल का उपयोग कर तार विभाजित:SQL कथन पार्सिंग के लिए regex

splitter = re.compile(r'\bGO\b', re.IGNORECASE) 
for script in splitter.split(scriptBlob): 
    if(script): 
     [... execute the query ...] 

यह दुर्लभ मामला वहाँ शब्द एक टिप्पणी या एक स्ट्रिंग में "जाओ" है कि में टूट जाता है। मैं उस बिल्ली के आसपास कैसे काम करूंगा? यानी सही ढंग से दो लिपियों में इस स्ट्रिंग पार्स:

-- this is a great database script! go team go! 
INSERT INTO myTable(stringColumn) VALUES ('go away!') 
/* 
    here are some comments that go with this script. 
*/ 
GO 
INSERT INTO myTable(stringColumn) VALUES ('this is the next script') 

संपादित करें:

मैं और अधिक खोज की है और इस एसक्यूएल प्रलेखन पाया: http://msdn.microsoft.com/en-us/library/ms188037(SQL.90).aspx

यह पता चला है के रूप में, गो होना चाहिए कुछ उत्तरों के सुझाव के रूप में अपनी लाइन पर। हालांकि इसके बाद "गिनती" पूर्णांक हो सकता है जो वास्तव में कथन बैच निष्पादित करेगा जो कि कई बार (किसी ने वास्तव में पहले इसका उपयोग किया है ??) और इसके बाद उसी पंक्ति पर एकल-पंक्ति टिप्पणियां हो सकती हैं (लेकिन नहीं बहु लाइन, मैं इस परीक्षण किया) तो जादू regex कुछ ऐसा दिखाई देगा:।

"(?m)^\s*GO\s*\d*\s*$" 

इस छोड़कर खाता नहीं है के लिए:

  • एक संभव एकल लाइन टिप्पणी ("--" किसी भी द्वारा पीछा किया एक लाइन ब्रेक को छोड़कर चरित्र) अंत में।
  • पूरी लाइन एक बड़ी बहु-पंक्ति टिप्पणी के अंदर है।

मैं "गिनती" तर्क को कैप्चर करने और इसका उपयोग करने के बारे में चिंतित नहीं हूं। अब जब मेरे पास कुछ तकनीकी दस्तावेज हैं, तो मैं इसे "spec" लिखने के करीब tantalizingly बंद कर रहा हूं और कभी इसके बारे में चिंता करने की ज़रूरत नहीं है।

+0

मैं हर किसी के जवाब upmodded, सहायता के लिए धन्यवाद मैंने mcassano का जवाब दिया क्योंकि वह सुझाव देने वाला पहला व्यक्ति था कि जीओ हमेशा अपनी लाइन पर हो सकता है, जिसने मुझे उस आदेश के लिए प्रलेखन को देखने के लिए प्रेरित किया और आखिरकार एक बहुत ही आसान समाधान हुआ। –

उत्तर

8

हमेशा "लाइन" हमेशा एक पंक्ति पर है? आप बस "^ जाओ $" पर विभाजित हो सकते हैं।

+0

, मैंने वैकल्पिक व्हाइटसाइट में भी फेंक दिया, बस मामले में। –

+0

मुझे लगता है कि यह आमतौर पर अपनी लाइन पर होता है, और यह इस स्क्रिप्ट के लिए एक अच्छा पर्याप्त समाधान हो सकता है। यद्यपि सख्ती से, यह एक मल्टीलाइन टिप्पणी या मल्टीलाइन स्ट्रिंग के अंदर अपनी लाइन पर किसी गो के खिलाफ सुरक्षा नहीं करेगा (हालांकि बहुत दुर्लभ है।) –

+0

मेरी अब अपडेट की गई स्क्रिप्ट इस दोष का उपचार करती है। –

5

चूंकि आप टिप्पणियों, नेस्टेड टिप्पणियों, प्रश्नों के अंदर टिप्पणियों आदि के अंदर टिप्पणी कर सकते हैं, रेगेक्स के साथ ऐसा करने का कोई सौहार्दपूर्ण तरीका नहीं है। यही कारण है कि

INSERT INTO table (name) VALUES (
-- GO NOW GO 
'GO to GO /* GO */ GO' + 
/* some comment 'go go go' 
-- */ 'GO GO' /* 
GO */ 
) 

का उल्लेख किए बिना:

बस निम्न स्क्रिप्ट immagine

INSERT INTO table (go) values ('xxx') GO 

एक ही रास्ता बजाय स्टेटफुल पार्सर के निर्माण के लिए किया जाएगा। एक जो एक समय में एक char पढ़ता है, और इसमें एक ध्वज होता है जिसे एक टिप्पणी/उद्धरण-सीमित स्ट्रिंग/आदि के अंदर सेट किया जाएगा और जब यह समाप्त होता है तो रीसेट हो जाएंगे, इसलिए कोड उन लोगों के अंदर "जाओ" उदाहरणों को अनदेखा कर सकता है।

+1

एक पार्सर सबसे अच्छा समाधान होगा, लेकिन यदि आप गारंटी दे सकते हैं कि जीओ हमेशा एक लाइन पर है, तो आप बहुत सुरक्षित हैं, विशेष रूप से SQL92 में मल्टीलाइन टिप्पणियां नहीं हैं। –

+1

आपने बस मेरे दिमाग को उड़ा दिया। –

5

GO एक लाइन पर हमेशा होता है, तो अपने आप में आप इस तरह स्प्लिट का उपयोग कर सकते हैं:

#!/usr/bin/python 

import re 

sql = """-- this is a great database script! go team go! 
INSERT INTO myTable(stringColumn) VALUES ('go away!') 
/* 
    here are some comments that go with this script. 
*/ 
GO 5 --this is a test 
INSERT INTO myTable(stringColumn) VALUES ('this is the next script')""" 

statements = re.split("(?m)^\s*GO\s*(?:[0-9]+)?\s*(?:--.*)?$", sql) 

for statement in statements: 
    print "the statement is\n%s\n" % (statement) 
  • (?m) बहु matchings पर मुड़ता है, कि ^ है और $ की (बजाय शुरू करने और पंक्ति के अंत से मेल खाएगी स्ट्रिंग की शुरुआत और अंत)।एक लाइन
  • \s* शून्य या अधिक व्हाइटस्पेस से मेल खाता है (अंतरिक्ष, टैब, आदि)
  • GO के शुरू में
  • ^ मैचों एक शाब्दिक GO के रूप में पहले
  • (?:[0-9]+)? एक वैकल्पिक से मेल खाता है
  • \s* मैचों से मेल खाता है पूर्णांक संख्या (संभावित अग्रणी शून्य के साथ)
  • \s*
  • ,210 एक लाइन

विभाजन GO लाइन की खपत होगी के अंत में एक वैकल्पिक अंत के लाइन टिप्पणी से मेल खाता है

  • $ मैच, ताकि आप इसके बारे में चिंता करने की ज़रूरत नहीं होगी। यह आपको बयानों की एक सूची के साथ छोड़ देगा।

    इस संशोधित विभाजन में एक समस्या है: यह आपको गो के बाद नंबर वापस नहीं देगा, अगर यह महत्वपूर्ण है तो मैं कहूंगा कि यह किसी रूप के एक पार्सर में जाने का समय है।

  • +0

    यह एक अच्छा सुझाव है, और विस्तृत टूटने के लिए धन्यवाद। –

    +0

    चास, उपरोक्त संपादन देखें - मैं गो लाइन के अंत में एक पंक्ति टिप्पणी की जांच कैसे करूं? कृपया मेरे regex अनुभवहीनता क्षमा करें। मल्टीलाइन मिलान चालू करने के बाद –

    2

    यह पता नहीं लगाएगा कि GO कभी किसी कथन के अंदर एक चर नाम के रूप में उपयोग किया जाता है, लेकिन टिप्पणियों या तारों के अंदर उन लोगों का ख्याल रखना चाहिए।

    संपादित करें: यह अब काम करता है GO, बयान का हिस्सा है जब तक यह अपने आप लाइन में नहीं है।

    import re 
    
    line_comment = r'(?:--|#).*$' 
    block_comment = r'/\*[\S\s]*?\*/' 
    singe_quote_string = r"'(?:\\.|[^'\\])*'" 
    double_quote_string = r'"(?:\\.|[^"\\])*"' 
    go_word = r'^[^\S\n]*(?P<GO>GO)[^\S\n]*\d*[^\S\n]*(?:(?:--|#).*)?$' 
    
    full_pattern = re.compile(r'|'.join((
        line_comment, 
        block_comment, 
        singe_quote_string, 
        double_quote_string, 
        go_word, 
    )), re.IGNORECASE | re.MULTILINE) 
    
    def split_sql_statements(statement_string): 
        last_end = 0 
        for match in full_pattern.finditer(statement_string): 
         if match.group('GO'): 
          yield statement_string[last_end:match.start()] 
          last_end = match.end() 
        yield statement_string[last_end:] 
    

    उदाहरण उपयोग:

    statement_string = r""" 
    -- this is a great database script! go team go! 
    INSERT INTO go(go) VALUES ('go away!') 
    go 7 -- foo 
    INSERT INTO go(go) VALUES (
        'I have to GO " with a /* comment to GO inside a /* GO string /*' 
    ) 
    /* 
        here are some comments that go with this script. 
        */ 
        GO 
        INSERT INTO go(go) VALUES ('this is the next script') 
    """ 
    
    for statement in split_sql_statements(statement_string): 
        print '=======' 
        print statement 
    

    आउटपुट:

    ======= 
    
    -- this is a great database script! go team go! 
    INSERT INTO go(go) VALUES ('go away!') 
    
    ======= 
    
    INSERT INTO go(go) VALUES (
        'I have to GO " with a /* comment to GO inside a /* GO string /*' 
    ) 
    /* 
        here are some comments that go with this script. 
        */ 
    
    ======= 
    
        INSERT INTO go(go) VALUES ('this is the next script') 
    
    +0

    ए/* जाओ स्ट्रिंग/* 'गो – nosklo

    +0

    के अंदर जाओ/* टिप्पणी के साथ' मुझे जाना है 'पर विफल रहता है, नहीं, ऐसा नहीं है। यह सभी गैर-ओवरलैपिंग मैचों को मिलेगा, इसलिए संपूर्ण सिंगल-कोट स्ट्रिंग एक मैच के रूप में पहचाना जाएगा। –

    +0

    कोई रन नहीं जब मैं इसे चलाता हूं ... –

    संबंधित मुद्दे