sqloader 0.2.8__tar.gz → 0.2.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {sqloader-0.2.8/sqloader.egg-info → sqloader-0.2.9}/PKG-INFO +2 -1
  2. {sqloader-0.2.8 → sqloader-0.2.9}/pyproject.toml +2 -2
  3. {sqloader-0.2.8 → sqloader-0.2.9}/setup.py +2 -1
  4. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/migrator.py +7 -5
  5. {sqloader-0.2.8 → sqloader-0.2.9/sqloader.egg-info}/PKG-INFO +2 -1
  6. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader.egg-info/requires.txt +1 -0
  7. {sqloader-0.2.8 → sqloader-0.2.9}/tests/test_migrator.py +47 -1
  8. {sqloader-0.2.8 → sqloader-0.2.9}/LICENSE +0 -0
  9. {sqloader-0.2.8 → sqloader-0.2.9}/README.md +0 -0
  10. {sqloader-0.2.8 → sqloader-0.2.9}/setup.cfg +0 -0
  11. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/__init__.py +0 -0
  12. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/__main__.py +0 -0
  13. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/_async_prototype.py +0 -0
  14. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/_prototype.py +0 -0
  15. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/init.py +0 -0
  16. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/mysql.py +0 -0
  17. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/mysql_async.py +0 -0
  18. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/postgresql.py +0 -0
  19. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/postgresql_async.py +0 -0
  20. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/sqlite3.py +0 -0
  21. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/sqlite3_async.py +0 -0
  22. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader/sqloader.py +0 -0
  23. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader.egg-info/SOURCES.txt +0 -0
  24. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader.egg-info/dependency_links.txt +0 -0
  25. {sqloader-0.2.8 → sqloader-0.2.9}/sqloader.egg-info/top_level.txt +0 -0
  26. {sqloader-0.2.8 → sqloader-0.2.9}/tests/test_fetch_aliases.py +0 -0
  27. {sqloader-0.2.8 → sqloader-0.2.9}/tests/test_mysql.py +0 -0
  28. {sqloader-0.2.8 → sqloader-0.2.9}/tests/test_postgresql.py +0 -0
  29. {sqloader-0.2.8 → sqloader-0.2.9}/tests/test_sqlite.py +0 -0
  30. {sqloader-0.2.8 → sqloader-0.2.9}/tests/test_sqloader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqloader
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: A simple and extensible SQL migration and loader utility for Python.
5
5
  Home-page: https://github.com/horrible-gh/py_sqloader.git
6
6
  Author: horrible-gh
@@ -16,6 +16,7 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: LogAssist
18
18
  Requires-Dist: pymysql>=1.1.1
19
+ Requires-Dist: sqlparse>=0.4.0
19
20
  Provides-Extra: postgresql
20
21
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgresql"
21
22
  Provides-Extra: async-mysql
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sqloader"
7
- version = "0.2.8"
7
+ version = "0.2.9"
8
8
  authors = [
9
9
  { name="horrible", email="shinjpn1@gmail.com" },
10
10
  ]
@@ -21,7 +21,7 @@ keywords = [
21
21
  "MySQL", "SQLite", "SQL migration", "schema management",
22
22
  "json sql loader"
23
23
  ]
24
- dependencies=['LogAssist', 'pymysql>=1.1.1']
24
+ dependencies=['LogAssist', 'pymysql>=1.1.1', 'sqlparse>=0.4.0']
25
25
 
26
26
  [project.optional-dependencies]
27
27
  postgresql = ["psycopg2-binary>=2.9.0"]
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='sqloader',
5
- version='0.2.8',
5
+ version='0.2.9',
6
6
  description='py_sqloader package',
7
7
  author='horrible-gh',
8
8
  author_email='shinjpn1@gmail.com',
