dbhydra 2.3.0__py3-none-any.whl → 2.3.3__py3-none-any.whl
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.
- dbhydra/dbhydra_core.py +5 -0
- dbhydra/src/abstract_table.py +32 -8
- dbhydra/src/mysql_db.py +1 -0
- dbhydra/src/tables.py +11 -4
- dbhydra/tests/test_mysql_ddl.py +91 -0
- {dbhydra-2.3.0.dist-info → dbhydra-2.3.3.dist-info}/METADATA +1 -1
- {dbhydra-2.3.0.dist-info → dbhydra-2.3.3.dist-info}/RECORD +10 -9
- {dbhydra-2.3.0.dist-info → dbhydra-2.3.3.dist-info}/WHEEL +1 -1
- {dbhydra-2.3.0.dist-info → dbhydra-2.3.3.dist-info}/LICENSE +0 -0
- {dbhydra-2.3.0.dist-info → dbhydra-2.3.3.dist-info}/top_level.txt +0 -0
dbhydra/dbhydra_core.py
CHANGED
|
@@ -27,6 +27,11 @@ class Blob(str):
|
|
|
27
27
|
pass
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
class LongText(str):
|
|
31
|
+
"""Marker type for columns that should be mapped to LONGTEXT in SQL backends."""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
30
35
|
# dataframe - dictionary auxiliary functions
|
|
31
36
|
def df_to_dict(df, column1, column2):
|
|
32
37
|
dictionary = df.set_index(column1).to_dict()[column2]
|
dbhydra/src/abstract_table.py
CHANGED
|
@@ -134,18 +134,32 @@ class AbstractSelectable:
|
|
|
134
134
|
return(rows)
|
|
135
135
|
|
|
136
136
|
|
|
137
|
-
def select_all(self, debug_mode = False):
|
|
137
|
+
def select_all(self, debug_mode = False, limit: Optional[int] = None, offset: Optional[int] = None):
|
|
138
138
|
quote = self.db1.identifier_quote
|
|
139
139
|
all_cols_query = ""
|
|
140
140
|
for col in self.columns:
|
|
141
141
|
all_cols_query = all_cols_query + quote + col + quote + ","
|
|
142
142
|
if all_cols_query[-1] == ",":
|
|
143
143
|
all_cols_query = all_cols_query[:-1]
|
|
144
|
-
|
|
144
|
+
|
|
145
|
+
query = f"SELECT {all_cols_query} FROM {quote}{self.name}{quote}"
|
|
146
|
+
|
|
147
|
+
# Add LIMIT and OFFSET to the query
|
|
148
|
+
if limit is not None and limit > 0:
|
|
149
|
+
if offset is not None and offset > 0:
|
|
150
|
+
query += f" LIMIT {offset}, {limit}"
|
|
151
|
+
else:
|
|
152
|
+
query += f" LIMIT {limit}"
|
|
153
|
+
elif offset is not None and offset > 0:
|
|
154
|
+
# MySQL requires LIMIT when using OFFSET, use a large number for all remaining rows
|
|
155
|
+
query += f" LIMIT {offset}, 18446744073709551615"
|
|
156
|
+
|
|
157
|
+
query += ";"
|
|
158
|
+
list1 = self.select(query, debug_mode = debug_mode)
|
|
145
159
|
return (list1)
|
|
146
160
|
|
|
147
|
-
def select_to_df(self, debug_mode = False):
|
|
148
|
-
rows = self.select_all(debug_mode = debug_mode)
|
|
161
|
+
def select_to_df(self, debug_mode = False, limit: Optional[int] = None, offset: Optional[int] = None):
|
|
162
|
+
rows = self.select_all(debug_mode = debug_mode, limit = limit, offset = offset)
|
|
149
163
|
if self.query_building_enabled:
|
|
150
164
|
self.to_df()
|
|
151
165
|
df=None
|
|
@@ -269,10 +283,20 @@ class AbstractTable(AbstractJoinable, abc.ABC):
|
|
|
269
283
|
|
|
270
284
|
def update(self, variable_assign, where=None, debug_mode = False):
|
|
271
285
|
quote = self.db1.identifier_quote
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
286
|
+
|
|
287
|
+
assert "=" in variable_assign
|
|
288
|
+
assigned_variable,assigned_value=variable_assign.split("=").strip()
|
|
289
|
+
|
|
290
|
+
query = f"UPDATE {quote}{self.name}{quote} SET {quote}{assigned_variable}{quote} = {assigned_value}"
|
|
291
|
+
|
|
292
|
+
if where:
|
|
293
|
+
query += f" WHERE {where}"
|
|
294
|
+
|
|
295
|
+
#Old broken implementation it gives `` for the SQL syntax query
|
|
296
|
+
# if where is None:
|
|
297
|
+
# query = f"UPDATE {quote}{self.name}{quote} SET {quote}{variable_assign}{quote}"
|
|
298
|
+
# else:
|
|
299
|
+
# query = f"UPDATE {quote}{self.name}{quote} SET {quote}{variable_assign}{quote} WHERE {quote}{where}{quote}"
|
|
276
300
|
|
|
277
301
|
if debug_mode:
|
|
278
302
|
print(query)
|
dbhydra/src/mysql_db.py
CHANGED
dbhydra/src/tables.py
CHANGED
|
@@ -38,7 +38,6 @@ PYTHON_TO_MYSQL_DATA_MAPPING = {
|
|
|
38
38
|
|
|
39
39
|
def save_migration(function, *args, **kw): # decorator
|
|
40
40
|
def new_function(instance, *args, **kw):
|
|
41
|
-
print("TOTO TU")
|
|
42
41
|
print(instance)
|
|
43
42
|
print(*args)
|
|
44
43
|
command = function.__name__
|
|
@@ -63,9 +62,12 @@ def save_migration(function, *args, **kw): # decorator
|
|
|
63
62
|
print(migration_dict)
|
|
64
63
|
# TODO: add other methods
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
if hasattr(instance.db1, 'migrator'):
|
|
66
|
+
migrator = instance.db1.migrator
|
|
67
|
+
migrator.migration_list.append(migration_dict)
|
|
68
|
+
# migrator.migration_list_to_json()
|
|
69
|
+
else:
|
|
70
|
+
print(f"[save_migration] WARNING: db1 object of type {type(instance.db1)} has no 'migrator' attribute. Migration not saved.")
|
|
69
71
|
function(instance, *args, **kw)
|
|
70
72
|
|
|
71
73
|
return (new_function)
|
|
@@ -806,6 +808,11 @@ class MysqlTable(AbstractTable):
|
|
|
806
808
|
query += "'" + str(rows[k][j]) + "',"
|
|
807
809
|
elif "json" in self.types[j + start_index]:
|
|
808
810
|
query += f"'{rows[k][j]}', "
|
|
811
|
+
elif "text" in self.types[j + start_index]:
|
|
812
|
+
# Covers text, mediumtext, longtext
|
|
813
|
+
if replace_apostrophes:
|
|
814
|
+
rows[k][j] = str(rows[k][j]).replace("'", "")
|
|
815
|
+
query += "'" + str(rows[k][j]) + "',"
|
|
809
816
|
elif 'blob' in self.types[j + start_index]:
|
|
810
817
|
# Convert to hex to allow insertion into SQL query
|
|
811
818
|
hex_data = binascii.hexlify(rows[k][j]).decode('ascii')
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
##### DDL (data definition language) tests for MySQL #####
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import pytest
|
|
5
|
+
import random
|
|
6
|
+
import string
|
|
7
|
+
import dbhydra.dbhydra_core as dh
|
|
8
|
+
|
|
9
|
+
def random_table_name(prefix="test_table_"):
|
|
10
|
+
return prefix + ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
|
|
11
|
+
|
|
12
|
+
# Rename mysqldb fixture and all references to db1
|
|
13
|
+
@pytest.fixture(scope="module")
|
|
14
|
+
def db1():
|
|
15
|
+
# Get the directory of this test file, then go up one level to the project root
|
|
16
|
+
root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
17
|
+
config_path = os.path.join(root_dir, "config-mysql.ini")
|
|
18
|
+
return dh.MysqlDb(config_file=config_path)
|
|
19
|
+
|
|
20
|
+
@pytest.fixture(scope="function")
|
|
21
|
+
def temp_mysql_table(db1):
|
|
22
|
+
table_name = random_table_name()
|
|
23
|
+
columns = ["id", "name"]
|
|
24
|
+
types = ["int", "varchar(255)"]
|
|
25
|
+
table = dh.MysqlTable(db1, table_name, columns, types)
|
|
26
|
+
with db1.connect_to_db():
|
|
27
|
+
table.create()
|
|
28
|
+
table_dict = db1.generate_table_dict()
|
|
29
|
+
assert table_name in table_dict, f"Temp table {table_name} was not created!"
|
|
30
|
+
yield table
|
|
31
|
+
# Cleanup
|
|
32
|
+
try:
|
|
33
|
+
with db1.connect_to_db():
|
|
34
|
+
table.drop()
|
|
35
|
+
table_dict = db1.generate_table_dict()
|
|
36
|
+
assert table_name not in table_dict, f"Temp table {table_name} was not dropped!"
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print(f"[CLEANUP] Could not drop table {table_name}: {e}")
|
|
39
|
+
raise
|
|
40
|
+
|
|
41
|
+
def test_mysql_create_and_drop_table(db1):
|
|
42
|
+
table_name = random_table_name()
|
|
43
|
+
columns = ["id", "name"]
|
|
44
|
+
types = ["int", "varchar(255)"]
|
|
45
|
+
table = dh.MysqlTable(db1, table_name, columns, types)
|
|
46
|
+
with db1.connect_to_db():
|
|
47
|
+
table.create()
|
|
48
|
+
table_dict = db1.generate_table_dict()
|
|
49
|
+
assert table_name in table_dict, f"Table {table_name} was not created!"
|
|
50
|
+
table.drop()
|
|
51
|
+
table_dict = db1.generate_table_dict()
|
|
52
|
+
assert table_name not in table_dict, f"Table {table_name} was not dropped!"
|
|
53
|
+
|
|
54
|
+
def test_mysql_add_column(temp_mysql_table, db1):
|
|
55
|
+
table = temp_mysql_table
|
|
56
|
+
column = "age"
|
|
57
|
+
type = "int"
|
|
58
|
+
with db1.connect_to_db():
|
|
59
|
+
table.add_column(column, type)
|
|
60
|
+
df = table.select_to_df()
|
|
61
|
+
columns = df.columns.tolist()
|
|
62
|
+
assert column in columns, f"Column '{column}' was not added! Columns: {columns}"
|
|
63
|
+
|
|
64
|
+
def test_mysql_drop_column(temp_mysql_table, db1):
|
|
65
|
+
table = temp_mysql_table
|
|
66
|
+
column = "age"
|
|
67
|
+
type = "int"
|
|
68
|
+
with db1.connect_to_db():
|
|
69
|
+
table.add_column(column, type)
|
|
70
|
+
df = table.select_to_df()
|
|
71
|
+
columns = df.columns.tolist()
|
|
72
|
+
assert column in columns, f"Column '{column}' was not added! Columns: {columns}"
|
|
73
|
+
table.drop_column(column)
|
|
74
|
+
df = table.select_to_df()
|
|
75
|
+
columns = df.columns.tolist()
|
|
76
|
+
assert column not in columns, f"Column '{column}' was not dropped! Columns: {columns}"
|
|
77
|
+
|
|
78
|
+
def test_mysql_modify_column(temp_mysql_table, db1):
|
|
79
|
+
table = temp_mysql_table
|
|
80
|
+
column = "age"
|
|
81
|
+
type = "int"
|
|
82
|
+
with db1.connect_to_db():
|
|
83
|
+
table.add_column(column, type)
|
|
84
|
+
df = table.select_to_df()
|
|
85
|
+
columns = df.columns.tolist()
|
|
86
|
+
assert column in columns, f"Column '{column}' was not added! Columns: {columns}"
|
|
87
|
+
# Modify column type
|
|
88
|
+
type2 = "varchar(100)"
|
|
89
|
+
table.modify_column(column, type2)
|
|
90
|
+
types = table.get_all_types()
|
|
91
|
+
assert any("varchar" in t for t in types), f"Column '{column}' was not modified to varchar! Types: {types}"
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
dbhydra/__init__.py,sha256=DCocEeXf4QxdVlBRlNiFvuP5IZJ5aa77_DbUR-_4C14,65
|
|
2
|
-
dbhydra/dbhydra_core.py,sha256=
|
|
2
|
+
dbhydra/dbhydra_core.py,sha256=Rs_lR-RdHu2Yoq67-k68syQRri-hrrDYl-MvdkvWK-M,2659
|
|
3
3
|
dbhydra/test_migrator.py,sha256=e3Nnb2mCd3CfjhjSexNg1tXVJMjkl5cCoYcuhbfZ4pM,803
|
|
4
4
|
dbhydra/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
dbhydra/src/abstract_db.py,sha256=7Rv2XgdaKiQ5yBnU0EDh5uT8XbB3JI5Pis_in6iHn2c,7172
|
|
6
|
-
dbhydra/src/abstract_table.py,sha256=
|
|
6
|
+
dbhydra/src/abstract_table.py,sha256=uppvejYtc_5j9oFH-kk5EPRVqKs2-R6UkxIsfcDu6SY,20184
|
|
7
7
|
dbhydra/src/bigquery_db.py,sha256=77XsgvYbANlvYaJnuVve-kz-PNBx_CHoYCL-eYnA8e4,1834
|
|
8
8
|
dbhydra/src/migrator.py,sha256=QzaODEFfraD9_6HN_Osaidaj-nLYQryCYYWwJtUu3n8,18931
|
|
9
9
|
dbhydra/src/mongo_db.py,sha256=mP48zRjI7mXKpm45R8prroZI-Eo7JKf0KJqGX-oTy3w,1922
|
|
10
|
-
dbhydra/src/mysql_db.py,sha256=
|
|
10
|
+
dbhydra/src/mysql_db.py,sha256=p0ISoezMfFMXdtKNknPi0sZybdXHa8o5gHh4XtVwzs8,3612
|
|
11
11
|
dbhydra/src/postgres_db.py,sha256=L7MaBq_6ArwDSP_5LaEqK58oLxZ1X7FgIokcDOSB7wk,1805
|
|
12
12
|
dbhydra/src/sqlserver_db.py,sha256=9Xi3NAliqM79MTV8fpNQb0nWMH8Bqjl1leJSEqgyT94,3611
|
|
13
|
-
dbhydra/src/tables.py,sha256=
|
|
13
|
+
dbhydra/src/tables.py,sha256=obx3x3afBn7vY-J7GInMl8bKz-9IQ-glTPTvB7oH1_k,48092
|
|
14
14
|
dbhydra/src/xlsx_db.py,sha256=glFF-0dQK0GGinAqc2zvf9JQ9iAH9YkDAYd4Jb3oimQ,3616
|
|
15
15
|
dbhydra/src/errors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
dbhydra/src/errors/exceptions.py,sha256=LVpfbTd3NHfQIM-D5TFAU6hOZwGQ3b5DwFD4B6vtf2U,149
|
|
17
17
|
dbhydra/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
dbhydra/tests/test_cases.py,sha256=eAFGaHaIaab3md3HHm2_ryb_HHfObtcXDAEzLh4qWx8,508
|
|
19
19
|
dbhydra/tests/test_mongo.py,sha256=M8TD72M0iQAk7ZcLTWwLmcmmF_zwALnYEGTWjhQlq0s,1979
|
|
20
|
+
dbhydra/tests/test_mysql_ddl.py,sha256=-gncZE_HjTOAI1yxOn8u5TZkoqoRdShLMF469Trbb9U,3531
|
|
20
21
|
dbhydra/tests/test_sql.py,sha256=aPFXyA0jh8o9VG3B5f9fNz7qDbuVPZ9TcE2twn5dAeQ,3126
|
|
21
|
-
dbhydra-2.3.
|
|
22
|
-
dbhydra-2.3.
|
|
23
|
-
dbhydra-2.3.
|
|
24
|
-
dbhydra-2.3.
|
|
25
|
-
dbhydra-2.3.
|
|
22
|
+
dbhydra-2.3.3.dist-info/LICENSE,sha256=k49Yga8CP889JJaHlOpGFzr_be2nqMoep2chYeIDctk,1091
|
|
23
|
+
dbhydra-2.3.3.dist-info/METADATA,sha256=nW-c2bp-4KlDrB05tmKnowJVKAPobV6ugxQyi3tzELw,2298
|
|
24
|
+
dbhydra-2.3.3.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
25
|
+
dbhydra-2.3.3.dist-info/top_level.txt,sha256=oO4Gf1T8_txIsIlp11GI0k7PtBIMb9GRwb5ObF4MLVg,8
|
|
26
|
+
dbhydra-2.3.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|