reydb 1.1.48__py3-none-any.whl → 1.1.50__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.
reydb/rexec.py CHANGED
@@ -2,336 +2,973 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  """
5
- @Time : 2022-12-05 14:10:02
5
+ @Time : 2025-09-22 22:12:33
6
6
  @Author : Rey
7
7
  @Contact : reyxbo@163.com
8
- @Explain : Database path methods.
8
+ @Explain : Execute methods.
9
9
  """
10
10
 
11
11
 
12
- from typing import Any, Self
13
- from reykit.rbase import throw
14
- from reykit.rtable import TableData
12
+ from typing import Any, Literal
13
+ from collections.abc import Iterable, Generator, Container
14
+ from enum import EnumType
15
+ from sqlalchemy import text as sqlalchemy_text
16
+ from sqlalchemy.sql.elements import TextClause
17
+ from reykit.rbase import throw, get_first_notnone
18
+ from reykit.rdata import FunctionGenerator, to_json
19
+ from reykit.rmonkey import monkey_sqlalchemy_result_more_fetch, monkey_sqlalchemy_row_index_field
20
+ from reykit.rre import findall
21
+ from reykit.rstdout import echo
22
+ from reykit.rtable import TableData, Table
23
+ from reykit.rwrap import wrap_runtime
15
24
 
16
25
  from .rbase import DatabaseBase
17
26
  from .rconn import DatabaseConnection
18
- from .rdb import Database, Result
19
27
 
20
28
 
21
29
  __all__ = (
22
- 'DatabaseExecute',
30
+ 'Result',
31
+ 'DatabaseExecute'
23
32
  )
24
33
 
25
34
 
35
+ # Monkey path.
36
+ Result_ = monkey_sqlalchemy_result_more_fetch()
37
+ Result = Result_
38
+ monkey_sqlalchemy_row_index_field()
39
+
40
+
26
41
  class DatabaseExecute(DatabaseBase):
27
42
  """
28
43
  Database execute type.
29
-
30
- Examples
31
- --------
32
- Select.
33
- >>> field = ['id', 'value']
34
- >>> where = '`id` = ids'
35
- >>> ids = (1, 2)
36
- >>> result = DatabaseExecute.database.table(field, where, ids=ids)
37
-
38
- Insert.
39
- >>> data = [{'id': 1}, {'id': 2}]
40
- >>> duplicate = 'ignore'
41
- >>> result = DatabaseExecute.database.table + data
42
- >>> result = DatabaseExecute.database.table + (data, duplicate)
43
- >>> result = DatabaseExecute.database.table + {'data': data, 'duplicate': duplicate}
44
-
45
- Update.
46
- >>> data = [{'name': 'a', 'id': 1}, {'name': 'b', 'id': 2}]
47
- >>> where_fields = 'id'
48
- >>> result = DatabaseExecute.database.table & data
49
- >>> result = DatabaseExecute.database.table & (data, where_fields)
50
- >>> result = DatabaseExecute.database.table & {'data': data, 'where_fields': where_fields}
51
-
52
- Delete.
53
- >>> where = '`id` IN (1, 2)'
54
- >>> report = True
55
- >>> result = DatabaseExecute.database.table - where
56
- >>> result = DatabaseExecute.database.table - (where, report)
57
- >>> result = DatabaseExecute.database.table - {'where': where, 'report': report}
58
-
59
- Copy.
60
- >>> where = '`id` IN (1, 2)'
61
- >>> limit = 1
62
- >>> result = DatabaseExecute.database.table * where
63
- >>> result = DatabaseExecute.database.table * (where, limit)
64
- >>> result = DatabaseExecute.database.table * {'where': where, 'limit': limit}
65
-
66
- Exist.
67
- >>> where = '`id` IN (1, 2)'
68
- >>> report = True
69
- >>> result = where in DatabaseExecute.database.table
70
- >>> result = (where, report) in DatabaseExecute.database.table
71
- >>> result = {'where': where, 'report': report} in DatabaseExecute.database.table
72
-
73
- Count.
74
- >>> result = len(DatabaseExecute.database.table)
75
-
76
- Default database.
77
- >>> engine = Database(**server, database)
78
- >>> result = engine.exe.table()
79
44
  """
80
45
 
81
46
 
82
- def __init__(self, rdatabase: Database | DatabaseConnection) -> None:
47
+ def __init__(self, dbconn: DatabaseConnection) -> None:
83
48
  """
84
49
  Build instance attributes.
85
50
 
86
51
  Parameters
87
52
  ----------
88
- rdatabase : Database or DatabaseConnection instance.
53
+ dbconn : DatabaseConnection instance.
89
54
  """
90
55
 
91
- # Set parameter.
92
- self._rdatabase = rdatabase
93
- self._path: list[str] = []
56
+ # Build.
57
+ self.dbconn = dbconn
94
58
 
95
59
 
96
- def __getattr__(self, name: str) -> Self:
60
+ def execute(
61
+ self,
62
+ sql: str | TextClause,
63
+ data: TableData | None = None,
64
+ report: bool | None = None,
65
+ **kwdata: Any
66
+ ) -> Result:
97
67
  """
98
- Set database name or set table name.
68
+ Execute SQL.
99
69
 
100
70
  Parameters
101
71
  ----------
102
- name : Database name or table name.
72
+ sql : SQL in method `sqlalchemy.text` format, or `TextClause` object.
73
+ data : Data set for filling.
74
+ report : Whether report SQL execute information.
75
+ - `None`: Use attribute `default_report`.
76
+ - `bool`: Use this value.
77
+ kwdata : Keyword parameters for filling.
103
78
 
104
79
  Returns
105
80
  -------
106
- Self.
81
+ Result object.
107
82
  """
108
83
 
109
- # Check.
110
- if len(self._path) not in (0, 1):
111
- throw(AssertionError, self._path)
84
+ # Handle parameter.
85
+ report = get_first_notnone(report, self.dbconn.db.default_report)
86
+ sql = self.handle_sql(sql)
87
+ if data is None:
88
+ if kwdata == {}:
89
+ data = []
90
+ else:
91
+ data = [kwdata]
92
+ else:
93
+ data_table = Table(data)
94
+ data = data_table.to_table()
95
+ for row in data:
96
+ row.update(kwdata)
97
+ data = self.handle_data(data, sql)
98
+
99
+ # Execute.
100
+
101
+ ## Report.
102
+ if report:
103
+ execute = wrap_runtime(self.conn.execute, to_return=True)
104
+ result, report_runtime, *_ = execute(sql, data)
105
+ report_info = (
106
+ f'{report_runtime}\n'
107
+ f'Row Count: {result.rowcount}'
108
+ )
109
+ sqls = [
110
+ sql_part.strip()
111
+ for sql_part in sql.text.split(';')
112
+ if sql_part != ''
113
+ ]
114
+ if data == []:
115
+ echo(report_info, *sqls, title='SQL')
116
+ else:
117
+ echo(report_info, *sqls, data, title='SQL')
118
+
119
+ ## Not report.
120
+ else:
121
+ result = self.dbconn.conn.execute(sql, data)
122
+
123
+ # Automatic commit.
124
+ if self.dbconn.autocommit:
125
+ self.dbconn.conn.commit()
112
126
 
113
- # Set.
114
- self._path.append(name)
115
-
116
- return self
117
-
118
-
119
- @property
120
- def __get_path(self) -> tuple[str, str]:
121
- """
122
- Get database name and table name.
123
-
124
- Returns
125
- -------
126
- Database name and table name.
127
- """
127
+ return result
128
128
 
129
- # Get.
130
- path_len = len(self._path)
131
- match path_len:
132
- case 1:
133
- database = self._rdatabase.database
134
- table = self._path[0]
135
- case 2:
136
- database = self._path[0]
137
- table = self._path[1]
138
- case _:
139
- throw(AssertionError, path_len)
140
129
 
141
- return database, table
130
+ __call__ = execute
142
131
 
143
132
 
144
- def __call__(
133
+ def select(
145
134
  self,
146
- *args: Any,
147
- **kwargs: Any
135
+ table: str,
136
+ fields: str | Iterable[str] | None = None,
137
+ where: str | None = None,
138
+ group: str | None = None,
139
+ having: str | None = None,
140
+ order: str | None = None,
141
+ limit: int | str | tuple[int, int] | None = None,
142
+ report: bool | None = None,
143
+ **kwdata: Any
148
144
  ) -> Result:
149
145
  """
150
- Select the data of table in the datebase.
146
+ Execute select SQL.
151
147
 
152
148
  Parameters
153
149
  ----------
154
- args : Position arguments.
155
- kwargs : Keyword arguments.
150
+ table : Table name.
151
+ fields : Select clause content.
152
+ - `None`: Is `SELECT *`.
153
+ - `str`: Join as `SELECT str`.
154
+ - `Iterable[str]`, Join as `SELECT ``str``: ...`.
155
+ `str and first character is ':'`: Use this syntax.
156
+ `str`: Use this field.
157
+ where : Clause `WHERE` content, join as `WHERE str`.
158
+ group : Clause `GROUP BY` content, join as `GROUP BY str`.
159
+ having : Clause `HAVING` content, join as `HAVING str`.
160
+ order : Clause `ORDER BY` content, join as `ORDER BY str`.
161
+ limit : Clause `LIMIT` content.
162
+ - `int | str`: Join as `LIMIT int/str`.
163
+ - `tuple[int, int]`: Join as `LIMIT int, int`.
164
+ report : Whether report SQL execute information.
165
+ - `None`, Use attribute `report_execute_info`: of object `ROption`.
166
+ - `int`: Use this value.
167
+ kwdata : Keyword parameters for filling.
156
168
 
157
169
  Returns
158
170
  -------
159
171
  Result object.
172
+
173
+ Examples
174
+ --------
175
+ Parameter `fields`.
176
+ >>> fields = ['id', ':`id` + 1 AS `id_`']
177
+ >>> result = Database.execute.select('table', fields)
178
+ >>> print(result.to_table())
179
+ [{'id': 1, 'id_': 2}, ...]
180
+
181
+ Parameter `kwdata`.
182
+ >>> fields = '`id`, `id` + :value AS `id_`'
183
+ >>> result = Database.execute.select('table', fields, value=1)
184
+ >>> print(result.to_table())
185
+ [{'id': 1, 'id_': 2}, ...]
160
186
  """
161
187
 
162
- # Selete.
163
- result = self._rdatabase.execute_select(self.__get_path, *args, **kwargs)
188
+ # Generate SQL.
189
+ sql_list = []
190
+
191
+ ## Part 'SELECT' syntax.
192
+ if fields is None:
193
+ fields = '*'
194
+ elif type(fields) != str:
195
+ fields = ', '.join(
196
+ [
197
+ field[1:]
198
+ if (
199
+ field.startswith(':')
200
+ and field != ':'
201
+ )
202
+ else f'`{field}`'
203
+ for field in fields
204
+ ]
205
+ )
206
+ sql_select = f'SELECT {fields}'
207
+ sql_list.append(sql_select)
208
+
209
+ ## Part 'FROM' syntax.
210
+ sql_from = f'FROM `{self.dbconn.db.database}`.`{table}`'
211
+ sql_list.append(sql_from)
212
+
213
+ ## Part 'WHERE' syntax.
214
+ if where is not None:
215
+ sql_where = f'WHERE {where}'
216
+ sql_list.append(sql_where)
217
+
218
+ ## Part 'GROUP BY' syntax.
219
+ if group is not None:
220
+ sql_group = f'GROUP BY {group}'
221
+ sql_list.append(sql_group)
222
+
223
+ ## Part 'GROUP BY' syntax.
224
+ if having is not None:
225
+ sql_having = f'HAVING {having}'
226
+ sql_list.append(sql_having)
227
+
228
+ ## Part 'ORDER BY' syntax.
229
+ if order is not None:
230
+ sql_order = f'ORDER BY {order}'
231
+ sql_list.append(sql_order)
232
+
233
+ ## Part 'LIMIT' syntax.
234
+ if limit is not None:
235
+ if type(limit) in (str, int):
236
+ sql_limit = f'LIMIT {limit}'
237
+ else:
238
+ if len(limit) == 2:
239
+ sql_limit = f'LIMIT {limit[0]}, {limit[1]}'
240
+ else:
241
+ throw(ValueError, limit)
242
+ sql_list.append(sql_limit)
243
+
244
+ ## Join sql part.
245
+ sql = '\n'.join(sql_list)
246
+
247
+ # Execute SQL.
248
+ result = self.execute(sql, report=report, **kwdata)
164
249
 
165
250
  return result
166
251
 
167
252
 
168
- def __add__(
253
+ def insert(
169
254
  self,
170
- params: tuple | dict | TableData
255
+ table: str,
256
+ data: TableData,
257
+ duplicate: Literal['ignore', 'update'] | Container[str] | None = None,
258
+ report: bool | None = None,
259
+ **kwdata: Any
171
260
  ) -> Result:
172
261
  """
173
262
  Insert the data of table in the datebase.
174
263
 
175
264
  Parameters
176
265
  ----------
177
- params : Insert parameters.
178
- - `tuple`: Enter parameters in '(path, *params)' format.
179
- - `dict`: Enter parameters in '(path, **params)' format.
180
- - `TableData`: Enter parameters in '(path, params)' format.
266
+ table : Table name.
267
+ data : Insert data.
268
+ duplicate : Handle method when constraint error.
269
+ - `None`: Not handled.
270
+ - `ignore`: Use `UPDATE IGNORE INTO` clause.
271
+ - `update`: Use `ON DUPLICATE KEY UPDATE` clause and update all fields.
272
+ - `Container[str]`: Use `ON DUPLICATE KEY UPDATE` clause and update this fields.
273
+ report : Whether report SQL execute information.
274
+ - `None`, Use attribute `report_execute_info`: of object `ROption`.
275
+ - `int`: Use this value.
276
+ kwdata : Keyword parameters for filling.
277
+ - `str and first character is ':'`: Use this syntax.
278
+ - `Any`: Use this value.
181
279
 
182
280
  Returns
183
281
  -------
184
282
  Result object.
283
+
284
+ Examples
285
+ --------
286
+ Parameter `data` and `kwdata`.
287
+ >>> data = [{'key': 'a'}, {'key': 'b'}]
288
+ >>> kwdata = {'value1': 1, 'value2': ':(SELECT 2)'}
289
+ >>> result = Database.execute.insert('table', data, **kwdata)
290
+ >>> print(result.rowcount)
291
+ 2
292
+ >>> result = Database.execute.select('table')
293
+ >>> print(result.to_table())
294
+ [{'key': 'a', 'value1': 1, 'value2': 2}, {'key': 'b', 'value1': 1, 'value2': 2}]
185
295
  """
186
296
 
187
- # Insert.
188
- match params:
189
- case tuple():
190
- result = self._rdatabase.execute_insert(self.__get_path, *params)
191
- case dict():
192
- result = self._rdatabase.execute_insert(self.__get_path, **params)
297
+ # Handle parameter.
298
+
299
+ ## Data.
300
+ data_table = Table(data)
301
+ data = data_table.to_table()
302
+
303
+ ## Check.
304
+ if data in ([], [{}]):
305
+ throw(ValueError, data)
306
+
307
+ ## Keyword data.
308
+ kwdata_method = {}
309
+ kwdata_replace = {}
310
+ for key, value in kwdata.items():
311
+ if (
312
+ type(value) == str
313
+ and value.startswith(':')
314
+ and value != ':'
315
+ ):
316
+ kwdata_method[key] = value[1:]
317
+ else:
318
+ kwdata_replace[key] = value
319
+
320
+ # Generate SQL.
321
+
322
+ ## Part 'fields' syntax.
323
+ fields_replace = {
324
+ field
325
+ for row in data
326
+ for field in row
327
+ }
328
+ fields_replace = {
329
+ field
330
+ for field in fields_replace
331
+ if field not in kwdata
332
+ }
333
+ sql_fields_list = (
334
+ *kwdata_method,
335
+ *kwdata_replace,
336
+ *fields_replace
337
+ )
338
+ sql_fields = ', '.join(
339
+ [
340
+ f'`{field}`'
341
+ for field in sql_fields_list
342
+ ]
343
+ )
344
+
345
+ ## Part 'values' syntax.
346
+ sql_values_list = (
347
+ *kwdata_method.values(),
348
+ *[
349
+ ':' + field
350
+ for field in (
351
+ *kwdata_replace,
352
+ *fields_replace
353
+ )
354
+ ]
355
+ )
356
+ sql_values = ', '.join(sql_values_list)
357
+
358
+ ## Join sql part.
359
+ match duplicate:
360
+
361
+ ### Not handle.
362
+ case None:
363
+ sql = (
364
+ f'INSERT INTO `{self.dbconn.db.database}`.`{table}`({sql_fields})\n'
365
+ f'VALUES({sql_values})'
366
+ )
367
+
368
+ ### Ignore.
369
+ case 'ignore':
370
+ sql = (
371
+ f'INSERT IGNORE INTO `{self.dbconn.db.database}`.`{table}`({sql_fields})\n'
372
+ f'VALUES({sql_values})'
373
+ )
374
+
375
+ ### Update.
193
376
  case _:
194
- result = self._rdatabase.execute_insert(self.__get_path, params)
377
+ sql_fields_list_update = sql_fields_list
378
+ if duplicate != 'update':
379
+ sql_fields_list_update = [
380
+ field
381
+ for field in sql_fields_list
382
+ if field in duplicate
383
+ ]
384
+ update_content = ',\n '.join(
385
+ [
386
+ f'`{field}` = VALUES(`{field}`)'
387
+ for field in sql_fields_list_update
388
+ ]
389
+ )
390
+ sql = (
391
+ f'INSERT INTO `{self.dbconn.db.database}`.`{table}`({sql_fields})\n'
392
+ f'VALUES({sql_values})\n'
393
+ 'ON DUPLICATE KEY UPDATE\n'
394
+ f' {update_content}'
395
+ )
396
+
397
+ # Execute SQL.
398
+ result = self.execute(sql, data, report, **kwdata_replace)
195
399
 
196
400
  return result
197
401
 
198
402
 
199
- def __and__(
403
+ def update(
200
404
  self,
201
- params: tuple | dict | TableData
405
+ table: str,
406
+ data: TableData,
407
+ where_fields: str | Iterable[str] | None = None,
408
+ report: bool | None = None,
409
+ **kwdata: Any
202
410
  ) -> Result:
203
411
  """
204
412
  Update the data of table in the datebase.
205
413
 
206
414
  Parameters
207
415
  ----------
208
- params : Update parameters.
209
- - `tuple`: Enter parameters in '(path, *params)' format.
210
- - `dict`: Enter parameters in '(path, **params)' format.
211
- - `TableData`: Enter parameters in '(path, params)' format.
416
+ table : Table name.
417
+ data : Update data, clause `SET` and `WHERE` and `ORDER BY` and `LIMIT` content.
418
+ - `Key`: Table field.
419
+ `literal['order']`: Clause `ORDER BY` content, join as `ORDER BY str`.
420
+ `literal['limit']`: Clause `LIMIT` content, join as `LIMIT str`.
421
+ `Other`: Clause `SET` and `WHERE` content.
422
+ - `Value`: Table value.
423
+ `list | tuple`: Join as `field IN :str`.
424
+ `Any`: Join as `field = :str`.
425
+ where_fields : Clause `WHERE` content fields.
426
+ - `None`: The first key value pair of each item is judged.
427
+ - `str`: This key value pair of each item is judged.
428
+ - `Iterable[str]`: Multiple judged, `and`: relationship.
429
+ report : Whether report SQL execute information.
430
+ - `None`, Use attribute `report_execute_info`: of object `ROption`.
431
+ - `int`: Use this value.
432
+ kwdata : Keyword parameters for filling.
433
+ - `str and first character is ':'`: Use this syntax.
434
+ - `Any`: Use this value.
212
435
 
213
436
  Returns
214
437
  -------
215
438
  Result object.
439
+
440
+ Examples
441
+ --------
442
+ Parameter `data` and `kwdata`.
443
+ >>> data = [{'key': 'a'}, {'key': 'b'}]
444
+ >>> kwdata = {'value': 1, 'name': ':`key`'}
445
+ >>> result = Database.execute.update('table', data, **kwdata)
446
+ >>> print(result.rowcount)
447
+ 2
448
+ >>> result = Database.execute.select('table')
449
+ >>> print(result.to_table())
450
+ [{'key': 'a', 'value': 1, 'name': 'a'}, {'key': 'b', 'value': 1, 'name': 'b'}]
216
451
  """
217
452
 
218
- # Update.
219
- match params:
220
- case tuple():
221
- result = self._rdatabase.execute_update(self.__get_path, *params)
222
- case dict():
223
- result = self._rdatabase.execute_update(self.__get_path, **params)
224
- case _:
225
- result = self._rdatabase.execute_update(self.__get_path, params)
453
+ # Handle parameter.
454
+
455
+ ## Data.
456
+ data_table = Table(data)
457
+ data = data_table.to_table()
458
+
459
+ ## Check.
460
+ if data in ([], [{}]):
461
+ throw(ValueError, data)
462
+
463
+ ## Keyword data.
464
+ kwdata_method = {}
465
+ kwdata_replace = {}
466
+ for key, value in kwdata.items():
467
+ if (
468
+ type(value) == str
469
+ and value.startswith(':')
470
+ and value != ':'
471
+ ):
472
+ kwdata_method[key] = value[1:]
473
+ else:
474
+ kwdata_replace[key] = value
475
+ sql_set_list_kwdata = [
476
+ f'`{key}` = {value}'
477
+ for key, value in kwdata_method.items()
478
+ ]
479
+ sql_set_list_kwdata.extend(
480
+ [
481
+ f'`{key}` = :{key}'
482
+ for key in kwdata_replace
483
+ ]
484
+ )
485
+
486
+ # Generate SQL.
487
+ data_flatten = kwdata_replace
488
+ if where_fields is None:
489
+ no_where = True
490
+ else:
491
+ no_where = False
492
+ if type(where_fields) == str:
493
+ where_fields = [where_fields]
494
+ sqls_list = []
495
+ sql_update = f'UPDATE `{self.dbconn.db.database}`.`{table}`'
496
+ for index, row in enumerate(data):
497
+ sql_parts = [sql_update]
498
+ for key, value in row.items():
499
+ if key in ('order', 'limit'):
500
+ continue
501
+ index_key = f'{index}_{key}'
502
+ data_flatten[index_key] = value
503
+ if no_where:
504
+ for key in row:
505
+ where_fields = [key]
506
+ break
507
+
508
+ ## Part 'SET' syntax.
509
+ sql_set_list = sql_set_list_kwdata.copy()
510
+ sql_set_list.extend(
511
+ [
512
+ f'`{key}` = :{index}_{key}'
513
+ for key in row
514
+ if (
515
+ key not in where_fields
516
+ and key not in kwdata
517
+ and key not in ('order', 'limit')
518
+ )
519
+ ]
520
+ )
521
+ sql_set = 'SET ' + ',\n '.join(sql_set_list)
522
+ sql_parts.append(sql_set)
523
+
524
+ ## Part 'WHERE' syntax.
525
+ sql_where_list = []
526
+ for field in where_fields:
527
+ index_field = f'{index}_{field}'
528
+ index_value = data_flatten[index_field]
529
+ if type(index_value) in (list, tuple):
530
+ sql_where_part = f'`{field}` IN :{index_field}'
531
+ else:
532
+ sql_where_part = f'`{field}` = :{index_field}'
533
+ sql_where_list.append(sql_where_part)
534
+ sql_where = 'WHERE ' + '\n AND '.join(sql_where_list)
535
+ sql_parts.append(sql_where)
536
+
537
+ ## Part 'ORDER BY' syntax.
538
+ order = row.get('order')
539
+ if order is not None:
540
+ sql_order = f'ORDER BY {order}'
541
+ sql_parts.append(sql_order)
542
+
543
+ ## Part 'LIMIT' syntax.
544
+ limit = row.get('limit')
545
+ if limit is not None:
546
+ sql_limit = f'LIMIT {limit}'
547
+ sql_parts.append(sql_limit)
548
+
549
+ ## Join sql part.
550
+ sql = '\n'.join(sql_parts)
551
+ sqls_list.append(sql)
552
+
553
+ ## Join sqls.
554
+ sqls = ';\n'.join(sqls_list)
555
+
556
+ # Execute SQL.
557
+ result = self.execute(sqls, data_flatten, report)
226
558
 
227
559
  return result
228
560
 
229
561
 
230
- def __sub__(
562
+ def delete(
231
563
  self,
232
- params: tuple | dict | str
564
+ table: str,
565
+ where: str | None = None,
566
+ order: str | None = None,
567
+ limit: int | str | None = None,
568
+ report: bool | None = None,
569
+ **kwdata: Any
233
570
  ) -> Result:
234
571
  """
235
572
  Delete the data of table in the datebase.
236
573
 
237
574
  Parameters
238
575
  ----------
239
- params : Update parameters.
240
- - `tuple`: Enter parameters in '(path, *params)' format.
241
- - `dict`: Enter parameters in '(path, **params)' format.
242
- - `str`: Enter parameters in '(path, params)' format.
576
+ table : Table name.
577
+ where : Clause `WHERE` content, join as `WHERE str`.
578
+ order : Clause `ORDER BY` content, join as `ORDER BY str`.
579
+ limit : Clause `LIMIT` content, join as `LIMIT int/str`.
580
+ report : Whether report SQL execute information.
581
+ - `None`, Use attribute `report_execute_info`: of object `ROption`.
582
+ - `int`: Use this value.
583
+ kwdata : Keyword parameters for filling.
243
584
 
244
585
  Returns
245
586
  -------
246
587
  Result object.
588
+
589
+ Examples
590
+ --------
591
+ Parameter `where` and `kwdata`.
592
+ >>> where = '`id` IN :ids'
593
+ >>> ids = (1, 2)
594
+ >>> result = Database.execute.delete('table', where, ids=ids)
595
+ >>> print(result.rowcount)
596
+ 2
247
597
  """
248
598
 
249
- # Update.
250
- match params:
251
- case tuple():
252
- result = self._rdatabase.execute_delete(self.__get_path, *params)
253
- case dict():
254
- result = self._rdatabase.execute_delete(self.__get_path, **params)
255
- case _:
256
- result = self._rdatabase.execute_delete(self.__get_path, params)
599
+ # Generate SQL.
600
+ sqls = []
601
+
602
+ ## Part 'DELETE' syntax.
603
+ sql_delete = f'DELETE FROM `{self.dbconn.db.database}`.`{table}`'
604
+ sqls.append(sql_delete)
605
+
606
+ ## Part 'WHERE' syntax.
607
+ if where is not None:
608
+ sql_where = f'WHERE {where}'
609
+ sqls.append(sql_where)
610
+
611
+ ## Part 'ORDER BY' syntax.
612
+ if order is not None:
613
+ sql_order = f'ORDER BY {order}'
614
+ sqls.append(sql_order)
615
+
616
+ ## Part 'LIMIT' syntax.
617
+ if limit is not None:
618
+ sql_limit = f'LIMIT {limit}'
619
+ sqls.append(sql_limit)
620
+
621
+ ## Join sqls.
622
+ sqls = '\n'.join(sqls)
623
+
624
+ # Execute SQL.
625
+ result = self.execute(sqls, report=report, **kwdata)
257
626
 
258
627
  return result
259
628
 
260
629
 
261
- def __mul__(
630
+ def copy(
262
631
  self,
263
- params: tuple | dict | str
632
+ table: str,
633
+ where: str | None = None,
634
+ limit: int | str | tuple[int, int] | None = None,
635
+ report: bool | None = None,
636
+ **kwdata: Any
264
637
  ) -> Result:
265
638
  """
266
639
  Copy record of table in the datebase.
267
640
 
268
641
  Parameters
269
642
  ----------
270
- params : Update parameters.
271
- - `tuple`: Enter parameters in '(path, *params)' format.
272
- - `dict`: Enter parameters in '(path, **params)' format.
273
- - `str`: Enter parameters in '(path, params)' format.
643
+ table : Table name.
644
+ where : Clause `WHERE` content, join as `WHERE str`.
645
+ limit : Clause `LIMIT` content.
646
+ - `int | str`: Join as `LIMIT int/str`.
647
+ - `tuple[int, int]`: Join as `LIMIT int, int`.
648
+ report : Whether report SQL execute information.
649
+ - `None`, Use attribute `report_execute_info`: of object `ROption`.
650
+ - `int`: Use this value.
651
+ kwdata : Keyword parameters for filling.
652
+ - `In 'WHERE' syntax`: Fill 'WHERE' syntax.
653
+ - `Not in 'WHERE' syntax`: Fill 'INSERT' and 'SELECT' syntax.
654
+ `str and first character is ':'`: Use this syntax.
655
+ `Any`: Use this value.
274
656
 
275
657
  Returns
276
658
  -------
277
659
  Result object.
660
+
661
+ Examples
662
+ --------
663
+ Parameter `where` and `kwdata`.
664
+ >>> where = '`id` IN :ids'
665
+ >>> ids = (1, 2, 3)
666
+ >>> result = Database.execute.copy('table', where, 2, ids=ids, id=None, time=':NOW()')
667
+ >>> print(result.rowcount)
668
+ 2
278
669
  """
279
670
 
280
- # Update.
281
- match params:
282
- case tuple():
283
- result = self._rdatabase.execute_copy(self.__get_path, *params)
284
- case dict():
285
- result = self._rdatabase.execute_copy(self.__get_path, **params)
286
- case _:
287
- result = self._rdatabase.execute_copy(self.__get_path, params)
671
+ # Handle parameter.
672
+ table_info: list[dict] = self.dbconn.db.info(self.dbconn.db.database)(table)()
673
+ field_key = 'COLUMN_NAME'
674
+ fields = [
675
+ row[field_key]
676
+ for row in table_info
677
+ ]
678
+ pattern = '(?<!\\\\):(\\w+)'
679
+ if type(where) == str:
680
+ where_keys = findall(pattern, where)
681
+ else:
682
+ where_keys = ()
683
+
684
+ # Generate SQL.
685
+ sqls = []
686
+
687
+ ## Part 'INSERT' syntax.
688
+ sql_fields = ', '.join(
689
+ f'`{field}`'
690
+ for field in fields
691
+ if field not in kwdata
692
+ )
693
+ if kwdata != {}:
694
+ sql_fields_kwdata = ', '.join(
695
+ f'`{field}`'
696
+ for field in kwdata
697
+ if field not in where_keys
698
+ )
699
+ sql_fields_filter = filter(
700
+ lambda sql: sql != '',
701
+ (
702
+ sql_fields,
703
+ sql_fields_kwdata
704
+ )
705
+ )
706
+ sql_fields = ', '.join(sql_fields_filter)
707
+ sql_insert = f'INSERT INTO `{self.dbconn.db.database}`.`{table}`({sql_fields})'
708
+ sqls.append(sql_insert)
709
+
710
+ ## Part 'SELECT' syntax.
711
+ sql_values = ', '.join(
712
+ f'`{field}`'
713
+ for field in fields
714
+ if field not in kwdata
715
+ )
716
+ if kwdata != {}:
717
+ sql_values_kwdata = ', '.join(
718
+ value[1:]
719
+ if (
720
+ type(value) == str
721
+ and value.startswith(':')
722
+ and value != ':'
723
+ )
724
+ else f':{field}'
725
+ for field, value in kwdata.items()
726
+ if field not in where_keys
727
+ )
728
+ sql_values_filter = filter(
729
+ lambda sql: sql != '',
730
+ (
731
+ sql_values,
732
+ sql_values_kwdata
733
+ )
734
+ )
735
+ sql_values = ', '.join(sql_values_filter)
736
+ sql_select = (
737
+ f'SELECT {sql_values}\n'
738
+ f'FROM `{self.dbconn.db.database}`.`{table}`'
739
+ )
740
+ sqls.append(sql_select)
741
+
742
+ ## Part 'WHERE' syntax.
743
+ if where is not None:
744
+ sql_where = f'WHERE {where}'
745
+ sqls.append(sql_where)
746
+
747
+ ## Part 'LIMIT' syntax.
748
+ if limit is not None:
749
+ if type(limit) in (str, int):
750
+ sql_limit = f'LIMIT {limit}'
751
+ else:
752
+ if len(limit) == 2:
753
+ sql_limit = f'LIMIT {limit[0]}, {limit[1]}'
754
+ else:
755
+ throw(ValueError, limit)
756
+ sqls.append(sql_limit)
757
+
758
+ ## Join.
759
+ sql = '\n'.join(sqls)
760
+
761
+ # Execute SQL.
762
+ result = self.execute(sql, report=report, **kwdata)
288
763
 
289
764
  return result
290
765
 
291
766
 
292
- def __contains__(
767
+ def count(
768
+ self,
769
+ table: str,
770
+ where: str | None = None,
771
+ report: bool | None = None,
772
+ **kwdata: Any
773
+ ) -> int:
774
+ """
775
+ Count records.
776
+
777
+ Parameters
778
+ ----------
779
+ table : Table name.
780
+ where : Match condition, `WHERE` clause content, join as `WHERE str`.
781
+ - `None`: Match all.
782
+ - `str`: Match condition.
783
+ report : Whether report SQL execute information.
784
+ - `None`, Use attribute `report_execute_info`: of object `ROption`.
785
+ - `int`: Use this value.
786
+ kwdata : Keyword parameters for filling.
787
+
788
+ Returns
789
+ -------
790
+ Record count.
791
+
792
+ Examples
793
+ --------
794
+ Parameter `where` and `kwdata`.
795
+ >>> where = '`id` IN :ids'
796
+ >>> ids = (1, 2)
797
+ >>> result = Database.execute.count('table', where, ids=ids)
798
+ >>> print(result)
799
+ 2
800
+ """
801
+
802
+ # Execute.
803
+ result = self.select(table, '1', where=where, report=report, **kwdata)
804
+ count = len(tuple(result))
805
+
806
+ return count
807
+
808
+
809
+ def exist(
293
810
  self,
294
- params: tuple | dict | str
811
+ table: str,
812
+ where: str | None = None,
813
+ report: bool | None = None,
814
+ **kwdata: Any
295
815
  ) -> bool:
296
816
  """
297
817
  Judge the exist of record.
298
818
 
299
819
  Parameters
300
820
  ----------
301
- params : Update parameters.
302
- - `tuple`: Enter parameters in '(path, *params)' format.
303
- - `dict`: Enter parameters in '(path, **params)' format.
304
- - `str`: Enter parameters in '(path, params)' format.
821
+ table : Table name.
822
+ where : Match condition, `WHERE` clause content, join as `WHERE str`.
823
+ - `None`: Match all.
824
+ - `str`: Match condition.
825
+ report : Whether report SQL execute information.
826
+ - `None`, Use attribute `report_execute_info`: of object `ROption`.
827
+ - `int`: Use this value.
828
+ kwdata : Keyword parameters for filling.
305
829
 
306
830
  Returns
307
831
  -------
308
- Result object.
832
+ Judged result.
833
+
834
+ Examples
835
+ --------
836
+ Parameter `where` and `kwdata`.
837
+ >>> data = [{'id': 1}]
838
+ >>> Database.execute.insert('table', data)
839
+ >>> where = '`id` = :id_'
840
+ >>> id_ = 1
841
+ >>> result = Database.execute.exist('table', where, id_=id_)
842
+ >>> print(result)
843
+ True
309
844
  """
310
845
 
311
- # Update.
312
- match params:
313
- case tuple():
314
- result = self._rdatabase.execute_exist(self.__get_path, *params)
315
- case dict():
316
- result = self._rdatabase.execute_exist(self.__get_path, **params)
317
- case _:
318
- result = self._rdatabase.execute_exist(self.__get_path, params)
846
+ # Execute.
847
+ result = self.count(table, where, report, **kwdata)
319
848
 
320
- return result
849
+ # Judge.
850
+ judge = result != 0
321
851
 
852
+ return judge
322
853
 
323
- def __len__(
324
- self
325
- ) -> int:
854
+
855
+ def generator(
856
+ self,
857
+ sql: str | TextClause,
858
+ data: TableData,
859
+ report: bool | None = None,
860
+ **kwdata: Any
861
+ ) -> Generator[Result, Any, None]:
326
862
  """
327
- Count records.
863
+ Return a generator that can execute SQL.
864
+
865
+ Parameters
866
+ ----------
867
+ sql : SQL in method `sqlalchemy.text` format, or `TextClause` object.
868
+ data : Data set for filling.
869
+ report : Whether report SQL execute information.
870
+ - `None`: Use attribute `default_report`.
871
+ - `bool`: Use this value.
872
+ kwdata : Keyword parameters for filling.
328
873
 
329
874
  Returns
330
875
  -------
331
- Record count.
876
+ Generator.
332
877
  """
333
878
 
334
- # Update.
335
- result = self._rdatabase.execute_count(self.__get_path)
879
+ # Instance.
880
+ func_generator = FunctionGenerator(
881
+ self.execute,
882
+ sql=sql,
883
+ report=report,
884
+ **kwdata
885
+ )
336
886
 
337
- return result
887
+ # Add.
888
+ for row in data:
889
+ func_generator(**row)
890
+
891
+ return func_generator.generator
892
+
893
+
894
+ def handle_sql(self, sql: str | TextClause) -> TextClause:
895
+ """
896
+ Handle SQL.
897
+
898
+ Parameters
899
+ ----------
900
+ sql : SQL in method `sqlalchemy.text` format, or TextClause object.
901
+
902
+ Returns
903
+ -------
904
+ TextClause instance.
905
+ """
906
+
907
+ # Handle parameter.
908
+ if type(sql) == TextClause:
909
+ sql = sql.text
910
+
911
+ # Handle.
912
+ sql = sql.strip()
913
+ if sql[-1] != ';':
914
+ sql += ';'
915
+ sql = sqlalchemy_text(sql)
916
+
917
+ return sql
918
+
919
+
920
+ def handle_data(
921
+ self,
922
+ data: list[dict],
923
+ sql: str | TextClause,
924
+ ) -> list[dict]:
925
+ """
926
+ Handle data based on the content of SQL.
927
+
928
+ Parameters
929
+ ----------
930
+ data : Data set for filling.
931
+ sql : SQL in method `sqlalchemy.text` format, or TextClause object.
932
+
933
+ Returns
934
+ -------
935
+ Filled data.
936
+ """
937
+
938
+ # Handle parameter.
939
+ if type(sql) == TextClause:
940
+ sql = sql.text
941
+
942
+ # Extract keys.
943
+ pattern = '(?<!\\\\):(\\w+)'
944
+ sql_keys = findall(pattern, sql)
945
+
946
+ # Extract keys of syntax "in".
947
+ pattern = '[iI][nN]\\s+(?<!\\\\):(\\w+)'
948
+ sql_keys_in = findall(pattern, sql)
949
+
950
+ # Loop.
951
+ for row in data:
952
+ if row == {}:
953
+ continue
954
+ for key in sql_keys:
955
+ value = row.get(key)
956
+
957
+ # Empty string.
958
+ if value == '':
959
+ value = None
960
+
961
+ # Convert.
962
+ elif (
963
+ type(value) in (list, dict)
964
+ and key not in sql_keys_in
965
+ ):
966
+ value = to_json(value)
967
+
968
+ # Enum.
969
+ elif isinstance(type(value), EnumType):
970
+ value = value.value
971
+
972
+ row[key] = value
973
+
974
+ return data