@@ -23,6 +23,7 @@ setup(
23
23
  python_requires='>=3.6',
24
24
  install_requires=[
25
25
  "pymysql>=1.1.1", # MySQL sync is always included
26
+ "sqlparse>=0.4.0",
26
27
  ],
27
28
  extras_require={
28
29
  "postgresql": ["psycopg2-binary>=2.9.0"],
@@ -1,5 +1,7 @@
1
1
  import os
2
+ import re
2
3
  import glob
4
+ import sqlparse
3
5
  from ._prototype import DatabasePrototype, MYSQL, SQLITE, POSTGRESQL
4
6
 
5
7
  class DatabaseMigrator:
@@ -53,15 +55,15 @@ class DatabaseMigrator:
53
55
  full_path = os.path.join(self.migrations_path, migration)
54
56
 
55
57
  with open(full_path, 'r', encoding='utf-8') as f:
56
- sql_commands = f.read().split(';')
58
+ sql_commands = [s.strip() for s in sqlparse.split(f.read()) if s.strip()]
57
59
 
58
60
  try:
59
61
  # Execute all statements in a single transaction
60
62
  with self.db.begin_transaction() as txn:
61
63
  for command in sql_commands:
62
- command = command.strip()
63
- if command:
64
- txn.execute(command)
64
+ if not re.sub(r'--[^\n]*', '', command).strip():
65
+ continue
66
+ txn.execute(command)
65
67
  # Auto-commit on exit, auto-rollback on exception
66
68
 
67
69
  if self.db.db_type == SQLITE:
@@ -97,4 +99,4 @@ class DatabaseMigrator:
97
99
  Filenames match the relative paths inserted by apply_migration().
98
100
  """
99
101
  rows = self.db.fetch_all("SELECT filename FROM migrations")
100
- return {row['filename'] for row in rows}
102
+ return {row['filename'] for row in rows}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqloader
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: A simple and extensible SQL migration and loader utility for Python.
5
5
  Home-page: https://github.com/horrible-gh/py_sqloader.git
6
6
  Author: horrible-gh
@@ -16,6 +16,7 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: LogAssist
18
18
  Requires-Dist: pymysql>=1.1.1
19
+ Requires-Dist: sqlparse>=0.4.0
19
20
  Provides-Extra: postgresql
20
21
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgresql"
21
22
  Provides-Extra: async-mysql
@@ -1,5 +1,6 @@
1
1
  LogAssist
2
2
  pymysql>=1.1.1
3
+ sqlparse>=0.4.0
3
4
 
4
5
  [all]
5
6
  psycopg2-binary>=2.9.0
@@ -128,4 +128,50 @@ class TestAutoRun:
128
128
  def test_auto_run_false_does_not_apply(self, db, migration_dir):
129
129
  m = DatabaseMigrator(db, migration_dir, auto_run=False)
130
130
  applied = m.get_applied_migrations()
131
- assert len(applied) == 0
131
+ assert len(applied) == 0
132
+
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # comment-only chunk (문제 1)
136
+ # ---------------------------------------------------------------------------
137
+
138
+ class TestCommentOnlyChunk:
139
+ def test_comment_only_file_does_not_raise(self, db):
140
+ """주석과 공백만 있는 청크는 실행 없이 건너뛰어야 한다."""
141
+ with tempfile.TemporaryDirectory() as tmpdir:
142
+ with open(os.path.join(tmpdir, "001_comments.sql"), "w") as f:
143
+ f.write(
144
+ "-- this is a comment\n"
145
+ "CREATE TABLE comment_test (id INTEGER PRIMARY KEY);\n"
146
+ "-- trailing comment\n"
147
+ )
148
+ m = DatabaseMigrator(db, tmpdir, auto_run=True)
149
+ applied = m.get_applied_migrations()
150
+ assert "001_comments.sql" in applied
151
+
152
+ rows = db.fetch_all(
153
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='comment_test'"
154
+ )
155
+ assert len(list(rows)) == 1
156
+
157
+
158
+ # ---------------------------------------------------------------------------
159
+ # multi-statement SQL file (문제 2 — split 경계 검증)
160
+ # ---------------------------------------------------------------------------
161
+
162
+ class TestMultiStatementFile:
163
+ def test_multiple_statements_in_one_file(self, db):
164
+ """세미콜론으로 구분된 여러 구문이 모두 올바르게 실행되어야 한다."""
165
+ with tempfile.TemporaryDirectory() as tmpdir:
166
+ with open(os.path.join(tmpdir, "001_multi.sql"), "w") as f:
167
+ f.write(
168
+ "CREATE TABLE multi_a (id INTEGER PRIMARY KEY);\n"
169
+ "CREATE TABLE multi_b (id INTEGER PRIMARY KEY);\n"
170
+ )
171
+ DatabaseMigrator(db, tmpdir, auto_run=True)
172
+
173
+ for table in ("multi_a", "multi_b"):
174
+ rows = db.fetch_all(
175
+ f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table}'"
176
+ )
177
+ assert len(list(rows)) == 1, f"Table {table} was not created"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes