sqloader 0.2.7__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.
- {sqloader-0.2.7/sqloader.egg-info → sqloader-0.2.9}/PKG-INFO +2 -1
- {sqloader-0.2.7 → sqloader-0.2.9}/pyproject.toml +2 -2
- {sqloader-0.2.7 → sqloader-0.2.9}/setup.py +2 -1
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/migrator.py +7 -5
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/mysql_async.py +3 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/postgresql.py +3 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/postgresql_async.py +3 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/sqlite3.py +3 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/sqlite3_async.py +3 -0
- {sqloader-0.2.7 → sqloader-0.2.9/sqloader.egg-info}/PKG-INFO +2 -1
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader.egg-info/requires.txt +1 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/tests/test_migrator.py +47 -1
- {sqloader-0.2.7 → sqloader-0.2.9}/LICENSE +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/README.md +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/setup.cfg +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/__init__.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/__main__.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/_async_prototype.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/_prototype.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/init.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/mysql.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader/sqloader.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader.egg-info/SOURCES.txt +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader.egg-info/dependency_links.txt +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/sqloader.egg-info/top_level.txt +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/tests/test_fetch_aliases.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/tests/test_mysql.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/tests/test_postgresql.py +0 -0
- {sqloader-0.2.7 → sqloader-0.2.9}/tests/test_sqlite.py +0 -0
- {sqloader-0.2.7 → 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.
|
|
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.
|
|
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.
|
|
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().
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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}
|
|
@@ -89,6 +89,9 @@ class AsyncMySqlWrapper(AsyncDatabasePrototype):
|
|
|
89
89
|
print(f"Last query: {query}")
|
|
90
90
|
raise
|
|
91
91
|
|
|
92
|
+
async def execute_query(self, query, params=None, commit=True):
|
|
93
|
+
return await self.execute(query, params, commit)
|
|
94
|
+
|
|
92
95
|
async def fetchone(self, query, params=None):
|
|
93
96
|
return await self.fetch_one(query, params)
|
|
94
97
|
|
|
@@ -68,6 +68,9 @@ class PostgreSQLWrapper(DatabasePrototype):
|
|
|
68
68
|
self.pool.putconn(conn)
|
|
69
69
|
query_semaphore.release()
|
|
70
70
|
|
|
71
|
+
def execute_query(self, query, params=None, commit=True):
|
|
72
|
+
return self.execute(query, params, commit)
|
|
73
|
+
|
|
71
74
|
def fetchone(self, query, params=None):
|
|
72
75
|
return self.fetch_one(query, params)
|
|
73
76
|
|
|
@@ -119,6 +119,9 @@ class AsyncPostgreSQLWrapper(AsyncDatabasePrototype):
|
|
|
119
119
|
print(f"Last query: {q}")
|
|
120
120
|
raise
|
|
121
121
|
|
|
122
|
+
async def execute_query(self, query, params=None, commit=True):
|
|
123
|
+
return await self.execute(query, params, commit)
|
|
124
|
+
|
|
122
125
|
async def fetchone(self, query, params=None):
|
|
123
126
|
return await self.fetch_one(query, params)
|
|
124
127
|
|
|
@@ -86,6 +86,9 @@ class SQLiteWrapper(DatabasePrototype):
|
|
|
86
86
|
else:
|
|
87
87
|
return self._execute_file(query, params, commit)
|
|
88
88
|
|
|
89
|
+
def execute_query(self, query, params=None, commit=True):
|
|
90
|
+
return self.execute(query, params, commit)
|
|
91
|
+
|
|
89
92
|
def fetchone(self, query, params=None):
|
|
90
93
|
return self.fetch_one(query, params)
|
|
91
94
|
|
|
@@ -94,6 +94,9 @@ class AsyncSQLiteWrapper(AsyncDatabasePrototype):
|
|
|
94
94
|
await self._conn.rollback()
|
|
95
95
|
raise
|
|
96
96
|
|
|
97
|
+
async def execute_query(self, query, params=None, commit=True):
|
|
98
|
+
return await self.execute(query, params, commit)
|
|
99
|
+
|
|
97
100
|
async def fetchone(self, query, params=None):
|
|
98
101
|
return await self.fetch_one(query, params)
|
|
99
102
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqloader
|
|
3
|
-
Version: 0.2.
|
|
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
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|