SQLPyHelper 0.1.2__py3-none-any.whl → 0.1.4__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.
- sqlpyhelper/__init__.py +8 -1
- sqlpyhelper/automation_utils.py +145 -0
- sqlpyhelper/cli.py +146 -0
- sqlpyhelper/db_helper.py +200 -51
- sqlpyhelper/py.typed +0 -0
- sqlpyhelper-0.1.4.dist-info/METADATA +238 -0
- sqlpyhelper-0.1.4.dist-info/RECORD +11 -0
- {sqlpyhelper-0.1.2.dist-info → sqlpyhelper-0.1.4.dist-info}/WHEEL +1 -1
- sqlpyhelper-0.1.4.dist-info/entry_points.txt +2 -0
- sqlpyhelper-0.1.2.dist-info/METADATA +0 -211
- sqlpyhelper-0.1.2.dist-info/RECORD +0 -7
- {sqlpyhelper-0.1.2.dist-info → sqlpyhelper-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {sqlpyhelper-0.1.2.dist-info → sqlpyhelper-0.1.4.dist-info}/top_level.txt +0 -0
sqlpyhelper/__init__.py
CHANGED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from sqlpyhelper.db_helper import SQLPyHelper
|
|
3
|
+
import subprocess
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AutomationUtils:
|
|
10
|
+
def __init__(self, db=None, **db_kwargs):
|
|
11
|
+
"""
|
|
12
|
+
Optionally accepts db instance or connection parameters like:
|
|
13
|
+
db_type, host, user, password, database, port, driver.
|
|
14
|
+
"""
|
|
15
|
+
self.db = db or SQLPyHelper(**db_kwargs)
|
|
16
|
+
|
|
17
|
+
def backup_database(self, target="local", tag="autobackup"):
|
|
18
|
+
"""
|
|
19
|
+
Backs up the active PostgreSQL database using pg_dump.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
target (str): Backup destination ("local" only for now).
|
|
23
|
+
tag (str): Custom tag for backup file naming.
|
|
24
|
+
"""
|
|
25
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
|
|
26
|
+
filename = f"{tag}_{timestamp}.sql"
|
|
27
|
+
backup_dir = "backups"
|
|
28
|
+
os.makedirs(backup_dir, exist_ok=True)
|
|
29
|
+
filepath = os.path.join(backup_dir, filename)
|
|
30
|
+
|
|
31
|
+
db_name = self.db.database
|
|
32
|
+
user = self.db.user
|
|
33
|
+
host = self.db.host or "localhost"
|
|
34
|
+
port = str(self.db.port or "5432")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
if self.db.db_type == "sqlite":
|
|
38
|
+
filename2 = f"{tag}_{timestamp}.db"
|
|
39
|
+
sqlite_filepath = os.path.join(backup_dir, filename2)
|
|
40
|
+
shutil.copy2(self.db.database, sqlite_filepath)
|
|
41
|
+
else:
|
|
42
|
+
print(f"📦 Backing up database to {filepath}")
|
|
43
|
+
subprocess.run(
|
|
44
|
+
[
|
|
45
|
+
"pg_dump",
|
|
46
|
+
"-h", host,
|
|
47
|
+
"-p", port,
|
|
48
|
+
"-U", user,
|
|
49
|
+
db_name,
|
|
50
|
+
"-F", "c",
|
|
51
|
+
"-f", filepath,
|
|
52
|
+
],
|
|
53
|
+
check=True,
|
|
54
|
+
shell=False,
|
|
55
|
+
)
|
|
56
|
+
except subprocess.CalledProcessError as e:
|
|
57
|
+
print("❌ Backup failed:", e)
|
|
58
|
+
|
|
59
|
+
def load_data_from_csv(self, file_path, table_name, if_exists="append"):
|
|
60
|
+
"""
|
|
61
|
+
Loads a CSV file into the specified database table.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
file_path (str): Path to the CSV file.
|
|
65
|
+
table_name (str): Destination table name in the database.
|
|
66
|
+
if_exists (str): 'append' or 'replace'. Default is 'append'.
|
|
67
|
+
"""
|
|
68
|
+
df = pd.read_csv(file_path)
|
|
69
|
+
|
|
70
|
+
if if_exists == "replace":
|
|
71
|
+
self.db.execute_query(f"DROP TABLE IF EXISTS {table_name}")
|
|
72
|
+
|
|
73
|
+
for _, row in df.iterrows():
|
|
74
|
+
self.db.insert_dynamic(table_name, row.to_dict())
|
|
75
|
+
|
|
76
|
+
def detect_missing_periods(self, table, entity_column, date_column):
|
|
77
|
+
"""
|
|
78
|
+
Flags rows where recurring time periods (e.g. monthly) are missing per entity.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
table (str): Table name to query.
|
|
82
|
+
entity_column (str): Column representing entity ID.
|
|
83
|
+
date_column (str): Column representing timestamp/date.
|
|
84
|
+
"""
|
|
85
|
+
if self.db.db_type == 'sqlite':
|
|
86
|
+
month_expr = f"strftime('%Y-%m', {date_column})"
|
|
87
|
+
else:
|
|
88
|
+
month_expr = f"DATE_TRUNC('month', {date_column})"
|
|
89
|
+
query = f"""
|
|
90
|
+
SELECT {entity_column}, COUNT(DISTINCT {month_expr}) AS recorded_months
|
|
91
|
+
FROM {table}
|
|
92
|
+
GROUP BY {entity_column}
|
|
93
|
+
HAVING COUNT(DISTINCT {month_expr}) < 12
|
|
94
|
+
"""
|
|
95
|
+
self.db.execute_query(query)
|
|
96
|
+
return self.db.fetch_all()
|
|
97
|
+
|
|
98
|
+
def aggregate_column(self, table, value_column, group_column=None, time_column=None):
|
|
99
|
+
"""
|
|
100
|
+
Computes sum of any value column grouped by entity or month.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
table (str): Table name.
|
|
104
|
+
value_column (str): Numeric column to aggregate.
|
|
105
|
+
group_column (str, optional): Entity or category to group by.
|
|
106
|
+
time_column (str, optional): Timestamp to extract month grouping.
|
|
107
|
+
"""
|
|
108
|
+
if self.db.db_type == 'sqlite':
|
|
109
|
+
month_expr = f"strftime('%Y-%m', {time_column})"
|
|
110
|
+
else:
|
|
111
|
+
month_expr = f"DATE_TRUNC('month', {time_column})"
|
|
112
|
+
|
|
113
|
+
if group_column and time_column:
|
|
114
|
+
query = f"""
|
|
115
|
+
SELECT {group_column}, {month_expr} AS month, SUM({value_column}) AS total
|
|
116
|
+
FROM {table}
|
|
117
|
+
GROUP BY {group_column}, month
|
|
118
|
+
ORDER BY month
|
|
119
|
+
"""
|
|
120
|
+
else:
|
|
121
|
+
query = f"SELECT SUM({value_column}) FROM {table}"
|
|
122
|
+
|
|
123
|
+
self.db.execute_query(query)
|
|
124
|
+
return self.db.fetch_all()
|
|
125
|
+
|
|
126
|
+
def detect_outliers(self, table, numeric_column, threshold=2):
|
|
127
|
+
"""
|
|
128
|
+
Detects statistical outliers based on deviation from mean.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
table (str): Table name.
|
|
132
|
+
numeric_column (str): Column to analyze.
|
|
133
|
+
threshold (int): Number of standard deviations from mean to flag as outlier.
|
|
134
|
+
"""
|
|
135
|
+
query = f"""
|
|
136
|
+
SELECT *, {numeric_column}
|
|
137
|
+
AS value FROM {table}
|
|
138
|
+
"""
|
|
139
|
+
self.db.execute_query(query)
|
|
140
|
+
data = pd.DataFrame(self.db.fetch_all(), columns=[desc[0] for desc in self.db.cursor.description])
|
|
141
|
+
|
|
142
|
+
mean_val = data["value"].mean()
|
|
143
|
+
std_val = data["value"].std()
|
|
144
|
+
outliers = data[abs(data["value"] - mean_val) > threshold * std_val]
|
|
145
|
+
return outliers.values.tolist()
|
sqlpyhelper/cli.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from sqlpyhelper.db_helper import SQLPyHelper
|
|
3
|
+
from sqlpyhelper.automation_utils import AutomationUtils
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.group()
|
|
7
|
+
def cli():
|
|
8
|
+
"""SQLPyHelper Command Line Interface"""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@cli.command()
|
|
13
|
+
@click.option('--db_type', help='Type of database (e.g., sqlite, postgres, mysql)')
|
|
14
|
+
@click.option('--host', help='Database host')
|
|
15
|
+
@click.option('--user', help='Username')
|
|
16
|
+
@click.option('--password', help='Password')
|
|
17
|
+
@click.option('--database', help='Database name or file')
|
|
18
|
+
@click.option('--query', required=True, help='SQL query to run')
|
|
19
|
+
def run_query(db_type, host, user, password, database, query):
|
|
20
|
+
"""Run a single SQL query and print results"""
|
|
21
|
+
db = SQLPyHelper(db_type=db_type, host=host, user=user, password=password, database=database)
|
|
22
|
+
results = db.execute_query(query)
|
|
23
|
+
for row in results:
|
|
24
|
+
click.echo(row)
|
|
25
|
+
db.close()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@cli.command()
|
|
29
|
+
@click.option('--db_type', required=True)
|
|
30
|
+
@click.option('--host')
|
|
31
|
+
@click.option('--user')
|
|
32
|
+
@click.option('--password')
|
|
33
|
+
@click.option('--database', required=True)
|
|
34
|
+
def interactive_shell(db_type, host, user, password, database):
|
|
35
|
+
"""Launch an interactive SQL shell"""
|
|
36
|
+
db = SQLPyHelper(db_type=db_type, host=host, user=user, password=password, database=database)
|
|
37
|
+
click.echo("Interactive shell started. Type your SQL query or 'exit'")
|
|
38
|
+
while True:
|
|
39
|
+
query = input("sqlpy> ")
|
|
40
|
+
if query.lower() in ("exit", "quit"):
|
|
41
|
+
break
|
|
42
|
+
try:
|
|
43
|
+
db.execute_query(query)
|
|
44
|
+
results = db.fetch_all()
|
|
45
|
+
for row in results:
|
|
46
|
+
click.echo(row)
|
|
47
|
+
except Exception as e:
|
|
48
|
+
click.echo(f"Error: {e}")
|
|
49
|
+
db.close()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@cli.command()
|
|
53
|
+
@click.option('--target', default="local", help="Backup destination")
|
|
54
|
+
@click.option('--tag', default="autobackup", help="Tag for backup file naming")
|
|
55
|
+
@click.option('--db-type')
|
|
56
|
+
@click.option('--host')
|
|
57
|
+
@click.option('--user')
|
|
58
|
+
@click.option('--password')
|
|
59
|
+
@click.option('--database')
|
|
60
|
+
@click.option('--port')
|
|
61
|
+
def backup(target, tag, db_type, host, user, password, database, port):
|
|
62
|
+
"""Create a timestamped backup of the connected database."""
|
|
63
|
+
utils = AutomationUtils(
|
|
64
|
+
db_type=db_type,
|
|
65
|
+
host=host,
|
|
66
|
+
user=user,
|
|
67
|
+
password=password,
|
|
68
|
+
database=database,
|
|
69
|
+
port=port
|
|
70
|
+
)
|
|
71
|
+
utils.backup_database(target=target, tag=tag)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@cli.command()
|
|
75
|
+
@click.option('--file', required=True, help="Path to CSV file")
|
|
76
|
+
@click.option('--table', required=True, help="Destination table")
|
|
77
|
+
@click.option('--if-exists', default="append", type=click.Choice(["append", "replace"]), help="What to do if table exists")
|
|
78
|
+
@click.option('--db-type')
|
|
79
|
+
@click.option('--host')
|
|
80
|
+
@click.option('--user')
|
|
81
|
+
@click.option('--password')
|
|
82
|
+
@click.option('--database')
|
|
83
|
+
@click.option('--port')
|
|
84
|
+
def load_data(file, table, if_exists, db_type, host, user, password, database, port):
|
|
85
|
+
"""Load data from CSV into database table."""
|
|
86
|
+
utils = AutomationUtils(db_type=db_type, host=host, user=user, password=password, database=database, port=port)
|
|
87
|
+
utils.load_data_from_csv(file, table, if_exists=if_exists)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@cli.command()
|
|
91
|
+
@click.option('--table', required=True)
|
|
92
|
+
@click.option('--entity-column', required=True)
|
|
93
|
+
@click.option('--date-column', required=True)
|
|
94
|
+
@click.option('--db-type')
|
|
95
|
+
@click.option('--host')
|
|
96
|
+
@click.option('--user')
|
|
97
|
+
@click.option('--password')
|
|
98
|
+
@click.option('--database')
|
|
99
|
+
@click.option('--port')
|
|
100
|
+
def detect_missing_periods(table, entity_column, date_column, db_type, host, user, password, database, port):
|
|
101
|
+
"""Flag entities with fewer than 12 months of activity."""
|
|
102
|
+
utils = AutomationUtils(db_type=db_type, host=host, user=user, password=password, database=database, port=port)
|
|
103
|
+
results = utils.detect_missing_periods(table, entity_column, date_column)
|
|
104
|
+
for row in results:
|
|
105
|
+
click.echo(row)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@cli.command()
|
|
109
|
+
@click.option('--table', required=True)
|
|
110
|
+
@click.option('--value-column', required=True)
|
|
111
|
+
@click.option('--group-column')
|
|
112
|
+
@click.option('--time-column')
|
|
113
|
+
@click.option('--db-type')
|
|
114
|
+
@click.option('--host')
|
|
115
|
+
@click.option('--user')
|
|
116
|
+
@click.option('--password')
|
|
117
|
+
@click.option('--database')
|
|
118
|
+
@click.option('--port')
|
|
119
|
+
def aggregate(table, value_column, group_column, time_column, db_type, host, user, password, database, port):
|
|
120
|
+
"""Aggregate numeric column optionally grouped by entity and time."""
|
|
121
|
+
utils = AutomationUtils(db_type=db_type, host=host, user=user, password=password, database=database, port=port)
|
|
122
|
+
results = utils.aggregate_column(table, value_column, group_column, time_column)
|
|
123
|
+
for row in results:
|
|
124
|
+
click.echo(row)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@cli.command()
|
|
128
|
+
@click.option('--table', required=True)
|
|
129
|
+
@click.option('--numeric-column', required=True)
|
|
130
|
+
@click.option('--threshold', default=2, type=int)
|
|
131
|
+
@click.option('--db-type')
|
|
132
|
+
@click.option('--host')
|
|
133
|
+
@click.option('--user')
|
|
134
|
+
@click.option('--password')
|
|
135
|
+
@click.option('--database')
|
|
136
|
+
@click.option('--port')
|
|
137
|
+
def detect_outliers(table, numeric_column, threshold, db_type, host, user, password, database, port):
|
|
138
|
+
"""Flag rows where values deviate statistically from average."""
|
|
139
|
+
utils = AutomationUtils(db_type=db_type, host=host, user=user, password=password, database=database, port=port)
|
|
140
|
+
results = utils.detect_outliers(table, numeric_column, threshold)
|
|
141
|
+
for row in results:
|
|
142
|
+
click.echo(row)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
cli()
|
sqlpyhelper/db_helper.py
CHANGED
|
@@ -1,27 +1,70 @@
|
|
|
1
1
|
import csv
|
|
2
2
|
from dotenv import load_dotenv
|
|
3
3
|
import os
|
|
4
|
+
import re
|
|
4
5
|
|
|
5
6
|
load_dotenv() # Load environment variables from .env file
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
def
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
def _validate_identifier(name: str) -> str:
|
|
10
|
+
"""
|
|
11
|
+
Validate a SQL identifier (table or column name).
|
|
12
|
+
Allows only alphanumeric characters and underscores.
|
|
13
|
+
Raises ValueError for anything else, preventing SQL injection via identifiers.
|
|
14
|
+
"""
|
|
15
|
+
if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', name):
|
|
16
|
+
raise ValueError(
|
|
17
|
+
f"Invalid SQL identifier: {name!r}. "
|
|
18
|
+
"Only letters, digits, and underscores are allowed."
|
|
19
|
+
)
|
|
20
|
+
return name
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SQLPyHelperError(Exception):
|
|
24
|
+
"""Base exception for SQLPyHelper errors."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ConnectionError(SQLPyHelperError):
|
|
28
|
+
"""Raised when a database connection fails."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class QueryError(SQLPyHelperError):
|
|
32
|
+
"""Raised when a query fails to execute."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BackupError(SQLPyHelperError):
|
|
36
|
+
"""Raised when a backup operation fails."""
|
|
12
37
|
|
|
13
38
|
|
|
14
39
|
class SQLPyHelper:
|
|
15
|
-
def __init__(self
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
self.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
def __init__(self, db_type=None, host=None, user=None, password=None,
|
|
41
|
+
database=None, driver=None, port=None, oracle_sid=None):
|
|
42
|
+
|
|
43
|
+
# Store original params so reconnect() can replay them
|
|
44
|
+
self._init_kwargs = {
|
|
45
|
+
"db_type": db_type,
|
|
46
|
+
"host": host,
|
|
47
|
+
"user": user,
|
|
48
|
+
"password": password,
|
|
49
|
+
"database": database,
|
|
50
|
+
"driver": driver,
|
|
51
|
+
"port": port,
|
|
52
|
+
"oracle_sid": oracle_sid,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
self.db_type = db_type or os.getenv("DB_TYPE").lower()
|
|
56
|
+
self.host = host or os.getenv("DB_HOST")
|
|
57
|
+
self.user = user or os.getenv("DB_USER")
|
|
58
|
+
self.password = password or os.getenv("DB_PASSWORD")
|
|
59
|
+
self.database = database or os.getenv("DB_NAME")
|
|
60
|
+
self.driver = driver or os.getenv("DB_DRIVER")
|
|
61
|
+
self.port = port or os.getenv("DB_PORT")
|
|
62
|
+
self.oracle_sid = oracle_sid or os.getenv("ORACLE_SID")
|
|
23
63
|
self.pool = None
|
|
24
64
|
|
|
65
|
+
if not self.db_type or not self.database:
|
|
66
|
+
raise ValueError("Missing required database configuration.")
|
|
67
|
+
|
|
25
68
|
if self.db_type == "sqlite":
|
|
26
69
|
import sqlite3
|
|
27
70
|
self.connection = sqlite3.connect(self.database)
|
|
@@ -47,6 +90,13 @@ class SQLPyHelper:
|
|
|
47
90
|
|
|
48
91
|
self.cursor = self.connection.cursor()
|
|
49
92
|
|
|
93
|
+
def __enter__(self):
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
97
|
+
self.close()
|
|
98
|
+
return False
|
|
99
|
+
|
|
50
100
|
def execute_query(self, query, params=None):
|
|
51
101
|
"""Executes a query with optional parameters"""
|
|
52
102
|
try:
|
|
@@ -56,24 +106,45 @@ class SQLPyHelper:
|
|
|
56
106
|
self.cursor.execute(query)
|
|
57
107
|
self.connection.commit()
|
|
58
108
|
except Exception as e:
|
|
59
|
-
|
|
109
|
+
if "server has gone away" in str(e): # Example check for MySQL lost connection
|
|
110
|
+
self.reconnect()
|
|
111
|
+
self.cursor.execute(query, params)
|
|
112
|
+
self.connection.commit()
|
|
113
|
+
else:
|
|
114
|
+
raise QueryError(f"Query failed: {e}") from e
|
|
60
115
|
|
|
61
116
|
def fetch_one(self):
|
|
62
|
-
|
|
117
|
+
"""Fetches a single row"""
|
|
118
|
+
try:
|
|
119
|
+
return self.cursor.fetchone()
|
|
120
|
+
except Exception as e:
|
|
121
|
+
raise QueryError(f"Failed to fetch row: {e}") from e
|
|
63
122
|
|
|
64
123
|
def fetch_all(self):
|
|
65
124
|
"""Fetches all rows from the last executed query"""
|
|
66
|
-
|
|
125
|
+
try:
|
|
126
|
+
return self.cursor.fetchall()
|
|
127
|
+
except Exception as e:
|
|
128
|
+
raise QueryError(f"Failed to fetch rows: {e}") from e
|
|
67
129
|
|
|
68
130
|
def fetch_by_param(self, table_name, column_name, value):
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
131
|
+
try:
|
|
132
|
+
table_name = _validate_identifier(table_name)
|
|
133
|
+
column_name = _validate_identifier(column_name)
|
|
134
|
+
placeholder = "?" if self.db_type == "sqlite" else "%s"
|
|
135
|
+
query = f"SELECT * FROM {table_name} WHERE {column_name} = {placeholder}"
|
|
136
|
+
self.cursor.execute(query, (value,))
|
|
137
|
+
return self.cursor.fetchall()
|
|
138
|
+
except Exception as e:
|
|
139
|
+
raise QueryError(f"Failed to fetch by param: {e}") from e
|
|
72
140
|
|
|
73
141
|
def close(self):
|
|
74
142
|
"""Closes the connection"""
|
|
75
|
-
|
|
76
|
-
|
|
143
|
+
try:
|
|
144
|
+
self.cursor.close()
|
|
145
|
+
self.connection.close()
|
|
146
|
+
except Exception as e:
|
|
147
|
+
raise ConnectionError(f"Failed to close connection: {e}") from e
|
|
77
148
|
|
|
78
149
|
def create_table(self, table_name, columns):
|
|
79
150
|
"""
|
|
@@ -81,9 +152,14 @@ class SQLPyHelper:
|
|
|
81
152
|
Example:
|
|
82
153
|
columns = {'id': 'INTEGER PRIMARY KEY', 'name': 'TEXT', 'age': 'INTEGER'}
|
|
83
154
|
"""
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
155
|
+
try:
|
|
156
|
+
table_name = _validate_identifier(table_name)
|
|
157
|
+
validated_cols = {_validate_identifier(col): dtype for col, dtype in columns.items()}
|
|
158
|
+
columns_def = ", ".join([f"{col} {dtype}" for col, dtype in validated_cols.items()])
|
|
159
|
+
query = f"CREATE TABLE IF NOT EXISTS {table_name} ({columns_def})"
|
|
160
|
+
self.execute_query(query)
|
|
161
|
+
except Exception as e:
|
|
162
|
+
raise QueryError(f"Failed to create table: {e}") from e
|
|
87
163
|
|
|
88
164
|
def insert_bulk(self, table_name, data):
|
|
89
165
|
"""
|
|
@@ -91,12 +167,19 @@ class SQLPyHelper:
|
|
|
91
167
|
Example:
|
|
92
168
|
data = [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]
|
|
93
169
|
"""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
170
|
+
try:
|
|
171
|
+
table_name = _validate_identifier(table_name)
|
|
172
|
+
col_names = [_validate_identifier(col) for col in data[0].keys()]
|
|
173
|
+
columns = ", ".join(col_names)
|
|
174
|
+
placeholder = "?" if self.db_type == "sqlite" else "%s"
|
|
175
|
+
placeholders = ", ".join([placeholder] * len(data[0]))
|
|
176
|
+
query = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})"
|
|
177
|
+
values = [tuple(row.values()) for row in data]
|
|
178
|
+
self.cursor.executemany(query, values)
|
|
179
|
+
self.connection.commit()
|
|
180
|
+
|
|
181
|
+
except Exception as e:
|
|
182
|
+
raise QueryError(f"Failed to insert bulk rows: {e}") from e
|
|
100
183
|
|
|
101
184
|
def backup_table(self, table_name, backup_file):
|
|
102
185
|
"""
|
|
@@ -104,32 +187,98 @@ class SQLPyHelper:
|
|
|
104
187
|
Example:
|
|
105
188
|
backup_table('users', 'users_backup.csv')
|
|
106
189
|
"""
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
190
|
+
try:
|
|
191
|
+
table_name = _validate_identifier(table_name)
|
|
192
|
+
query = f"SELECT * FROM {table_name}"
|
|
193
|
+
self.execute_query(query)
|
|
194
|
+
rows = self.fetch_all()
|
|
110
195
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
196
|
+
with open(backup_file, mode="w", newline="") as file:
|
|
197
|
+
writer = csv.writer(file)
|
|
198
|
+
writer.writerow([desc[0] for desc in self.cursor.description]) # Column headers
|
|
199
|
+
writer.writerows(rows)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
raise BackupError(f"Failed to backup table: {e}") from e
|
|
115
202
|
|
|
116
|
-
def
|
|
117
|
-
"""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
203
|
+
def setup_connection_pool(self, min_conn=1, max_conn=5, pool_size=5):
|
|
204
|
+
"""Sets up connection pooling based on the database type"""
|
|
205
|
+
try:
|
|
206
|
+
if self.db_type == "postgres":
|
|
207
|
+
from psycopg2 import pool
|
|
208
|
+
self.pool = pool.SimpleConnectionPool(min_conn, max_conn,
|
|
209
|
+
host=self.host, user=self.user,
|
|
210
|
+
password=self.password, dbname=self.database)
|
|
211
|
+
|
|
212
|
+
elif self.db_type == "mysql":
|
|
213
|
+
import mysql.connector.pooling
|
|
214
|
+
self.pool = mysql.connector.pooling.MySQLConnectionPool(pool_name="mypool",
|
|
215
|
+
pool_size=pool_size, host=self.host,
|
|
216
|
+
user=self.user, password=self.password,
|
|
217
|
+
database=self.database)
|
|
218
|
+
|
|
219
|
+
elif self.db_type == "sqlserver":
|
|
220
|
+
import pyodbc
|
|
221
|
+
self.pool = [
|
|
222
|
+
pyodbc.connect(f"DRIVER={self.driver};SERVER={self.host};DATABASE={self.database};"
|
|
223
|
+
f"UID={self.user};PWD={self.password};ConnectionPooling=Yes")
|
|
224
|
+
for _ in range(pool_size)
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
elif self.db_type == "oracle":
|
|
228
|
+
import cx_Oracle
|
|
229
|
+
oracle_port = os.getenv("ORACLE_DB_PORT", "1521") # Default Oracle port
|
|
230
|
+
dsn = cx_Oracle.makedsn(self.host, oracle_port, self.oracle_sid)
|
|
231
|
+
self.pool = cx_Oracle.SessionPool(user=self.user, password=self.password, dsn=dsn,
|
|
232
|
+
min=min_conn, max=max_conn, increment=1, threaded=True)
|
|
233
|
+
|
|
234
|
+
else:
|
|
235
|
+
raise ValueError(f"Connection pooling not supported for {self.db_type}")
|
|
236
|
+
except Exception as e:
|
|
237
|
+
raise ConnectionError(f"Failed to set up connection pool: {e}") from e
|
|
128
238
|
|
|
129
239
|
def get_connection_from_pool(self):
|
|
130
240
|
"""Fetches a connection from the pool."""
|
|
131
|
-
return self.pool.
|
|
241
|
+
return self.pool.get_connection()
|
|
132
242
|
|
|
133
|
-
def return_connection_to_pool(self,
|
|
243
|
+
def return_connection_to_pool(self, connection=None) -> None:
|
|
134
244
|
"""Returns a connection back to the pool."""
|
|
135
|
-
self.
|
|
245
|
+
conn = connection or self.connection
|
|
246
|
+
if self.pool is None:
|
|
247
|
+
raise RuntimeError("No connection pool initialised. Call setup_connection_pool() first.")
|
|
248
|
+
|
|
249
|
+
if self.db_type == "postgres":
|
|
250
|
+
self.pool.putconn(conn)
|
|
251
|
+
elif self.db_type == "mysql":
|
|
252
|
+
conn.close()
|
|
253
|
+
elif self.db_type == "oracle":
|
|
254
|
+
self.pool.release(conn)
|
|
255
|
+
else:
|
|
256
|
+
conn.close()
|
|
257
|
+
|
|
258
|
+
def reconnect(self):
|
|
259
|
+
"""Reconnects to the database if connection is lost"""
|
|
260
|
+
try:
|
|
261
|
+
self.connection.close()
|
|
262
|
+
self.__init__(**self._init_kwargs)
|
|
263
|
+
print("Database reconnected successfully.")
|
|
264
|
+
except Exception as e:
|
|
265
|
+
raise ConnectionError(f"Reconnection failed: {e}") from e
|
|
266
|
+
|
|
267
|
+
def begin_transaction(self):
|
|
268
|
+
self.execute_query("START TRANSACTION")
|
|
269
|
+
|
|
270
|
+
def rollback_transaction(self):
|
|
271
|
+
self.execute_query("ROLLBACK")
|
|
272
|
+
|
|
273
|
+
def insert_dynamic(self, table, data: dict):
|
|
274
|
+
"""
|
|
275
|
+
Dynamically constructs and executes an INSERT query with database-specific placeholders.
|
|
276
|
+
"""
|
|
277
|
+
table = _validate_identifier(table)
|
|
278
|
+
columns = ", ".join(_validate_identifier(col) for col in data.keys())
|
|
279
|
+
placeholders_style = "?" if self.db_type == "sqlite" else "%s"
|
|
280
|
+
placeholders = ", ".join([placeholders_style] * len(data))
|
|
281
|
+
values = tuple(data.values())
|
|
282
|
+
|
|
283
|
+
sql = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
|
|
284
|
+
self.execute_query(sql, values)
|
sqlpyhelper/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: SQLPyHelper
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: A simple SQL database helper package for Python.
|
|
5
|
+
Home-page: https://github.com/adebayopeter/sqlpyhelper
|
|
6
|
+
Author: Adebayo Olaonipekun
|
|
7
|
+
Author-email: pekunmi@live.com
|
|
8
|
+
Project-URL: Source, https://github.com/adebayopeter/sqlpyhelper
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/adebayopeter/sqlpyhelper/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/adebayopeter/sqlpyhelper/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: database,sql,sqlite,postgresql,mysql,sqlserver,oracle,db,query,helper
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Development Status :: 4 - Beta
|
|
19
|
+
Classifier: Intended Audience :: Developers
|
|
20
|
+
Classifier: Topic :: Database :: Database Engines/Servers
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: python-dotenv
|
|
27
|
+
Requires-Dist: click
|
|
28
|
+
Provides-Extra: postgres
|
|
29
|
+
Requires-Dist: psycopg2; extra == "postgres"
|
|
30
|
+
Provides-Extra: mysql
|
|
31
|
+
Requires-Dist: mysql-connector-python; extra == "mysql"
|
|
32
|
+
Provides-Extra: sqlserver
|
|
33
|
+
Requires-Dist: pyodbc; extra == "sqlserver"
|
|
34
|
+
Provides-Extra: oracle
|
|
35
|
+
Requires-Dist: cx_Oracle; extra == "oracle"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: psycopg2; extra == "all"
|
|
38
|
+
Requires-Dist: mysql-connector-python; extra == "all"
|
|
39
|
+
Requires-Dist: pyodbc; extra == "all"
|
|
40
|
+
Requires-Dist: cx_Oracle; extra == "all"
|
|
41
|
+
Dynamic: author
|
|
42
|
+
Dynamic: author-email
|
|
43
|
+
Dynamic: classifier
|
|
44
|
+
Dynamic: description
|
|
45
|
+
Dynamic: description-content-type
|
|
46
|
+
Dynamic: home-page
|
|
47
|
+
Dynamic: keywords
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
Dynamic: project-url
|
|
50
|
+
Dynamic: provides-extra
|
|
51
|
+
Dynamic: requires-dist
|
|
52
|
+
Dynamic: requires-python
|
|
53
|
+
Dynamic: summary
|
|
54
|
+
|
|
55
|
+
# SQLPyHelper
|
|
56
|
+
|
|
57
|
+
[](https://pypi.org/project/sqlpyhelper/)
|
|
58
|
+
[](https://pypi.org/project/sqlpyhelper/)
|
|
59
|
+
[](https://pypi.org/project/sqlpyhelper/)
|
|
60
|
+
[](https://github.com/adebayopeter/sqlpyhelper/blob/main/LICENSE)
|
|
61
|
+
[](https://github.com/adebayopeter/sqlpyhelper)
|
|
62
|
+
|
|
63
|
+
# 📌 SQLPyHelper v.0.1.4 🚀
|
|
64
|
+
|
|
65
|
+
A Python library for simplified database interactions across **SQLite, PostgreSQL, MySQL, SQL Server, and Oracle**. SQLPyHelper provides an intuitive API for handling queries, connection pooling, transactions, logging, and backups efficiently.
|
|
66
|
+
|
|
67
|
+
## 📖 Table of Contents
|
|
68
|
+
- [🚀 Features](#-features)
|
|
69
|
+
- [📦 Installation](#-installation)
|
|
70
|
+
- [⚙️ Setup Using `.env`](#️-setup-using-env)
|
|
71
|
+
- [🛠 Usage Examples](#-usage-examples)
|
|
72
|
+
- [SQLite Example](#sqlite-example)
|
|
73
|
+
- [PostgreSQL Example](#postgresql-example)
|
|
74
|
+
- [MySQL Example](#mysql-example)
|
|
75
|
+
- [SQL Server Example](#sql-server-example)
|
|
76
|
+
- [Oracle Example](#oracle-example)
|
|
77
|
+
- [📂 Project Structure](#-project-structure)
|
|
78
|
+
- [📌 Available Methods in SQLPyHelper](#-available-methods-in-sqlpyhelper)
|
|
79
|
+
- [🌍 Contributing](#-contributing)
|
|
80
|
+
- [☕ Support the Project](#-support-the-project)
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 🚀 Features in v0.1.3
|
|
85
|
+
- Unified connection pooling for multiple databases.
|
|
86
|
+
- Automatic reconnection for lost connections.
|
|
87
|
+
- Transaction support (BEGIN, ROLLBACK, COMMIT).
|
|
88
|
+
- Secure parameterized queries to prevent SQL injection.
|
|
89
|
+
- Bulk insertion & dynamic table creation.
|
|
90
|
+
- Logging & error handling for better debugging.
|
|
91
|
+
- CSV export & database backups.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
## 📦 Installation
|
|
95
|
+
#### Install via PyPI:
|
|
96
|
+
```sh
|
|
97
|
+
pip install sqlpyhelper
|
|
98
|
+
```
|
|
99
|
+
📌 Package on PyPI: [SQLPyHelper on PyPI](https://pypi.org/project/SQLPyHelper/)
|
|
100
|
+
|
|
101
|
+
For local development:
|
|
102
|
+
```sh
|
|
103
|
+
git clone https://github.com/adebayopeter/sqlpyhelper.git
|
|
104
|
+
cd sqlpyhelper
|
|
105
|
+
pip install -r requirements.txt
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## ⚙️ Setup Using `.env`
|
|
111
|
+
Create a `.env` file in your project root to manage database configurations securely by renaming `.env_example`.
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
# .env_example (Rename to .env)
|
|
115
|
+
DB_TYPE=postgres
|
|
116
|
+
DB_HOST=localhost
|
|
117
|
+
DB_USER=your_user
|
|
118
|
+
DB_PASSWORD=your_secure_password
|
|
119
|
+
DB_NAME=database_name
|
|
120
|
+
DB_DRIVER={ODBC Driver 17 for SQL Server}
|
|
121
|
+
ORACLE_SID=XE
|
|
122
|
+
ORACLE_DB_PORT=1521
|
|
123
|
+
```
|
|
124
|
+
### Loading `.env` in Code
|
|
125
|
+
```pycon
|
|
126
|
+
from dotenv import load_dotenv
|
|
127
|
+
import os
|
|
128
|
+
|
|
129
|
+
load_dotenv()
|
|
130
|
+
db_type = os.getenv("DB_TYPE")
|
|
131
|
+
host = os.getenv("DB_HOST")
|
|
132
|
+
user = os.getenv("DB_USER")
|
|
133
|
+
password = os.getenv("DB_PASSWORD")
|
|
134
|
+
database = os.getenv("DB_NAME")
|
|
135
|
+
```
|
|
136
|
+
---
|
|
137
|
+
## 🛠 Usage Examples
|
|
138
|
+
### Initialize SQLPyHelper
|
|
139
|
+
```pycon
|
|
140
|
+
from sqlpyhelper.db_helper import SQLPyHelper
|
|
141
|
+
db = SQLPyHelper() # Auto-detects database type based on `DB_TYPE`
|
|
142
|
+
```
|
|
143
|
+
### SQLite Example
|
|
144
|
+
```pycon
|
|
145
|
+
db.execute_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
|
|
146
|
+
db.execute_query("INSERT INTO users (name) VALUES (?)", ("Alice",))
|
|
147
|
+
print(db.fetch_all()) # Expected Output: [(1, 'Alice')]
|
|
148
|
+
db.close()
|
|
149
|
+
```
|
|
150
|
+
### PostgreSQL Example
|
|
151
|
+
```pycon
|
|
152
|
+
db.execute_query("CREATE TABLE customers (id SERIAL PRIMARY KEY, name TEXT)")
|
|
153
|
+
db.execute_query("INSERT INTO customers (name) VALUES (%s)", ("Bob",))
|
|
154
|
+
db.begin_transaction()
|
|
155
|
+
db.execute_query("DELETE FROM customers WHERE name=%s", ("Bob",))
|
|
156
|
+
db.rollback_transaction() # Undo delete
|
|
157
|
+
```
|
|
158
|
+
### MySQL Example
|
|
159
|
+
```pycon
|
|
160
|
+
db.execute_query("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100))")
|
|
161
|
+
db.execute_query("INSERT INTO users (id, name) VALUES (%s, %s)", (1, "Alice"))
|
|
162
|
+
print(db.fetch_by_param("users", "id", 1)) # Expected Output: [(1, 'Alice')]
|
|
163
|
+
db.close()
|
|
164
|
+
```
|
|
165
|
+
### SQL Server Example
|
|
166
|
+
```pycon
|
|
167
|
+
db.execute_query("CREATE TABLE orders (order_id INT PRIMARY KEY, item NVARCHAR(100))")
|
|
168
|
+
db.insert_bulk("orders", [{"order_id": 1, "item": "Laptop"}, {"order_id": 2, "item": "Mouse"}])
|
|
169
|
+
db.backup_table("orders", "orders_backup.csv") # Export data to CSV
|
|
170
|
+
```
|
|
171
|
+
### Oracle Example
|
|
172
|
+
```pycon
|
|
173
|
+
db.execute_query("CREATE TABLE employees (id NUMBER PRIMARY KEY, name VARCHAR2(100))")
|
|
174
|
+
db.execute_query("INSERT INTO employees (id, name) VALUES (:1, :2)", (1, "Charlie"))
|
|
175
|
+
db.setup_connection_pool(min_conn=2, max_conn=10) # Enable pooling for better performance
|
|
176
|
+
conn = db.get_connection_from_pool()
|
|
177
|
+
db.return_connection_to_pool(conn)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 📂 Project Structure
|
|
181
|
+
```
|
|
182
|
+
📦 SQLPyHelper/
|
|
183
|
+
├─ sqlpyhelper/
|
|
184
|
+
│ ├─ __init__.py
|
|
185
|
+
│ └─ db_helper.py
|
|
186
|
+
├─ tests/
|
|
187
|
+
│ └─ test_sqlpyhelper.py
|
|
188
|
+
├─ .env_example
|
|
189
|
+
├─ .gitignore
|
|
190
|
+
├─ setup.py
|
|
191
|
+
├─ README.md
|
|
192
|
+
└─ requirements.txt
|
|
193
|
+
```
|
|
194
|
+
---
|
|
195
|
+
## 📌 Available Methods in SQLPyHelper
|
|
196
|
+
|
|
197
|
+
| Method | Description |
|
|
198
|
+
|--------|-------------|
|
|
199
|
+
| `execute_query(query, params=None)` | Executes a SQL query with optional parameters. |
|
|
200
|
+
| `fetch_one()` | Retrieves a **single row** from query results. |
|
|
201
|
+
| `fetch_all()` | Retrieves **all rows** from query results. |
|
|
202
|
+
| `fetch_by_param(table, column, value)` | Fetches **rows dynamically** based on a given parameter. |
|
|
203
|
+
| `create_table(table_name, columns_dict)` | Creates a table dynamically with a dictionary format. |
|
|
204
|
+
| `insert_bulk(table, data_list)` | Inserts **multiple rows at once** efficiently. |
|
|
205
|
+
| `backup_table(table, backup_file.csv)` | Exports table data to **CSV format**. |
|
|
206
|
+
| `setup_connection_pool()` | Initializes **database connection pooling**. |
|
|
207
|
+
| `get_connection_from_pool()` | Fetches a connection from the pool. |
|
|
208
|
+
| `return_connection_to_pool(conn)` | Returns connection back to pool. |
|
|
209
|
+
| `begin_transaction()` | Begins an **explicit transaction**. |
|
|
210
|
+
| `rollback_transaction()` | Rolls back **uncommitted transactions**. |
|
|
211
|
+
| `close()` | Closes the database connection safely. |
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
## 🌍 Contributing
|
|
215
|
+
We welcome contributions from the **open-source community**! Follow these steps to contribute:
|
|
216
|
+
|
|
217
|
+
1. Fork the repo: [SQLPyHelper GitHub Repository](https://github.com/adebayopeter/sqlpyhelper)
|
|
218
|
+
2. Clone your fork:
|
|
219
|
+
```sh
|
|
220
|
+
git clone https://github.com/adebayopeter/sqlpyhelper.git
|
|
221
|
+
```
|
|
222
|
+
3. Create a new branch:
|
|
223
|
+
```sh
|
|
224
|
+
git checkout -b feature-new-functionality
|
|
225
|
+
```
|
|
226
|
+
4. Make changes, commit, and push:
|
|
227
|
+
```sh
|
|
228
|
+
git commit -m "Added new feature"
|
|
229
|
+
git push origin feature-new-functionality
|
|
230
|
+
```
|
|
231
|
+
5. Submit a Pull Request!
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
## ☕ Support the Project
|
|
235
|
+
|
|
236
|
+
If you find SQLPyHelper useful, consider buying me a coffee to support continued development!
|
|
237
|
+
Donate Here: [PayPal](https://paypal.me/adebayopeter?country.x=GB&locale.x=en_GB)
|
|
238
|
+
---
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
sqlpyhelper/__init__.py,sha256=8hopTneD8N4lddcHwD9y-PJHbqElOYw5_aw-HZSMvbo,169
|
|
2
|
+
sqlpyhelper/automation_utils.py,sha256=z-9yTdMuOMu1TVUwJ9Q6D3wTrCRh2K6DhCldJ_7MLdI,5227
|
|
3
|
+
sqlpyhelper/cli.py,sha256=fJHztOrzufPnQ6agFe7OgepSCNcPT0RcpZw8gg8BgWI,5322
|
|
4
|
+
sqlpyhelper/db_helper.py,sha256=Dtpuxa2s67-kitgNQLbAa5JF-AFoN5bC9mLfQ1o4V48,11514
|
|
5
|
+
sqlpyhelper/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
sqlpyhelper-0.1.4.dist-info/licenses/LICENSE,sha256=9XzXxZ_8mWFM9-2TlqyE3L69zvRf4VPY_xIzSj5iU-g,1076
|
|
7
|
+
sqlpyhelper-0.1.4.dist-info/METADATA,sha256=_gPYGlQmVm6dJS-1z-TYfvWIAKOS4PEsNVzVONUMxnw,8601
|
|
8
|
+
sqlpyhelper-0.1.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
9
|
+
sqlpyhelper-0.1.4.dist-info/entry_points.txt,sha256=uAzSqwkAbbJqQUKHlPNwOebTJVA0FqkOvn2CRP6xSz8,52
|
|
10
|
+
sqlpyhelper-0.1.4.dist-info/top_level.txt,sha256=FrLqTmqTGDa8jHnnf2ZVkYO-gFvLXX9QonpUCE6wKGs,12
|
|
11
|
+
sqlpyhelper-0.1.4.dist-info/RECORD,,
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: SQLPyHelper
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Summary: A simple SQL database helper package for Python.
|
|
5
|
-
Author: Adebayo Olaonipekun
|
|
6
|
-
Author-email: pekunmi@live.com
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
-
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: Topic :: Database :: Database Engines/Servers
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Requires-Python: >=3.8
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
Requires-Dist: psycopg2
|
|
17
|
-
Requires-Dist: mysql-connector-python
|
|
18
|
-
Requires-Dist: pyodbc
|
|
19
|
-
Requires-Dist: cx_Oracle
|
|
20
|
-
Requires-Dist: python-dotenv
|
|
21
|
-
Provides-Extra: mysql
|
|
22
|
-
Requires-Dist: mysql-connector-python; extra == "mysql"
|
|
23
|
-
Provides-Extra: postgres
|
|
24
|
-
Requires-Dist: psycopg2; extra == "postgres"
|
|
25
|
-
Provides-Extra: oracle
|
|
26
|
-
Requires-Dist: cx_Oracle; extra == "oracle"
|
|
27
|
-
Provides-Extra: sqlserver
|
|
28
|
-
Requires-Dist: pyodbc; extra == "sqlserver"
|
|
29
|
-
Provides-Extra: sqlite
|
|
30
|
-
Dynamic: author
|
|
31
|
-
Dynamic: author-email
|
|
32
|
-
Dynamic: classifier
|
|
33
|
-
Dynamic: description
|
|
34
|
-
Dynamic: description-content-type
|
|
35
|
-
Dynamic: license-file
|
|
36
|
-
Dynamic: provides-extra
|
|
37
|
-
Dynamic: requires-dist
|
|
38
|
-
Dynamic: requires-python
|
|
39
|
-
Dynamic: summary
|
|
40
|
-
|
|
41
|
-
# 📌 SQLPyHelper
|
|
42
|
-
|
|
43
|
-
A Python library for simplified database interactions across **SQLite, PostgreSQL, MySQL, SQL Server, and Oracle**. This open-source package provides an intuitive API for handling database operations efficiently.
|
|
44
|
-
|
|
45
|
-
## 📖 Table of Contents
|
|
46
|
-
- [🚀 Features](#-features)
|
|
47
|
-
- [📦 Installation](#-installation)
|
|
48
|
-
- [⚙️ Setup Using `.env`](#️-setup-using-env)
|
|
49
|
-
- [🛠 Usage Examples](#-usage-examples)
|
|
50
|
-
- [SQLite Example](#sqlite-example)
|
|
51
|
-
- [PostgreSQL Example](#postgresql-example)
|
|
52
|
-
- [MySQL Example](#mysql-example)
|
|
53
|
-
- [SQL Server Example](#sql-server-example)
|
|
54
|
-
- [Oracle Example](#oracle-example)
|
|
55
|
-
- [📂 Project Structure](#-project-structure)
|
|
56
|
-
- [🌍 Contributing](#-contributing)
|
|
57
|
-
- [☕ Support the Project](#-support-the-project)
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
## 🚀 Features
|
|
62
|
-
- **Unified Interface** for multiple databases
|
|
63
|
-
- **Connection pooling support** for PostgreSQL
|
|
64
|
-
- **Bulk insertion & dynamic table creation**
|
|
65
|
-
- **Automated logging & query execution**
|
|
66
|
-
- **CSV export & backup functionality**
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
## 📦 Installation
|
|
70
|
-
#### Install via PyPI:
|
|
71
|
-
```sh
|
|
72
|
-
pip install sqlpyhelper
|
|
73
|
-
```
|
|
74
|
-
📌 Package on PyPI: [SQLPyHelper on PyPI](https://pypi.org/project/SQLPyHelper/)
|
|
75
|
-
|
|
76
|
-
Or, if working from source:
|
|
77
|
-
```sh
|
|
78
|
-
git clone https://github.com/adebayopeter/sqlpyhelper.git
|
|
79
|
-
cd sqlpyhelper
|
|
80
|
-
pip install -r requirements.txt
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## ⚙️ Setup Using `.env`
|
|
86
|
-
Create a `.env` file in your project root to manage database configurations securely by renaming `.env_example`.
|
|
87
|
-
|
|
88
|
-
```sh
|
|
89
|
-
# .env_example (Rename to .env)
|
|
90
|
-
DB_TYPE=postgres
|
|
91
|
-
DB_HOST=localhost
|
|
92
|
-
DB_USER=your_user
|
|
93
|
-
DB_PASSWORD=your_secure_password
|
|
94
|
-
DB_NAME=database_name
|
|
95
|
-
DB_DRIVER={ODBC Driver 17 for SQL Server}
|
|
96
|
-
ORACLE_SID=XE
|
|
97
|
-
ORACLE_DB_PORT=1521
|
|
98
|
-
```
|
|
99
|
-
### Loading `.env` in Code
|
|
100
|
-
```pycon
|
|
101
|
-
from dotenv import load_dotenv
|
|
102
|
-
import os
|
|
103
|
-
|
|
104
|
-
load_dotenv()
|
|
105
|
-
db_type = os.getenv("DB_TYPE")
|
|
106
|
-
host = os.getenv("DB_HOST")
|
|
107
|
-
user = os.getenv("DB_USER")
|
|
108
|
-
password = os.getenv("DB_PASSWORD")
|
|
109
|
-
database = os.getenv("DB_NAME")
|
|
110
|
-
```
|
|
111
|
-
---
|
|
112
|
-
## 🛠 Usage Examples
|
|
113
|
-
### SQLite Example
|
|
114
|
-
```pycon
|
|
115
|
-
from sqlpyhelper.db_helper import SQLPyHelper
|
|
116
|
-
|
|
117
|
-
db = SQLPyHelper()
|
|
118
|
-
db.execute_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
|
|
119
|
-
db.execute_query("INSERT INTO users (name) VALUES (?)", ("Alice",))
|
|
120
|
-
db.execute_query("SELECT * FROM users")
|
|
121
|
-
print(db.fetch_all())
|
|
122
|
-
db.close()
|
|
123
|
-
```
|
|
124
|
-
### PostgreSQL Example
|
|
125
|
-
```pycon
|
|
126
|
-
db = SQLPyHelper()
|
|
127
|
-
db.execute_query("CREATE TABLE IF NOT EXISTS employees (id SERIAL PRIMARY KEY, name VARCHAR(100))")
|
|
128
|
-
db.execute_query("INSERT INTO employees (name) VALUES (%s)", ("Charlie",))
|
|
129
|
-
db.execute_query("SELECT * FROM employees")
|
|
130
|
-
print(db.fetch_all())
|
|
131
|
-
db.close()
|
|
132
|
-
```
|
|
133
|
-
### MySQL Example
|
|
134
|
-
```pycon
|
|
135
|
-
db = SQLPyHelper()
|
|
136
|
-
db.execute_query("CREATE TABLE IF NOT EXISTS customers (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100))")
|
|
137
|
-
db.execute_query("INSERT INTO customers (name) VALUES (%s)", ("David",))
|
|
138
|
-
db.execute_query("SELECT * FROM customers")
|
|
139
|
-
print(db.fetch_all())
|
|
140
|
-
db.close()
|
|
141
|
-
```
|
|
142
|
-
```pycon
|
|
143
|
-
db = SQLPyHelper()
|
|
144
|
-
|
|
145
|
-
# Fetch rows where customer_id = 3
|
|
146
|
-
customers = db.fetch_by_param("customers", "id", 3)
|
|
147
|
-
print(customers)
|
|
148
|
-
|
|
149
|
-
db.close()
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### SQL Server Example
|
|
153
|
-
```pycon
|
|
154
|
-
db = SQLPyHelper()
|
|
155
|
-
db.execute_query("CREATE TABLE IF NOT EXISTS orders (id INT PRIMARY KEY, product VARCHAR(100))")
|
|
156
|
-
db.execute_query("INSERT INTO orders (id, product) VALUES (?, ?)", (1, "Laptop"))
|
|
157
|
-
db.execute_query("SELECT * FROM orders")
|
|
158
|
-
print(db.fetch_all())
|
|
159
|
-
db.close()
|
|
160
|
-
```
|
|
161
|
-
### Oracle Example
|
|
162
|
-
```pycon
|
|
163
|
-
db = SQLPyHelper()
|
|
164
|
-
db.execute_query("CREATE TABLE employees (id NUMBER PRIMARY KEY, name VARCHAR2(100))")
|
|
165
|
-
db.execute_query("INSERT INTO employees (id, name) VALUES (:1, :2)", (1, "Emily"))
|
|
166
|
-
db.execute_query("SELECT * FROM employees")
|
|
167
|
-
print(db.fetch_all())
|
|
168
|
-
db.close()
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## 📂 Project Structure
|
|
172
|
-
```
|
|
173
|
-
📦 SQLPyHelper/
|
|
174
|
-
├─ sqlpyhelper/
|
|
175
|
-
│ ├─ __init__.py
|
|
176
|
-
│ └─ db_helper.py
|
|
177
|
-
├─ tests/
|
|
178
|
-
│ └─ test_sqlpyhelper.py
|
|
179
|
-
├─ .env_example
|
|
180
|
-
├─ .gitignore
|
|
181
|
-
├─ setup.py
|
|
182
|
-
├─ README.md
|
|
183
|
-
└─ requirements.txt
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
## 🌍 Contributing
|
|
188
|
-
We welcome contributions from the **open-source community**! Follow these steps to contribute:
|
|
189
|
-
|
|
190
|
-
1. Fork the repo: [SQLPyHelper GitHub Repository](https://github.com/adebayopeter/sqlpyhelper)
|
|
191
|
-
2. Clone your fork:
|
|
192
|
-
```sh
|
|
193
|
-
git clone https://github.com/adebayopeter/sqlpyhelper.git
|
|
194
|
-
```
|
|
195
|
-
3. Create a new branch:
|
|
196
|
-
```sh
|
|
197
|
-
git checkout -b feature-new-functionality
|
|
198
|
-
```
|
|
199
|
-
4. Make changes, commit, and push:
|
|
200
|
-
```sh
|
|
201
|
-
git commit -m "Added new feature"
|
|
202
|
-
git push origin feature-new-functionality
|
|
203
|
-
```
|
|
204
|
-
5. Submit a Pull Request!
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
## ☕ Support the Project
|
|
208
|
-
|
|
209
|
-
If you find SQLPyHelper useful, consider buying me a coffee to support continued development!
|
|
210
|
-
Donate Here: [PayPal](https://paypal.me/adebayopeter?country.x=GB&locale.x=en_GB)
|
|
211
|
-
---
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
sqlpyhelper/__init__.py,sha256=kJA4mgypZmLd3SnwwTNkVwB0yIJ8PKP4h9cjOCIPfTA,54
|
|
2
|
-
sqlpyhelper/db_helper.py,sha256=TT93q3f1EVjkgUSXA4iXCy9Uw5gdGuRbqH-Wl-8OjeI,5269
|
|
3
|
-
sqlpyhelper-0.1.2.dist-info/licenses/LICENSE,sha256=9XzXxZ_8mWFM9-2TlqyE3L69zvRf4VPY_xIzSj5iU-g,1076
|
|
4
|
-
sqlpyhelper-0.1.2.dist-info/METADATA,sha256=ATC92p-IcBVUFEBHKXqqMBkYC524FsdQpxDVBmhQ3JI,5954
|
|
5
|
-
sqlpyhelper-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
-
sqlpyhelper-0.1.2.dist-info/top_level.txt,sha256=FrLqTmqTGDa8jHnnf2ZVkYO-gFvLXX9QonpUCE6wKGs,12
|
|
7
|
-
sqlpyhelper-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|