mayankjoin3

sql differece between two files

Apr 19th, 2026 (edited)
91
0
Never
5
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.48 KB | None | 0 0
  1. import re
  2.  
  3. FILE1 = "erp1.sql"
  4. FILE2 = "erp2.sql"
  5. OUTPUT_FILE = "schema_diff.txt"
  6.  
  7.  
  8. def remove_sql_comments(sql_text: str) -> str:
  9.     # Remove block comments /* ... */
  10.     sql_text = re.sub(r"/\*.*?\*/", "", sql_text, flags=re.S)
  11.  
  12.     # Remove full-line comments starting with --
  13.     sql_text = re.sub(r"^\s*--.*$", "", sql_text, flags=re.M)
  14.  
  15.     # Remove full-line comments starting with #
  16.     sql_text = re.sub(r"^\s*#.*$", "", sql_text, flags=re.M)
  17.  
  18.     return sql_text
  19.  
  20.  
  21. def extract_create_tables(sql_text: str):
  22.     """
  23.    Returns dict:
  24.    {
  25.        table_name: full CREATE TABLE body text
  26.    }
  27.    """
  28.     sql_text = remove_sql_comments(sql_text)
  29.  
  30.     pattern = re.compile(
  31.         r"CREATE\s+TABLE\s+`?([a-zA-Z0-9_]+)`?\s*\((.*?)\)\s*(ENGINE|TYPE|COMMENT|DEFAULT|;)",
  32.         re.I | re.S
  33.     )
  34.  
  35.     tables = {}
  36.     for match in pattern.finditer(sql_text):
  37.         table_name = match.group(1)
  38.         table_body = match.group(2)
  39.         tables[table_name] = table_body
  40.  
  41.     return tables
  42.  
  43.  
  44. def split_column_lines(table_body: str):
  45.     """
  46.    Split CREATE TABLE body into lines, but safely.
  47.    Usually one column/index per line in MySQL dumps.
  48.    """
  49.     lines = []
  50.     current = []
  51.     paren_level = 0
  52.  
  53.     for ch in table_body:
  54.         if ch == '(':
  55.             paren_level += 1
  56.         elif ch == ')':
  57.             paren_level -= 1
  58.  
  59.         if ch == ',' and paren_level == 0:
  60.             line = ''.join(current).strip()
  61.             if line:
  62.                 lines.append(line)
  63.             current = []
  64.         else:
  65.             current.append(ch)
  66.  
  67.     last = ''.join(current).strip()
  68.     if last:
  69.         lines.append(last)
  70.  
  71.     return lines
  72.  
  73.  
  74. def normalize_spaces(s: str) -> str:
  75.     return re.sub(r"\s+", " ", s.strip())
  76.  
  77.  
  78. def parse_columns(table_body: str):
  79.     """
  80.    Returns dict:
  81.    {
  82.        column_name: {
  83.            "type": "...",
  84.            "full": "..."
  85.        }
  86.    }
  87.  
  88.    Ignores PRIMARY KEY, KEY, UNIQUE KEY, CONSTRAINT, INDEX, etc.
  89.    """
  90.     lines = split_column_lines(table_body)
  91.     columns = {}
  92.  
  93.     skip_starts = (
  94.         "PRIMARY KEY", "UNIQUE KEY", "KEY", "INDEX",
  95.         "CONSTRAINT", "FULLTEXT KEY", "SPATIAL KEY"
  96.     )
  97.  
  98.     for line in lines:
  99.         clean = normalize_spaces(line)
  100.  
  101.         upper = clean.upper()
  102.         if upper.startswith(skip_starts):
  103.             continue
  104.  
  105.         # Match column definition: `colname` datatype ....
  106.         m = re.match(r"^`([^`]+)`\s+(.*)$", clean)
  107.         if not m:
  108.             # fallback for non-backtick column names
  109.             m = re.match(r"^([a-zA-Z0-9_]+)\s+(.*)$", clean)
  110.             if not m:
  111.                 continue
  112.  
  113.         col_name = m.group(1)
  114.         remainder = m.group(2).strip()
  115.  
  116.         # Extract type = first token, but allow types like:
  117.         # decimal(10,2), varchar(100), enum('a','b')
  118.         type_match = re.match(
  119.             r"^([a-zA-Z]+(?:\s+[a-zA-Z]+)?(?:\([^\)]*\))?(?:\s+unsigned)?(?:\s+zerofill)?)\b(.*)$",
  120.             remainder,
  121.             flags=re.I
  122.         )
  123.  
  124.         if type_match:
  125.             col_type = normalize_spaces(type_match.group(1))
  126.         else:
  127.             col_type = remainder.split()[0]
  128.  
  129.         columns[col_name] = {
  130.             "type": col_type,
  131.             "full": normalize_spaces(remainder)
  132.         }
  133.  
  134.     return columns
  135.  
  136.  
  137. def parse_schema(sql_file: str):
  138.     with open(sql_file, "r", encoding="utf-8", errors="ignore") as f:
  139.         sql_text = f.read()
  140.  
  141.     create_tables = extract_create_tables(sql_text)
  142.  
  143.     schema = {}
  144.     for table_name, table_body in create_tables.items():
  145.         schema[table_name] = parse_columns(table_body)
  146.  
  147.     return schema
  148.  
  149.  
  150. def compare_schemas(schema1, schema2, name1="erp1.sql", name2="erp2.sql"):
  151.     differences = []
  152.  
  153.     tables1 = set(schema1.keys())
  154.     tables2 = set(schema2.keys())
  155.  
  156.     only_in_1 = sorted(tables1 - tables2)
  157.     only_in_2 = sorted(tables2 - tables1)
  158.  
  159.     if only_in_1:
  160.         differences.append(f"Tables only in {name1}:")
  161.         for t in only_in_1:
  162.             differences.append(f"  - {t}")
  163.         differences.append("")
  164.  
  165.     if only_in_2:
  166.         differences.append(f"Tables only in {name2}:")
  167.         for t in only_in_2:
  168.             differences.append(f"  - {t}")
  169.         differences.append("")
  170.  
  171.     common_tables = sorted(tables1 & tables2)
  172.  
  173.     for table in common_tables:
  174.         cols1 = schema1[table]
  175.         cols2 = schema2[table]
  176.  
  177.         colset1 = set(cols1.keys())
  178.         colset2 = set(cols2.keys())
  179.  
  180.         only_cols1 = sorted(colset1 - colset2)
  181.         only_cols2 = sorted(colset2 - colset1)
  182.  
  183.         table_diff = []
  184.  
  185.         if only_cols1:
  186.             table_diff.append(f"  Columns only in {name1}:")
  187.             for c in only_cols1:
  188.                 table_diff.append(f"    - {c} : {cols1[c]['full']}")
  189.  
  190.         if only_cols2:
  191.             table_diff.append(f"  Columns only in {name2}:")
  192.             for c in only_cols2:
  193.                 table_diff.append(f"    - {c} : {cols2[c]['full']}")
  194.  
  195.         common_cols = sorted(colset1 & colset2)
  196.  
  197.         for col in common_cols:
  198.             type1 = cols1[col]["type"]
  199.             type2 = cols2[col]["type"]
  200.             full1 = cols1[col]["full"]
  201.             full2 = cols2[col]["full"]
  202.  
  203.             if normalize_spaces(type1).lower() != normalize_spaces(type2).lower():
  204.                 table_diff.append(f"  Datatype changed for column '{col}':")
  205.                 table_diff.append(f"    {name1}: {type1}")
  206.                 table_diff.append(f"    {name2}: {type2}")
  207.  
  208.             if normalize_spaces(full1).lower() != normalize_spaces(full2).lower():
  209.                 if normalize_spaces(type1).lower() == normalize_spaces(type2).lower():
  210.                     table_diff.append(f"  Definition changed for column '{col}':")
  211.                     table_diff.append(f"    {name1}: {full1}")
  212.                     table_diff.append(f"    {name2}: {full2}")
  213.  
  214.         if table_diff:
  215.             differences.append(f"Table: {table}")
  216.             differences.extend(table_diff)
  217.             differences.append("")
  218.  
  219.     if not differences:
  220.         differences.append("No schema differences found.")
  221.  
  222.     return "\n".join(differences)
  223.  
  224.  
  225. def main():
  226.     schema1 = parse_schema(FILE1)
  227.     schema2 = parse_schema(FILE2)
  228.  
  229.     result = compare_schemas(schema1, schema2, FILE1, FILE2)
  230.  
  231.     with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
  232.         f.write(result)
  233.  
  234.     print(f"Done. Differences written to: {OUTPUT_FILE}")
  235.  
  236.  
  237. if __name__ == "__main__":
  238.     main()
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment