libs.utils.dbutil

libs/utils/dbutil.py

  1"""
  2libs/utils/dbutil.py
  3"""
  4
  5import logging
  6import sqlite3
  7from contextlib import closing
  8from importlib.resources import files
  9from typing import TYPE_CHECKING, Any, Optional, Union
 10
 11import libs.global_value as g
 12
 13if TYPE_CHECKING:
 14    from pathlib import Path
 15
 16
 17def connection(database_path: Union["Path", str]) -> sqlite3.Connection:
 18    """
 19    DB接続共通処理
 20
 21    Args:
 22        database_path (Union[Path, str]): データベースファイル
 23
 24    Returns:
 25        sqlite3.Connection: オブジェクト
 26
 27    """
 28    conn = sqlite3.connect(
 29        database=f"file:{database_path}",
 30        # detect_types=sqlite3.PARSE_DECLTYPES,
 31        uri=True,
 32    )
 33    conn.row_factory = sqlite3.Row
 34
 35    return conn
 36
 37
 38def execute(query: str, params: Optional[dict[str, Any]] = None) -> list[dict[str, Any]]:
 39    """
 40    クエリ実行
 41
 42    Args:
 43        query (str): 実行クエリ
 44        params (Optional[dict[str,Any]]): プレースホルダ
 45
 46    Returns:
 47        list[dict[str, Any]]: 実行結果
 48
 49    """
 50    if not params:
 51        params = g.params.placeholder()
 52
 53    ret: list[dict[str, Any]] = []
 54
 55    g.params.update_from_dict(params)
 56    query = g.params.query_modification(query)
 57
 58    if g.args.verbose & 0x01:
 59        print(f">>> params={g.params.placeholder()}")
 60        print(f">>> SQL -> {g.cfg.setting.database_file}\n{g.params.named_query(query)}")
 61
 62    with closing(connection(g.cfg.setting.database_file)) as conn:
 63        try:
 64            rows = conn.execute(query, params)
 65            if conn.total_changes:
 66                conn.commit()
 67        except sqlite3.OperationalError as err:
 68            logging.error("OperationalError: %s", err)
 69            logging.error("params=%s", g.params.placeholder())
 70            logging.error("query: %s", g.params.named_query(query))
 71            return ret
 72
 73        for row in rows.fetchall():
 74            ret.append(dict(row))
 75
 76        if g.args.verbose & 0x02:
 77            print("=" * 80)
 78            print(ret)
 79
 80    return ret
 81
 82
 83def query(keyword: str) -> str:
 84    """
 85    SQLクエリを返す
 86
 87    Args:
 88        keyword (str): SQL選択キーワード
 89
 90    Raises:
 91        ValueError: 未定義のキーワード
 92
 93    Returns:
 94        str: SQL文
 95
 96    """
 97    sql_tables: dict[str, str] = {
 98        # テーブル作成
 99        "CREATE_TABLE_MEMBER": "table/member.sql",
100        "CREATE_TABLE_ALIAS": "table/alias.sql",
101        "CREATE_TABLE_TEAM": "table/team.sql",
102        "CREATE_TABLE_RESULT": "table/result.sql",
103        "CREATE_TABLE_REMARKS": "table/remarks.sql",
104        "CREATE_TABLE_WORDS": "table/words.sql",
105        "CREATE_TABLE_RULE": "table/rule.sql",
106        # VIEW作成
107        "CREATE_VIEW_INDIVIDUAL_RESULTS": "view/individual_results.sql",
108        "CREATE_VIEW_GAME_RESULTS": "view/game_results.sql",
109        "CREATE_VIEW_GAME_INFO": "view/game_info.sql",
110        "CREATE_VIEW_REGULATIONS": "view/regulations.sql",
111        # INDEX作成
112        "CREATE_INDEX": "table/index.sql",
113        # 情報取得
114        "GAME_INFO": "game.info.sql",
115        "RESULTS_INFO": "results.info.sql",
116        "MEMBER_INFO": "member.info.sql",
117        "TEAM_INFO": "team.info.sql",
118        "REMARKS_INFO": "remarks.info.sql",
119        "RECORD_INFO": "record.info.sql",
120        # 集計
121        "SUMMARY_GAMEDATA": "summary/gamedata.sql",
122        "SUMMARY_DETAILS": "summary/details.sql",
123        "SUMMARY_DETAILS2": "summary/details2.sql",
124        "SUMMARY_RESULTS": "summary/results.sql",
125        "SUMMARY_TOTAL": "summary/total.sql",
126        "SUMMARY_VERSUS_MATRIX": "summary/versus_matrix.sql",
127        "RANKING_RESULTS": "ranking/results.sql",
128        "RANKING_RATINGS": "ranking/ratings.sql",
129        "REPORT_PERSONAL_DATA": "report/personal_data.sql",
130        "REPORT_COUNT_DATA": "report/count_data.sql",
131        "REPORT_MONTHLY": "report/monthly.sql",
132        "REPORT_RESULTS_LIST": "report/results_list.sql",
133        "REPORT_WINNER": "report/winner.sql",
134        "REPORT_MATRIX_TABLE": "report/matrix_table.sql",
135        "REPORT_COUNT_MOVING": "report/count_moving.sql",
136        #
137        "RESULT_INSERT": "general/result_insert.sql",
138        "RESULT_UPDATE": "general/result_update.sql",
139        "RESULT_DELETE": "general/result_delete.sql",
140        #
141        "REMARKS_SELECT": "general/remarks_select.sql",
142        "REMARKS_INSERT": "general/remarks_insert.sql",
143        "REMARKS_DELETE_ALL": "general/remarks_delete_all.sql",
144        "REMARKS_DELETE_ONE": "general/remarks_delete_one.sql",
145        "REMARKS_DELETE_COMPAR": "general/remarks_delete_compar.sql",
146        #
147        "WORDS_INSERT": "general/words_insert.sql",
148        #
149        "SELECT_ALL_RESULTS": "general/select_all_results.sql",
150    }
151
152    if query_path := sql_tables.get(keyword):
153        with open(str(files("files.queries").joinpath(query_path)), "r", encoding="utf-8") as queryfile:
154            return str(queryfile.read()).strip()
155    else:
156        raise ValueError(f"Unknown keyword: {keyword}")
157
158
159def table_info(conn: sqlite3.Connection, table_name: str) -> dict[str, Any]:
160    """
161    テーブルのスキーマを取得して辞書で返す
162
163    Args:
164        conn (sqlite3.Connection): オブジェクト
165        table_name (str): テーブル名
166
167    Returns:
168        dict[str, Any]: スキーマ
169
170    """
171    rows = conn.execute(f"pragma table_info('{table_name}');")
172    schema = {row["name"]: dict(row) for row in rows.fetchall()}
173
174    return schema
def connection(database_path: pathlib.Path | str) -> sqlite3.Connection:
18def connection(database_path: Union["Path", str]) -> sqlite3.Connection:
19    """
20    DB接続共通処理
21
22    Args:
23        database_path (Union[Path, str]): データベースファイル
24
25    Returns:
26        sqlite3.Connection: オブジェクト
27
28    """
29    conn = sqlite3.connect(
30        database=f"file:{database_path}",
31        # detect_types=sqlite3.PARSE_DECLTYPES,
32        uri=True,
33    )
34    conn.row_factory = sqlite3.Row
35
36    return conn

DB接続共通処理

Arguments:
  • database_path (Union[Path, str]): データベースファイル
Returns:

sqlite3.Connection: オブジェクト

def execute( query: str, params: dict[str, Any] | None = None) -> list[dict[str, typing.Any]]:
39def execute(query: str, params: Optional[dict[str, Any]] = None) -> list[dict[str, Any]]:
40    """
41    クエリ実行
42
43    Args:
44        query (str): 実行クエリ
45        params (Optional[dict[str,Any]]): プレースホルダ
46
47    Returns:
48        list[dict[str, Any]]: 実行結果
49
50    """
51    if not params:
52        params = g.params.placeholder()
53
54    ret: list[dict[str, Any]] = []
55
56    g.params.update_from_dict(params)
57    query = g.params.query_modification(query)
58
59    if g.args.verbose & 0x01:
60        print(f">>> params={g.params.placeholder()}")
61        print(f">>> SQL -> {g.cfg.setting.database_file}\n{g.params.named_query(query)}")
62
63    with closing(connection(g.cfg.setting.database_file)) as conn:
64        try:
65            rows = conn.execute(query, params)
66            if conn.total_changes:
67                conn.commit()
68        except sqlite3.OperationalError as err:
69            logging.error("OperationalError: %s", err)
70            logging.error("params=%s", g.params.placeholder())
71            logging.error("query: %s", g.params.named_query(query))
72            return ret
73
74        for row in rows.fetchall():
75            ret.append(dict(row))
76
77        if g.args.verbose & 0x02:
78            print("=" * 80)
79            print(ret)
80
81    return ret

クエリ実行

Arguments:
  • query (str): 実行クエリ
  • params (Optional[dict[str,Any]]): プレースホルダ
Returns:

list[dict[str, Any]]: 実行結果

def query(keyword: str) -> str:
 84def query(keyword: str) -> str:
 85    """
 86    SQLクエリを返す
 87
 88    Args:
 89        keyword (str): SQL選択キーワード
 90
 91    Raises:
 92        ValueError: 未定義のキーワード
 93
 94    Returns:
 95        str: SQL文
 96
 97    """
 98    sql_tables: dict[str, str] = {
 99        # テーブル作成
100        "CREATE_TABLE_MEMBER": "table/member.sql",
101        "CREATE_TABLE_ALIAS": "table/alias.sql",
102        "CREATE_TABLE_TEAM": "table/team.sql",
103        "CREATE_TABLE_RESULT": "table/result.sql",
104        "CREATE_TABLE_REMARKS": "table/remarks.sql",
105        "CREATE_TABLE_WORDS": "table/words.sql",
106        "CREATE_TABLE_RULE": "table/rule.sql",
107        # VIEW作成
108        "CREATE_VIEW_INDIVIDUAL_RESULTS": "view/individual_results.sql",
109        "CREATE_VIEW_GAME_RESULTS": "view/game_results.sql",
110        "CREATE_VIEW_GAME_INFO": "view/game_info.sql",
111        "CREATE_VIEW_REGULATIONS": "view/regulations.sql",
112        # INDEX作成
113        "CREATE_INDEX": "table/index.sql",
114        # 情報取得
115        "GAME_INFO": "game.info.sql",
116        "RESULTS_INFO": "results.info.sql",
117        "MEMBER_INFO": "member.info.sql",
118        "TEAM_INFO": "team.info.sql",
119        "REMARKS_INFO": "remarks.info.sql",
120        "RECORD_INFO": "record.info.sql",
121        # 集計
122        "SUMMARY_GAMEDATA": "summary/gamedata.sql",
123        "SUMMARY_DETAILS": "summary/details.sql",
124        "SUMMARY_DETAILS2": "summary/details2.sql",
125        "SUMMARY_RESULTS": "summary/results.sql",
126        "SUMMARY_TOTAL": "summary/total.sql",
127        "SUMMARY_VERSUS_MATRIX": "summary/versus_matrix.sql",
128        "RANKING_RESULTS": "ranking/results.sql",
129        "RANKING_RATINGS": "ranking/ratings.sql",
130        "REPORT_PERSONAL_DATA": "report/personal_data.sql",
131        "REPORT_COUNT_DATA": "report/count_data.sql",
132        "REPORT_MONTHLY": "report/monthly.sql",
133        "REPORT_RESULTS_LIST": "report/results_list.sql",
134        "REPORT_WINNER": "report/winner.sql",
135        "REPORT_MATRIX_TABLE": "report/matrix_table.sql",
136        "REPORT_COUNT_MOVING": "report/count_moving.sql",
137        #
138        "RESULT_INSERT": "general/result_insert.sql",
139        "RESULT_UPDATE": "general/result_update.sql",
140        "RESULT_DELETE": "general/result_delete.sql",
141        #
142        "REMARKS_SELECT": "general/remarks_select.sql",
143        "REMARKS_INSERT": "general/remarks_insert.sql",
144        "REMARKS_DELETE_ALL": "general/remarks_delete_all.sql",
145        "REMARKS_DELETE_ONE": "general/remarks_delete_one.sql",
146        "REMARKS_DELETE_COMPAR": "general/remarks_delete_compar.sql",
147        #
148        "WORDS_INSERT": "general/words_insert.sql",
149        #
150        "SELECT_ALL_RESULTS": "general/select_all_results.sql",
151    }
152
153    if query_path := sql_tables.get(keyword):
154        with open(str(files("files.queries").joinpath(query_path)), "r", encoding="utf-8") as queryfile:
155            return str(queryfile.read()).strip()
156    else:
157        raise ValueError(f"Unknown keyword: {keyword}")

SQLクエリを返す

Arguments:
  • keyword (str): SQL選択キーワード
Raises:
  • ValueError: 未定義のキーワード
Returns:

str: SQL文

def table_info(conn: sqlite3.Connection, table_name: str) -> dict[str, typing.Any]:
160def table_info(conn: sqlite3.Connection, table_name: str) -> dict[str, Any]:
161    """
162    テーブルのスキーマを取得して辞書で返す
163
164    Args:
165        conn (sqlite3.Connection): オブジェクト
166        table_name (str): テーブル名
167
168    Returns:
169        dict[str, Any]: スキーマ
170
171    """
172    rows = conn.execute(f"pragma table_info('{table_name}');")
173    schema = {row["name"]: dict(row) for row in rows.fetchall()}
174
175    return schema

テーブルのスキーマを取得して辞書で返す

Arguments:
  • conn (sqlite3.Connection): オブジェクト
  • table_name (str): テーブル名
Returns:

dict[str, Any]: スキーマ