2025, Oct 04 09:34

MySQL में PARTITION BY RANGE और VALUES LESS THAN पर SQLGlot पार्सिंग क्यों विफल होती है

जानें क्यों SQLGlot 27.8.0 MySQL के PARTITION BY RANGE और VALUES LESS THAN सिंटैक्स को पार्स नहीं कर पाता, doris डायलेक्ट में भी विफल, और आगे क्या करें.

टेबल पार्टिशनिंग वाले MySQL DDL को पार्स करना सामान्य-उद्देश्य SQL पार्सरों के लिए मुश्किल साबित हो सकता है। अगर आपका स्क्रिप्ट PARTITION BY RANGE के साथ VALUES LESS THAN का उपयोग करता है, तो MySQL के लिए DDL वैध होने के बावजूद sqlglot 27.8.0 पार्स त्रुटि देगा।

समस्या को पुन: उत्पन्न करना

लक्ष्य एक स्कीमा फ़ाइल को पार्स करना और टेबल नाम निकालना है। नीचे दिया गया Python स्निपेट फ़ाइल पढ़ता है, sqlglot से उसे MySQL डायलेक्ट के साथ पार्स करने को कहता है, और exp.Table नोड्स इकट्ठा करता है।

import logging
import sqlglot
from sqlglot import exp
# लॉग सेटअप
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s"
)
log = logging.getLogger(__name__)
def collect_tables(path_to_sql, sql_flavor="mysql"):
    """
    Parse the SQL file and return a set of unique table names found.
    Logs errors if file not found or parsing fails.
    """
    try:
        with open(path_to_sql, "r") as fh:
            ddl_blob = fh.read()
        ast_list = sqlglot.parse(ddl_blob, dialect=sql_flavor)
        seen_tables = set()
        for node in ast_list:
            seen_tables.update([t.name for t in node.find_all(exp.Table)])
        return seen_tables
    except FileNotFoundError:
        log.error(f"File not found: {path_to_sql}")
        return set()
    except Exception as err:
        log.error(f"Error parsing `{path_to_sql}`: {err}")
        return set()
if __name__ == "__main__":
    input_sql = "changeLogs/health-service/create_db.sql"
    names = collect_tables(input_sql)
    log.info(f"Total unique tables found: {len(names)}")
    log.info(f"Table names: {sorted(list(names))}")

स्कीमा में VALUES LESS THAN का उपयोग करते हुए पार्टिशन की गई टेबल्स शामिल हैं। इसे पार्स करने की कोशिश करने पर इस तरह की त्रुटियाँ मिलती हैं:

An error occurred during parsing: Expecting ). Line 19, Col: 26.
  created_at`) USING BTREE
    ) PARTITION BY RANGE ( UNIX_TIMESTAMP(audit_ts)) (
    PARTITION p2401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')),
    PARTITION p2402 VALUES LESS THAN (UNIX_TIMES

यह वह SQL है जो विफलता ट्रिगर करता है:

-- liquibase स्वरूपित SQL
-- changeset debraj.manna@nexla.com:NEX-18235
CREATE TABLE IF NOT EXISTS `audit_control`
(
   `id`            BIGINT auto_increment NOT NULL,
   `message_id`    VARCHAR(100) DEFAULT NULL,
    `resource_type` VARCHAR(30) NOT NULL,
    `event_type`    VARCHAR(30) NOT NULL,
    `resource_id`   INT NOT NULL,
    `origin`        VARCHAR(100) NOT NULL,
    `created_at`    TIMESTAMP NOT NULL,
    `body`          mediumtext NOT NULL,
    `audit_ts`      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, audit_ts),
    KEY `audit_control_resource_type_resource_id_IDX` (`resource_type`,`resource_id`) USING BTREE,
    KEY `audit_control_created_at_IDX` (`created_at`) USING BTREE
    ) PARTITION BY RANGE ( UNIX_TIMESTAMP(audit_ts)) (
    PARTITION p2401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')),
    PARTITION p2402 VALUES LESS THAN (UNIX_TIMESTAMP('2024-03-01 00:00:00')),
    PARTITION p2403 VALUES LESS THAN (UNIX_TIMESTAMP('2024-04-01 00:00:00')),
    PARTITION p2404 VALUES LESS THAN (UNIX_TIMESTAMP('2024-05-01 00:00:00')),
    PARTITION p2405 VALUES LESS THAN (UNIX_TIMESTAMP('2024-06-01 00:00:00')),
    PARTITION p2406 VALUES LESS THAN (UNIX_TIMESTAMP('2024-07-01 00:00:00')),
    PARTITION p2407 VALUES LESS THAN (UNIX_TIMESTAMP('2024-08-01 00:00:00')),
    PARTITION p2408 VALUES LESS THAN (UNIX_TIMESTAMP('2024-09-01 00:00:00')),
    PARTITION p2409 VALUES LESS THAN (UNIX_TIMESTAMP('2024-10-01 00:00:00')),
    PARTITION p2410 VALUES LESS THAN (UNIX_TIMESTAMP('2024-11-01 00:00:00')),
    PARTITION p2411 VALUES LESS THAN (UNIX_TIMESTAMP('2024-12-01 00:00:00')),
    PARTITION p2412 VALUES LESS THAN (UNIX_TIMESTAMP('2025-01-01 00:00:00')),
    PARTITION pN VALUES LESS THAN MAXVALUE
);
CREATE TABLE IF NOT EXISTS `audit_coordination`
(
    `id`         BIGINT auto_increment NOT NULL,
    `message_id` VARCHAR(100) DEFAULT NULL,
    `event_type` VARCHAR(30) NOT NULL,
    `created_at` TIMESTAMP NOT NULL,
    `body`       TEXT NOT NULL,
    `audit_ts`   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, audit_ts)
    ) PARTITION BY RANGE ( UNIX_TIMESTAMP(audit_ts)) (
    PARTITION p2401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')),
    PARTITION p2402 VALUES LESS THAN (UNIX_TIMESTAMP('2024-03-01 00:00:00')),
    PARTITION p2403 VALUES LESS THAN (UNIX_TIMESTAMP('2024-04-01 00:00:00')),
    PARTITION p2404 VALUES LESS THAN (UNIX_TIMESTAMP('2024-05-01 00:00:00')),
    PARTITION p2405 VALUES LESS THAN (UNIX_TIMESTAMP('2024-06-01 00:00:00')),
    PARTITION p2406 VALUES LESS THAN (UNIX_TIMESTAMP('2024-07-01 00:00:00')),
    PARTITION p2407 VALUES LESS THAN (UNIX_TIMESTAMP('2024-08-01 00:00:00')),
    PARTITION p2408 VALUES LESS THAN (UNIX_TIMESTAMP('2024-09-01 00:00:00')),
    PARTITION p2409 VALUES LESS THAN (UNIX_TIMESTAMP('2024-10-01 00:00:00')),
    PARTITION p2410 VALUES LESS THAN (UNIX_TIMESTAMP('2024-11-01 00:00:00')),
    PARTITION p2411 VALUES LESS THAN (UNIX_TIMESTAMP('2024-12-01 00:00:00')),
    PARTITION p2412 VALUES LESS THAN (UNIX_TIMESTAMP('2025-01-01 00:00:00')),
    PARTITION pN VALUES LESS THAN MAXVALUE
);

वास्तव में क्या हो रहा है

पार्सर ऊपर उपयोग किए गए MySQL पार्टिशन सिंटैक्स को सपोर्ट नहीं करता। sqlglot 27.8.0 के संदर्भ में समुदाय चर्चा में यह स्पष्ट किया गया था, और मेंटेनर इसे लागू करने की योजना में नहीं हैं।

यदि इनपुट वैध MySQL है, तो यह संभवतः पार्सर में एक कमी है

लगता है कि हम MySQL के लिए VALUES LESS THAN सिंटैक्स को सपोर्ट नहीं करते

हालाँकि, Doris पहले से इसे सपोर्ट करता है

चूंकि यह MySQL से इनहेरिट करता है, हम शायद वह लॉजिक ऊपर ले आ सकते हैं ताकि यह सिर्फ Doris में न रहे, और दोनों इसका लाभ उठा सकें

डायलेक्ट बदलने की कोशिश भी यहाँ काम नहीं आती। डायलेक्ट को doris पर सेट करने पर भी पार्सिंग विफल रहती है:

An error occurred during parsing: Expecting ). Line 18, Col: 42.
   `audit_control_created_at_IDX` (`created_at`) USING BTREE
    ) PARTITION BY RANGE ( UNIX_TIMESTAMP(audit_ts)) (
    PARTITION p2401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01 00:00:00')),
    PARTI

हम इस पर काम करने की योजना नहीं बना रहे, इसलिए नहीं। लेकिन आप चाहें तो इस पर काम कर सकते हैं, हम एक अच्छे से टेस्ट किए और दस्तावेजीकृत PR को खुशी से स्वीकार करेंगे।

समाधान और व्यावहारिक अगले कदम

ऊपर बताए गए सीमाओं के भीतर, sqlglot 27.8.0 में यह व्यवहार अपेक्षित है। अगर आपका DDL PARTITION BY RANGE के साथ VALUES LESS THAN पर निर्भर करता है, तो लाइब्रेरी पार्स त्रुटि देगी। दिए गए उदाहरण में doris डायलेक्ट पर स्विच करने से भी विफलता दूर नहीं होती। मेंटेनर इस फ़ंक्शनैलिटी को जोड़ने वाले योगदान के लिए खुले हैं।

फिर भी यदि आप कोई दूसरा डायलेक्ट आज़माना चाहते हैं, तो कोड में केवल डायलेक्ट आर्ग्युमेंट बदलना होगा:

names = collect_tables(input_sql, sql_flavor="doris")

ध्यान रखें कि इस इनपुट के लिए doris के साथ भी पार्स त्रुटि बनी रहती है।

यह क्यों मायने रखता है

जब आप माइग्रेशन, लीनएज या स्टैटिक एनालिसिस जैसी AST-चालित टूलिंग पर निर्भर होते हैं, तो असमर्थित व्याकरण सुविधाएँ चुपचाप पाइपलाइनों को रोक सकती हैं। टाइम-सीरीज़ वर्कलोड और ऑडिटिंग स्कीमा में पार्टिशन की गई टेबल्स आम हैं; पहले से जान लेना कि VALUES LESS THAN इस समय sqlglot की कवरेज से बाहर है, आपको डाउनस्ट्रीम समस्याओं के पीछे समय गंवाने से बचाता है।

मुख्य निष्कर्ष

ऑटोमेशन में शामिल करने से पहले अपने DDL के अनुरूप पार्सर सपोर्ट को सत्यापित करें। अगर किसी पार्सिंग गैप से टकराएँ, तो ट्रबलशूटिंग आसान बनाने के लिए सुनिश्चित करें कि आपकी एरर स्निपेट और SQL नमूना रिपोर्ट की गई लाइन और कॉलम सहित बिल्कुल मेल खाते हों। sqlglot 27.8.0 पर PARTITION BY RANGE के साथ VALUES LESS THAN के लिए, पार्स विफलता की अपेक्षा करें और उसी अनुसार योजना बनाएँ, या गायब व्याकरण जोड़ने के लिए एक अच्छे से टेस्ट किए गए PR पर विचार करें।

यह लेख StackOverflow पर एक प्रश्न—लेखक tuk—और उसी उपयोगकर्ता tuk के उत्तर पर आधारित है।