chore: reorganize para current/, rails 8.1, testes e readme
- move app para current/ (estrutura capistrano) - rails 7.2 → 8.1, ruby 3.2, sqlite3 2.x - adiciona primary_key Idinformativo no model - schema.rb completo com todas as tabelas - testes minitest: models (Tag, Informativo, Tema) e controllers - readme atualizado em pt-br com stack e instruções de desenvolvimento - gitignore exclui dump.sql, *.duckdb e sqlite3
This commit is contained in:
107
mysql_to_duckdb.py
Normal file
107
mysql_to_duckdb.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
import duckdb
|
||||
|
||||
def convert_mysql_to_duckdb(sql):
|
||||
# Remove MySQL-specific conditional comments /*!...*/
|
||||
sql = re.sub(r'/\*!.*?\*/', '', sql, flags=re.DOTALL)
|
||||
|
||||
# Remove SET statements
|
||||
sql = re.sub(r'^SET\s+.*?;', '', sql, flags=re.MULTILINE | re.IGNORECASE)
|
||||
|
||||
# Remove LOCK/UNLOCK TABLES
|
||||
sql = re.sub(r'^LOCK TABLES.*?;', '', sql, flags=re.MULTILINE | re.IGNORECASE)
|
||||
sql = re.sub(r'^UNLOCK TABLES.*?;', '', sql, flags=re.MULTILINE | re.IGNORECASE)
|
||||
|
||||
# Remove DROP TABLE IF EXISTS (DuckDB will handle via CREATE OR REPLACE)
|
||||
sql = re.sub(r'^DROP TABLE IF EXISTS\s+`(\w+)`;', r'DROP TABLE IF EXISTS "\1";', sql, flags=re.MULTILINE | re.IGNORECASE)
|
||||
|
||||
# Replace backtick identifiers with double-quoted
|
||||
sql = re.sub(r'`(\w+)`', r'"\1"', sql)
|
||||
|
||||
# Remove trailing table options (ENGINE=..., AUTO_INCREMENT=..., DEFAULT CHARSET=...)
|
||||
sql = re.sub(
|
||||
r'\)\s*ENGINE\s*=\s*\w+[^;]*;',
|
||||
r');',
|
||||
sql,
|
||||
flags=re.IGNORECASE
|
||||
)
|
||||
|
||||
# Remove index/key definitions from CREATE TABLE (KEY, UNIQUE KEY, but keep PRIMARY KEY)
|
||||
# We'll process CREATE TABLE blocks to remove non-primary key definitions
|
||||
def clean_create_table(match):
|
||||
block = match.group(0)
|
||||
lines = block.split('\n')
|
||||
result = []
|
||||
for line in lines:
|
||||
stripped = line.strip().rstrip(',')
|
||||
# Skip KEY and UNIQUE KEY lines (not PRIMARY KEY)
|
||||
if re.match(r'(UNIQUE\s+)?KEY\s+"', stripped, re.IGNORECASE):
|
||||
continue
|
||||
result.append(line)
|
||||
# Fix trailing comma before closing paren
|
||||
cleaned = '\n'.join(result)
|
||||
cleaned = re.sub(r',\s*\n(\s*\))', r'\n\1', cleaned)
|
||||
return cleaned
|
||||
|
||||
sql = re.sub(r'CREATE TABLE.*?;', clean_create_table, sql, flags=re.DOTALL | re.IGNORECASE)
|
||||
|
||||
# Fix column type widths: int(N) -> int, bigint(N) -> bigint, tinyint(N) -> tinyint
|
||||
sql = re.sub(r'\b(int|bigint|tinyint|smallint|mediumint)\(\d+\)', r'\1', sql, flags=re.IGNORECASE)
|
||||
|
||||
# Convert MySQL types to DuckDB types
|
||||
sql = re.sub(r'\blongtext\b', 'text', sql, flags=re.IGNORECASE)
|
||||
sql = re.sub(r'\bmediumtext\b', 'text', sql, flags=re.IGNORECASE)
|
||||
sql = re.sub(r'\bmediumblob\b', 'blob', sql, flags=re.IGNORECASE)
|
||||
sql = re.sub(r'\bdatetime\b', 'timestamp', sql, flags=re.IGNORECASE)
|
||||
sql = re.sub(r'\bdouble\b', 'double', sql, flags=re.IGNORECASE)
|
||||
|
||||
# Remove AUTO_INCREMENT from column definitions
|
||||
sql = re.sub(r'\s+AUTO_INCREMENT\b', '', sql, flags=re.IGNORECASE)
|
||||
|
||||
# Remove DEFAULT CHARSET and COLLATE from column definitions
|
||||
sql = re.sub(r'\s+CHARACTER SET\s+\w+', '', sql, flags=re.IGNORECASE)
|
||||
sql = re.sub(r'\s+COLLATE\s+\w+', '', sql, flags=re.IGNORECASE)
|
||||
|
||||
# Remove 'unsigned' modifier (DuckDB doesn't have unsigned types)
|
||||
sql = re.sub(r'\s+unsigned\b', '', sql, flags=re.IGNORECASE)
|
||||
|
||||
return sql
|
||||
|
||||
|
||||
def main():
|
||||
print("Reading dump.sql...")
|
||||
with open('dump.sql', 'r', encoding='utf-8') as f:
|
||||
sql = f.read()
|
||||
|
||||
print("Converting MySQL SQL to DuckDB-compatible SQL...")
|
||||
converted = convert_mysql_to_duckdb(sql)
|
||||
|
||||
# Split into individual statements
|
||||
statements = [s.strip() for s in converted.split(';') if s.strip() and not s.strip().startswith('--')]
|
||||
|
||||
print(f"Connecting to ambienteja.duckdb...")
|
||||
con = duckdb.connect('ambienteja.duckdb')
|
||||
|
||||
errors = []
|
||||
ok = 0
|
||||
for i, stmt in enumerate(statements):
|
||||
if not stmt:
|
||||
continue
|
||||
try:
|
||||
con.execute(stmt)
|
||||
ok += 1
|
||||
except Exception as e:
|
||||
errors.append((stmt[:80].replace('\n', ' '), str(e)))
|
||||
|
||||
con.close()
|
||||
print(f"\nDone: {ok} statements executed successfully.")
|
||||
if errors:
|
||||
print(f"{len(errors)} errors:")
|
||||
for stmt_preview, err in errors[:20]:
|
||||
print(f" STMT: {stmt_preview}")
|
||||
print(f" ERR: {err}\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user