reydb 1.2.1__py3-none-any.whl → 1.2.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.
- reydb/rbase.py +4 -4
- reydb/rbuild.py +1493 -13
- reydb/rconfig.py +27 -21
- reydb/rdb.py +2 -2
- reydb/rerror.py +29 -23
- reydb/rexec.py +20 -20
- reydb/rinfo.py +13 -13
- reydb/rorm.py +62 -24
- {reydb-1.2.1.dist-info → reydb-1.2.3.dist-info}/METADATA +1 -1
- reydb-1.2.3.dist-info/RECORD +15 -0
- reydb-1.2.1.dist-info/RECORD +0 -15
- {reydb-1.2.1.dist-info → reydb-1.2.3.dist-info}/WHEEL +0 -0
- {reydb-1.2.1.dist-info → reydb-1.2.3.dist-info}/licenses/LICENSE +0 -0
reydb/rbuild.py
CHANGED
@@ -121,7 +121,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
121
121
|
Field set SQL.
|
122
122
|
"""
|
123
123
|
|
124
|
-
#
|
124
|
+
# Set parameter.
|
125
125
|
|
126
126
|
## Constraint.
|
127
127
|
constraint = ' ' + constraint
|
@@ -175,7 +175,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
175
175
|
Index set SQL.
|
176
176
|
"""
|
177
177
|
|
178
|
-
#
|
178
|
+
# Set parameter.
|
179
179
|
if fields.__class__ == str:
|
180
180
|
fields = [fields]
|
181
181
|
match type_:
|
@@ -275,7 +275,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
275
275
|
SQL.
|
276
276
|
"""
|
277
277
|
|
278
|
-
#
|
278
|
+
# Set parameter.
|
279
279
|
if type(path) == str:
|
280
280
|
database, table = self.db.database, path
|
281
281
|
else:
|
@@ -363,7 +363,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
363
363
|
SQL.
|
364
364
|
"""
|
365
365
|
|
366
|
-
#
|
366
|
+
# Set parameter.
|
367
367
|
if type(path) == str:
|
368
368
|
database, table = self.db.database, path
|
369
369
|
else:
|
@@ -478,7 +478,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
478
478
|
SQL.
|
479
479
|
"""
|
480
480
|
|
481
|
-
#
|
481
|
+
# Set parameter.
|
482
482
|
if type(path) == str:
|
483
483
|
database, table = self.db.database, path
|
484
484
|
else:
|
@@ -509,7 +509,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
509
509
|
SQL.
|
510
510
|
"""
|
511
511
|
|
512
|
-
#
|
512
|
+
# Set parameter.
|
513
513
|
if type(path) == str:
|
514
514
|
database, table = self.db.database, path
|
515
515
|
else:
|
@@ -616,7 +616,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
616
616
|
SQL.
|
617
617
|
"""
|
618
618
|
|
619
|
-
#
|
619
|
+
# Set parameter.
|
620
620
|
if type(path) == str:
|
621
621
|
database, table = self.db.database, path
|
622
622
|
else:
|
@@ -704,7 +704,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
704
704
|
SQL.
|
705
705
|
"""
|
706
706
|
|
707
|
-
#
|
707
|
+
# Set parameter.
|
708
708
|
if type(path) == str:
|
709
709
|
database, table = self.db.database, path
|
710
710
|
else:
|
@@ -792,7 +792,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
792
792
|
SQL.
|
793
793
|
"""
|
794
794
|
|
795
|
-
#
|
795
|
+
# Set parameter.
|
796
796
|
if type(path) == str:
|
797
797
|
database, table = self.db.database, path
|
798
798
|
else:
|
@@ -886,7 +886,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
886
886
|
SQL.
|
887
887
|
"""
|
888
888
|
|
889
|
-
#
|
889
|
+
# Set parameter.
|
890
890
|
if type(path) == str:
|
891
891
|
database, table = self.db.database, path
|
892
892
|
else:
|
@@ -917,7 +917,7 @@ class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
|
917
917
|
SQL.
|
918
918
|
"""
|
919
919
|
|
920
|
-
#
|
920
|
+
# Set parameter.
|
921
921
|
if type(path) == str:
|
922
922
|
database, table = self.db.database, path
|
923
923
|
else:
|
@@ -1104,7 +1104,7 @@ class DatabaseBuild(DatabaseBuildSuper['rdb.Database']):
|
|
1104
1104
|
skip : Whether skip existing table.
|
1105
1105
|
"""
|
1106
1106
|
|
1107
|
-
#
|
1107
|
+
# Set parameter.
|
1108
1108
|
databases = databases or []
|
1109
1109
|
tables = tables or []
|
1110
1110
|
views = views or []
|
@@ -1187,6 +1187,227 @@ class DatabaseBuild(DatabaseBuildSuper['rdb.Database']):
|
|
1187
1187
|
|
1188
1188
|
### Execute.
|
1189
1189
|
self.db.execute(sql)
|
1190
|
+
|
1191
|
+
## Report.
|
1192
|
+
text = f"Table '{table}' of database '{database}' build completed."
|
1193
|
+
print(text)
|
1194
|
+
refresh_schema = True
|
1195
|
+
|
1196
|
+
# Refresh schema.
|
1197
|
+
if refresh_schema:
|
1198
|
+
self.db.schema()
|
1199
|
+
|
1200
|
+
# View.
|
1201
|
+
for params in views:
|
1202
|
+
path = params['path']
|
1203
|
+
if type(path) == str:
|
1204
|
+
database, table = self.db.database, path
|
1205
|
+
else:
|
1206
|
+
database, table = path
|
1207
|
+
|
1208
|
+
## Exist.
|
1209
|
+
if (
|
1210
|
+
skip
|
1211
|
+
and self.db.schema.exist(database, table)
|
1212
|
+
):
|
1213
|
+
continue
|
1214
|
+
|
1215
|
+
## SQL.
|
1216
|
+
sql = self.get_sql_create_view(**params)
|
1217
|
+
|
1218
|
+
## Confirm.
|
1219
|
+
if ask:
|
1220
|
+
self.input_confirm_build(sql)
|
1221
|
+
|
1222
|
+
## Execute.
|
1223
|
+
self.db.execute(sql)
|
1224
|
+
|
1225
|
+
## Report.
|
1226
|
+
text = f"View '{table}' of database '{database}' build completed."
|
1227
|
+
print(text)
|
1228
|
+
|
1229
|
+
# View stats.
|
1230
|
+
for params in views_stats:
|
1231
|
+
path = params['path']
|
1232
|
+
if type(path) == str:
|
1233
|
+
database, table = self.db.database, path
|
1234
|
+
else:
|
1235
|
+
database, table = path
|
1236
|
+
|
1237
|
+
## Exist.
|
1238
|
+
if (
|
1239
|
+
skip
|
1240
|
+
and self.db.schema.exist(database, table)
|
1241
|
+
):
|
1242
|
+
continue
|
1243
|
+
|
1244
|
+
## SQL.
|
1245
|
+
sql = self.get_sql_create_view_stats(**params)
|
1246
|
+
|
1247
|
+
## Confirm.
|
1248
|
+
if ask:
|
1249
|
+
self.input_confirm_build(sql)
|
1250
|
+
|
1251
|
+
## Execute.
|
1252
|
+
self.db.execute(sql)
|
1253
|
+
|
1254
|
+
## Report.
|
1255
|
+
text = f"View '{table}' of database '{database}' build completed."
|
1256
|
+
print(text)
|
1257
|
+
|
1258
|
+
|
1259
|
+
__call__ = build
|
1260
|
+
|
1261
|
+
|
1262
|
+
class DatabaseBuildAsync(DatabaseBuildSuper['rdb.DatabaseAsync']):
|
1263
|
+
"""
|
1264
|
+
Asynchronous database build type.
|
1265
|
+
"""
|
1266
|
+
|
1267
|
+
|
1268
|
+
async def create_orm_table(
|
1269
|
+
self,
|
1270
|
+
*models: Type[DatabaseORMModel] | DatabaseORMModel,
|
1271
|
+
skip: bool = False
|
1272
|
+
) -> None:
|
1273
|
+
"""
|
1274
|
+
Asynchronous create tables by ORM model.
|
1275
|
+
|
1276
|
+
Parameters
|
1277
|
+
----------
|
1278
|
+
models : ORM model instances.
|
1279
|
+
skip : Whether skip existing table.
|
1280
|
+
"""
|
1281
|
+
|
1282
|
+
# Create.
|
1283
|
+
await self.db.orm.create(*models, skip=skip)
|
1284
|
+
|
1285
|
+
|
1286
|
+
async def drop_orm_table(
|
1287
|
+
self,
|
1288
|
+
*models: Type[DatabaseORMModel] | DatabaseORMModel,
|
1289
|
+
skip: bool = False
|
1290
|
+
) -> None:
|
1291
|
+
"""
|
1292
|
+
Asynchronous delete tables by model.
|
1293
|
+
|
1294
|
+
Parameters
|
1295
|
+
----------
|
1296
|
+
models : ORM model instances.
|
1297
|
+
skip : Skip not exist table.
|
1298
|
+
"""
|
1299
|
+
|
1300
|
+
# Drop.
|
1301
|
+
await self.db.orm.drop(*models, skip=skip)
|
1302
|
+
|
1303
|
+
|
1304
|
+
async def build(
|
1305
|
+
self,
|
1306
|
+
databases: list[dict] | None = None,
|
1307
|
+
tables: list[dict] | None = None,
|
1308
|
+
tables_orm: list[Type[DatabaseORMModel]] | None = None,
|
1309
|
+
views: list[dict] | None = None,
|
1310
|
+
views_stats: list[dict] | None = None,
|
1311
|
+
ask: bool = True,
|
1312
|
+
skip: bool = False
|
1313
|
+
) -> None:
|
1314
|
+
"""
|
1315
|
+
Asynchronous build databases or tables.
|
1316
|
+
|
1317
|
+
Parameters
|
1318
|
+
----------
|
1319
|
+
databases : Database build parameters, equivalent to the parameters of method `self.create_database`.
|
1320
|
+
tables : Tables build parameters, equivalent to the parameters of method `self.create_table`.
|
1321
|
+
views : Views build parameters, equivalent to the parameters of method `self.create_view`.
|
1322
|
+
views_stats : Views stats build parameters, equivalent to the parameters of method `self.create_view_stats`.
|
1323
|
+
ask : Whether ask confirm execute.
|
1324
|
+
skip : Whether skip existing table.
|
1325
|
+
"""
|
1326
|
+
|
1327
|
+
# Set parameter.
|
1328
|
+
databases = databases or []
|
1329
|
+
tables = tables or []
|
1330
|
+
tables_orm = tables_orm or []
|
1331
|
+
views = views or []
|
1332
|
+
views_stats = views_stats or []
|
1333
|
+
refresh_schema = False
|
1334
|
+
|
1335
|
+
# Database.
|
1336
|
+
for params in databases:
|
1337
|
+
database = params['name']
|
1338
|
+
|
1339
|
+
## Exist.
|
1340
|
+
if (
|
1341
|
+
skip
|
1342
|
+
and await self.db.schema.exist(database)
|
1343
|
+
):
|
1344
|
+
continue
|
1345
|
+
|
1346
|
+
## SQL.
|
1347
|
+
sql = self.get_sql_create_database(**params)
|
1348
|
+
|
1349
|
+
## Confirm.
|
1350
|
+
if ask:
|
1351
|
+
self.input_confirm_build(sql)
|
1352
|
+
|
1353
|
+
## Execute.
|
1354
|
+
await self.db.execute(sql)
|
1355
|
+
|
1356
|
+
## Report.
|
1357
|
+
text = f"Database '{database}' build completed."
|
1358
|
+
print(text)
|
1359
|
+
|
1360
|
+
# Table.
|
1361
|
+
for params in tables:
|
1362
|
+
|
1363
|
+
## ORM.
|
1364
|
+
if (
|
1365
|
+
is_instance(params)
|
1366
|
+
and isinstance(params, DatabaseORMModel)
|
1367
|
+
or issubclass(params, DatabaseORMModel)
|
1368
|
+
):
|
1369
|
+
database = self.db.database
|
1370
|
+
table = params._table().name
|
1371
|
+
|
1372
|
+
## Exist.
|
1373
|
+
if (
|
1374
|
+
skip
|
1375
|
+
and await self.db.schema.exist(self.db.database, table)
|
1376
|
+
):
|
1377
|
+
continue
|
1378
|
+
|
1379
|
+
## Confirm.
|
1380
|
+
if ask:
|
1381
|
+
text = self.get_orm_table_text(params)
|
1382
|
+
self.input_confirm_build(text)
|
1383
|
+
|
1384
|
+
## Execute.
|
1385
|
+
await self.create_orm_table(params)
|
1386
|
+
|
1387
|
+
## Parameter.
|
1388
|
+
else:
|
1389
|
+
path: str | tuple[str, str] = params['path']
|
1390
|
+
if type(path) == str:
|
1391
|
+
database, table = self.db.database, path
|
1392
|
+
else:
|
1393
|
+
database, table = path
|
1394
|
+
|
1395
|
+
### Exist.
|
1396
|
+
if (
|
1397
|
+
skip
|
1398
|
+
and await self.db.schema.exist(database, table)
|
1399
|
+
):
|
1400
|
+
continue
|
1401
|
+
|
1402
|
+
### SQL.
|
1403
|
+
sql = self.get_sql_create_table(**params)
|
1404
|
+
|
1405
|
+
### Confirm.
|
1406
|
+
if ask:
|
1407
|
+
self.input_confirm_build(sql)
|
1408
|
+
|
1409
|
+
### Execute.
|
1410
|
+
await self.db.execute(sql)
|
1190
1411
|
refresh_schema = True
|
1191
1412
|
|
1192
1413
|
## Report.
|
@@ -1197,6 +1418,1265 @@ class DatabaseBuild(DatabaseBuildSuper['rdb.Database']):
|
|
1197
1418
|
if refresh_schema:
|
1198
1419
|
self.db.schema()
|
1199
1420
|
|
1421
|
+
# View.
|
1422
|
+
for params in views:
|
1423
|
+
path = params['path']
|
1424
|
+
if type(path) == str:
|
1425
|
+
database, table = self.db.database, path
|
1426
|
+
else:
|
1427
|
+
database, table = path
|
1428
|
+
|
1429
|
+
## Exist.
|
1430
|
+
if (
|
1431
|
+
skip
|
1432
|
+
and await self.db.schema.exist(database, table)
|
1433
|
+
):
|
1434
|
+
continue
|
1435
|
+
|
1436
|
+
## SQL.
|
1437
|
+
sql = self.get_sql_create_view(**params)
|
1438
|
+
|
1439
|
+
## Confirm.
|
1440
|
+
if ask:
|
1441
|
+
self.input_confirm_build(sql)
|
1442
|
+
|
1443
|
+
## Execute.
|
1444
|
+
await self.db.execute(sql)
|
1445
|
+
|
1446
|
+
## Report.
|
1447
|
+
text = f"View '{table}' of database '{database}' build completed."
|
1448
|
+
print(text)
|
1449
|
+
|
1450
|
+
# View stats.
|
1451
|
+
for params in views_stats:
|
1452
|
+
path = params['path']
|
1453
|
+
if type(path) == str:
|
1454
|
+
database, table = self.db.database, path
|
1455
|
+
else:
|
1456
|
+
database, table = path
|
1457
|
+
|
1458
|
+
## Exist.
|
1459
|
+
if (
|
1460
|
+
skip
|
1461
|
+
and await self.db.schema.exist(database, table)
|
1462
|
+
):
|
1463
|
+
continue
|
1464
|
+
|
1465
|
+
## SQL.
|
1466
|
+
sql = self.get_sql_create_view_stats(**params)
|
1467
|
+
|
1468
|
+
## Confirm.
|
1469
|
+
if ask:
|
1470
|
+
self.input_confirm_build(sql)
|
1471
|
+
|
1472
|
+
## Execute.
|
1473
|
+
await self.db.execute(sql)
|
1474
|
+
|
1475
|
+
## Report.
|
1476
|
+
text = f"View '{table}' of database '{database}' build completed."
|
1477
|
+
print(text)
|
1478
|
+
|
1479
|
+
|
1480
|
+
__call__ = build
|
1481
|
+
# !/usr/bin/env python
|
1482
|
+
# -*- coding: utf-8 -*-
|
1483
|
+
|
1484
|
+
"""
|
1485
|
+
@Time : 2023-10-14 23:05:35
|
1486
|
+
@Author : Rey
|
1487
|
+
@Contact : reyxbo@163.com
|
1488
|
+
@Explain : Database build methods.
|
1489
|
+
"""
|
1490
|
+
|
1491
|
+
|
1492
|
+
from typing import TypedDict, NotRequired, Literal, Type, TypeVar, Generic
|
1493
|
+
from copy import deepcopy
|
1494
|
+
from reykit.rbase import throw, is_instance
|
1495
|
+
from reykit.rstdout import ask
|
1496
|
+
|
1497
|
+
from . import rdb
|
1498
|
+
from .rbase import DatabaseBase
|
1499
|
+
from .rorm import DatabaseORMModel
|
1500
|
+
|
1501
|
+
|
1502
|
+
__all__ = (
|
1503
|
+
'DatabaseBuildSuper',
|
1504
|
+
'DatabaseBuild',
|
1505
|
+
'DatabaseBuildAsync'
|
1506
|
+
)
|
1507
|
+
|
1508
|
+
|
1509
|
+
FieldSet = TypedDict(
|
1510
|
+
'FieldSet',
|
1511
|
+
{
|
1512
|
+
'name': str,
|
1513
|
+
'type': str,
|
1514
|
+
'constraint': NotRequired[str | None],
|
1515
|
+
'comment': NotRequired[str | None],
|
1516
|
+
'position': NotRequired[Literal['first'] | str | None]
|
1517
|
+
}
|
1518
|
+
)
|
1519
|
+
type IndexType = Literal['noraml', 'unique', 'fulltext', 'spatial']
|
1520
|
+
IndexSet = TypedDict(
|
1521
|
+
'IndexSet',
|
1522
|
+
{
|
1523
|
+
'name': str,
|
1524
|
+
'fields' : str | list[str],
|
1525
|
+
'type': IndexType,
|
1526
|
+
'comment': NotRequired[str | None]
|
1527
|
+
}
|
1528
|
+
)
|
1529
|
+
DatabaseT = TypeVar('DatabaseT', 'rdb.Database', 'rdb.DatabaseAsync')
|
1530
|
+
|
1531
|
+
|
1532
|
+
class DatabaseBuildSuper(DatabaseBase, Generic[DatabaseT]):
|
1533
|
+
"""
|
1534
|
+
Database build super type.
|
1535
|
+
"""
|
1536
|
+
|
1537
|
+
|
1538
|
+
def __init__(self, db: DatabaseT) -> None:
|
1539
|
+
"""
|
1540
|
+
Build instance attributes.
|
1541
|
+
|
1542
|
+
Parameters
|
1543
|
+
----------
|
1544
|
+
db: Database instance.
|
1545
|
+
"""
|
1546
|
+
|
1547
|
+
# Set attribute.
|
1548
|
+
self.db = db
|
1549
|
+
|
1550
|
+
|
1551
|
+
def get_sql_create_database(
|
1552
|
+
self,
|
1553
|
+
name: str,
|
1554
|
+
character: str = 'utf8mb4',
|
1555
|
+
collate: str = 'utf8mb4_0900_ai_ci'
|
1556
|
+
) -> str:
|
1557
|
+
"""
|
1558
|
+
Get SQL of create database.
|
1559
|
+
|
1560
|
+
Parameters
|
1561
|
+
----------
|
1562
|
+
name : Database name.
|
1563
|
+
character : Character set.
|
1564
|
+
collate : Collate rule.
|
1565
|
+
execute : Whether directly execute.
|
1566
|
+
|
1567
|
+
Returns
|
1568
|
+
-------
|
1569
|
+
SQL.
|
1570
|
+
"""
|
1571
|
+
|
1572
|
+
# Generate.
|
1573
|
+
sql = f'CREATE DATABASE `{name}` CHARACTER SET {character} COLLATE {collate}'
|
1574
|
+
|
1575
|
+
return sql
|
1576
|
+
|
1577
|
+
|
1578
|
+
def __get_field_sql(
|
1579
|
+
self,
|
1580
|
+
name: str,
|
1581
|
+
type_: str,
|
1582
|
+
constraint: str = 'DEFAULT NULL',
|
1583
|
+
comment: str | None = None,
|
1584
|
+
position: str | None = None,
|
1585
|
+
old_name: str | None = None
|
1586
|
+
) -> str:
|
1587
|
+
"""
|
1588
|
+
Get a field set SQL.
|
1589
|
+
|
1590
|
+
Parameters
|
1591
|
+
----------
|
1592
|
+
name : Field name.
|
1593
|
+
type_ : Field type.
|
1594
|
+
constraint : Field constraint.
|
1595
|
+
comment : Field comment.
|
1596
|
+
position : Field position.
|
1597
|
+
old_name : Field old name.
|
1598
|
+
|
1599
|
+
Returns
|
1600
|
+
-------
|
1601
|
+
Field set SQL.
|
1602
|
+
"""
|
1603
|
+
|
1604
|
+
# Set parameter.
|
1605
|
+
|
1606
|
+
## Constraint.
|
1607
|
+
constraint = ' ' + constraint
|
1608
|
+
|
1609
|
+
## Comment.
|
1610
|
+
if comment is None:
|
1611
|
+
comment = ''
|
1612
|
+
else:
|
1613
|
+
comment = f" COMMENT '{comment}'"
|
1614
|
+
|
1615
|
+
## Position.
|
1616
|
+
match position:
|
1617
|
+
case None:
|
1618
|
+
position = ''
|
1619
|
+
case 'first':
|
1620
|
+
position = ' FIRST'
|
1621
|
+
case _:
|
1622
|
+
position = f' AFTER `{position}`'
|
1623
|
+
|
1624
|
+
## Old name.
|
1625
|
+
if old_name is None:
|
1626
|
+
old_name = ''
|
1627
|
+
else:
|
1628
|
+
old_name = f'`{old_name}` '
|
1629
|
+
|
1630
|
+
# Generate.
|
1631
|
+
sql = f'{old_name}`{name}` {type_}{constraint}{comment}{position}'
|
1632
|
+
|
1633
|
+
return sql
|
1634
|
+
|
1635
|
+
|
1636
|
+
def __get_index_sql(
|
1637
|
+
self,
|
1638
|
+
name: str,
|
1639
|
+
fields: str | list[str],
|
1640
|
+
type_: IndexType,
|
1641
|
+
comment: str | None = None
|
1642
|
+
) -> str:
|
1643
|
+
"""
|
1644
|
+
Get a index set SQL.
|
1645
|
+
|
1646
|
+
Parameters
|
1647
|
+
----------
|
1648
|
+
name : Index name.
|
1649
|
+
fields : Index fileds.
|
1650
|
+
type\\_ : Index type.
|
1651
|
+
comment : Index comment.
|
1652
|
+
|
1653
|
+
Returns
|
1654
|
+
-------
|
1655
|
+
Index set SQL.
|
1656
|
+
"""
|
1657
|
+
|
1658
|
+
# Set parameter.
|
1659
|
+
if fields.__class__ == str:
|
1660
|
+
fields = [fields]
|
1661
|
+
match type_:
|
1662
|
+
case 'noraml':
|
1663
|
+
type_ = 'KEY'
|
1664
|
+
method = ' USING BTREE'
|
1665
|
+
case 'unique':
|
1666
|
+
type_ = 'UNIQUE KEY'
|
1667
|
+
method = ' USING BTREE'
|
1668
|
+
case 'fulltext':
|
1669
|
+
type_ = 'FULLTEXT KEY'
|
1670
|
+
method = ''
|
1671
|
+
case 'spatial':
|
1672
|
+
type_ = 'SPATIAL KEY'
|
1673
|
+
method = ''
|
1674
|
+
case _:
|
1675
|
+
throw(ValueError, type_)
|
1676
|
+
if comment in (None, ''):
|
1677
|
+
comment = ''
|
1678
|
+
else:
|
1679
|
+
comment = f" COMMENT '{comment}'"
|
1680
|
+
|
1681
|
+
# Generate.
|
1682
|
+
|
1683
|
+
## Fields.
|
1684
|
+
sql_fields = ', '.join(
|
1685
|
+
[
|
1686
|
+
f'`{field}`'
|
1687
|
+
for field in fields
|
1688
|
+
]
|
1689
|
+
)
|
1690
|
+
|
1691
|
+
## Join.
|
1692
|
+
sql = f'{type_} `{name}` ({sql_fields}){method}{comment}'
|
1693
|
+
|
1694
|
+
return sql
|
1695
|
+
|
1696
|
+
|
1697
|
+
def get_sql_create_table(
|
1698
|
+
self,
|
1699
|
+
path: str | tuple[str, str],
|
1700
|
+
fields: FieldSet | list[FieldSet],
|
1701
|
+
primary: str | list[str] | None = None,
|
1702
|
+
indexes: IndexSet | list[IndexSet] | None = None,
|
1703
|
+
engine: str = 'InnoDB',
|
1704
|
+
increment: int = 1,
|
1705
|
+
charset: str = 'utf8mb4',
|
1706
|
+
collate: str = 'utf8mb4_0900_ai_ci',
|
1707
|
+
comment: str | None = None
|
1708
|
+
) -> str:
|
1709
|
+
"""
|
1710
|
+
Get SQL of create table.
|
1711
|
+
|
1712
|
+
Parameters
|
1713
|
+
----------
|
1714
|
+
path : Path.
|
1715
|
+
- `str`: Table name.
|
1716
|
+
- `tuple[str, str]`: Database name and table name.
|
1717
|
+
fields : Fields set table.
|
1718
|
+
- `Key 'name'`: Field name, required.
|
1719
|
+
- `Key 'type'`: Field type, required.
|
1720
|
+
- `Key 'constraint'`: Field constraint.
|
1721
|
+
`Empty or None`: Use 'DEFAULT NULL'.
|
1722
|
+
`str`: Use this value.
|
1723
|
+
- `Key 'comment'`: Field comment.
|
1724
|
+
`Empty or None`: Not comment.
|
1725
|
+
`str`: Use this value.
|
1726
|
+
- `Key 'position'`: Field position.
|
1727
|
+
`None`: Last.
|
1728
|
+
`Literal['first']`: First.
|
1729
|
+
`str`: After this field.
|
1730
|
+
primary : Primary key fields.
|
1731
|
+
- `str`: One field.
|
1732
|
+
- `list[str]`: Multiple fileds.
|
1733
|
+
indexes : Index set table.
|
1734
|
+
- `Key 'name'`: Index name, required.
|
1735
|
+
- `Key 'fields'`: Index fields, required.
|
1736
|
+
`str`: One field.
|
1737
|
+
`list[str]`: Multiple fileds.
|
1738
|
+
- `Key 'type'`: Index type.
|
1739
|
+
`Literal['noraml']`: Noraml key.
|
1740
|
+
`Literal['unique']`: Unique key.
|
1741
|
+
`Literal['fulltext']`: Full text key.
|
1742
|
+
`Literal['spatial']`: Spatial key.
|
1743
|
+
- `Key 'comment'`: Field comment.
|
1744
|
+
`Empty or None`: Not comment.
|
1745
|
+
`str`: Use this value.
|
1746
|
+
engine : Engine type.
|
1747
|
+
increment : Automatic Increment start value.
|
1748
|
+
charset : Charset type.
|
1749
|
+
collate : Collate type.
|
1750
|
+
comment : Table comment.
|
1751
|
+
execute : Whether directly execute.
|
1752
|
+
|
1753
|
+
Returns
|
1754
|
+
-------
|
1755
|
+
SQL.
|
1756
|
+
"""
|
1757
|
+
|
1758
|
+
# Set parameter.
|
1759
|
+
if type(path) == str:
|
1760
|
+
database, table = self.db.database, path
|
1761
|
+
else:
|
1762
|
+
database, table = path
|
1763
|
+
if fields.__class__ == dict:
|
1764
|
+
fields = [fields]
|
1765
|
+
if primary.__class__ == str:
|
1766
|
+
primary = [primary]
|
1767
|
+
if primary in ([], ['']):
|
1768
|
+
primary = None
|
1769
|
+
if indexes.__class__ == dict:
|
1770
|
+
indexes = [indexes]
|
1771
|
+
|
1772
|
+
## Compatible dictionary key name.
|
1773
|
+
fields = deepcopy(fields)
|
1774
|
+
for row in fields:
|
1775
|
+
row['type_'] = row.pop('type')
|
1776
|
+
if indexes is not None:
|
1777
|
+
indexes = deepcopy(indexes)
|
1778
|
+
for row in indexes:
|
1779
|
+
row['type_'] = row.pop('type')
|
1780
|
+
|
1781
|
+
# Generate.
|
1782
|
+
|
1783
|
+
## Fields.
|
1784
|
+
sql_fields = [
|
1785
|
+
self.__get_field_sql(**field)
|
1786
|
+
for field in fields
|
1787
|
+
]
|
1788
|
+
|
1789
|
+
## Primary.
|
1790
|
+
if primary is not None:
|
1791
|
+
keys = ', '.join(
|
1792
|
+
[
|
1793
|
+
f'`{key}`'
|
1794
|
+
for key in primary
|
1795
|
+
]
|
1796
|
+
)
|
1797
|
+
sql_primary = f'PRIMARY KEY ({keys}) USING BTREE'
|
1798
|
+
sql_fields.append(sql_primary)
|
1799
|
+
|
1800
|
+
## Indexes.
|
1801
|
+
if indexes is not None:
|
1802
|
+
sql_indexes = [
|
1803
|
+
self.__get_index_sql(**index)
|
1804
|
+
for index in indexes
|
1805
|
+
]
|
1806
|
+
sql_fields.extend(sql_indexes)
|
1807
|
+
|
1808
|
+
## Comment.
|
1809
|
+
if comment is None:
|
1810
|
+
sql_comment = ''
|
1811
|
+
else:
|
1812
|
+
sql_comment = f" COMMENT='{comment}'"
|
1813
|
+
|
1814
|
+
## Join.
|
1815
|
+
sql_fields = ',\n '.join(sql_fields)
|
1816
|
+
sql = (
|
1817
|
+
f'CREATE TABLE `{database}`.`{table}`(\n'
|
1818
|
+
f' {sql_fields}\n'
|
1819
|
+
f') ENGINE={engine} AUTO_INCREMENT={increment} CHARSET={charset} COLLATE={collate}{sql_comment}'
|
1820
|
+
)
|
1821
|
+
|
1822
|
+
return sql
|
1823
|
+
|
1824
|
+
|
1825
|
+
def get_sql_create_view(
|
1826
|
+
self,
|
1827
|
+
path: str | tuple[str, str],
|
1828
|
+
select: str
|
1829
|
+
) -> str:
|
1830
|
+
"""
|
1831
|
+
Get SQL of create view.
|
1832
|
+
|
1833
|
+
Parameters
|
1834
|
+
----------
|
1835
|
+
path : Path.
|
1836
|
+
- `str`: Table name.
|
1837
|
+
- `tuple[str, str]`: Database name and table name.
|
1838
|
+
select : View select SQL.
|
1839
|
+
execute : Whether directly execute.
|
1840
|
+
|
1841
|
+
Returns
|
1842
|
+
-------
|
1843
|
+
SQL.
|
1844
|
+
"""
|
1845
|
+
|
1846
|
+
# Set parameter.
|
1847
|
+
if type(path) == str:
|
1848
|
+
database, table = self.db.database, path
|
1849
|
+
else:
|
1850
|
+
database, table = path
|
1851
|
+
|
1852
|
+
# Generate SQL.
|
1853
|
+
select = select.replace('\n', '\n ')
|
1854
|
+
sql = f'CREATE VIEW `{database}`.`{table}` AS (\n {select}\n)'
|
1855
|
+
|
1856
|
+
return sql
|
1857
|
+
|
1858
|
+
|
1859
|
+
def get_sql_create_view_stats(
|
1860
|
+
self,
|
1861
|
+
path: str | tuple[str, str],
|
1862
|
+
items: list[dict]
|
1863
|
+
) -> str:
|
1864
|
+
"""
|
1865
|
+
Get SQL of create stats view.
|
1866
|
+
|
1867
|
+
Parameters
|
1868
|
+
----------
|
1869
|
+
path : Path.
|
1870
|
+
- `str`: Table name.
|
1871
|
+
- `tuple[str, str]`: Database name and table name.
|
1872
|
+
items : Items set table.
|
1873
|
+
- `Key 'name'`: Item name, required.
|
1874
|
+
- `Key 'select'`: Item select SQL, must only return one value, required.
|
1875
|
+
- `Key 'comment'`: Item comment.
|
1876
|
+
execute : Whether directly execute.
|
1877
|
+
|
1878
|
+
Returns
|
1879
|
+
-------
|
1880
|
+
SQL.
|
1881
|
+
"""
|
1882
|
+
|
1883
|
+
# Check.
|
1884
|
+
if items == []:
|
1885
|
+
throw(ValueError, items)
|
1886
|
+
|
1887
|
+
# Generate select SQL.
|
1888
|
+
item_first = items[0]
|
1889
|
+
select_first = "SELECT '%s' AS `item`,\n(\n %s\n) AS `value`,\n%s AS `comment`" % (
|
1890
|
+
item_first['name'],
|
1891
|
+
item_first['select'].replace('\n', '\n '),
|
1892
|
+
(
|
1893
|
+
'NULL'
|
1894
|
+
if 'comment' not in item_first
|
1895
|
+
else "'%s'" % item_first['comment']
|
1896
|
+
)
|
1897
|
+
)
|
1898
|
+
selects = [
|
1899
|
+
"SELECT '%s',\n(\n %s\n),\n%s" % (
|
1900
|
+
item['name'],
|
1901
|
+
item['select'].replace('\n', '\n '),
|
1902
|
+
(
|
1903
|
+
'NULL'
|
1904
|
+
if 'comment' not in item
|
1905
|
+
else "'%s'" % item['comment']
|
1906
|
+
)
|
1907
|
+
)
|
1908
|
+
for item in items[1:]
|
1909
|
+
]
|
1910
|
+
selects[0:0] = [select_first]
|
1911
|
+
select = '\nUNION\n'.join(selects)
|
1912
|
+
|
1913
|
+
# Create.
|
1914
|
+
sql = self.get_sql_create_view(path, select)
|
1915
|
+
|
1916
|
+
return sql
|
1917
|
+
|
1918
|
+
|
1919
|
+
def get_sql_drop_database(
|
1920
|
+
self,
|
1921
|
+
database: str
|
1922
|
+
) -> str:
|
1923
|
+
"""
|
1924
|
+
Get SQL of drop database.
|
1925
|
+
|
1926
|
+
Parameters
|
1927
|
+
----------
|
1928
|
+
database : Database name.
|
1929
|
+
execute : Whether directly execute.
|
1930
|
+
|
1931
|
+
Returns
|
1932
|
+
-------
|
1933
|
+
SQL.
|
1934
|
+
"""
|
1935
|
+
|
1936
|
+
# Generate.
|
1937
|
+
sql = f'DROP DATABASE `{database}`'
|
1938
|
+
|
1939
|
+
return sql
|
1940
|
+
|
1941
|
+
|
1942
|
+
def get_sql_drop_table(
|
1943
|
+
self,
|
1944
|
+
path: str | tuple[str, str]
|
1945
|
+
) -> str:
|
1946
|
+
"""
|
1947
|
+
Get SQL of drop table.
|
1948
|
+
|
1949
|
+
Parameters
|
1950
|
+
----------
|
1951
|
+
path : Path.
|
1952
|
+
- `str`: Table name.
|
1953
|
+
- `tuple[str, str]`: Database name and table name.
|
1954
|
+
execute : Whether directly execute.
|
1955
|
+
|
1956
|
+
Returns
|
1957
|
+
-------
|
1958
|
+
SQL.
|
1959
|
+
"""
|
1960
|
+
|
1961
|
+
# Set parameter.
|
1962
|
+
if type(path) == str:
|
1963
|
+
database, table = self.db.database, path
|
1964
|
+
else:
|
1965
|
+
database, table = path
|
1966
|
+
|
1967
|
+
# Generate.
|
1968
|
+
sql = f'DROP TABLE `{database}`.`{table}`'
|
1969
|
+
|
1970
|
+
return sql
|
1971
|
+
|
1972
|
+
|
1973
|
+
def get_sql_drop_view(
|
1974
|
+
self,
|
1975
|
+
path: str | tuple[str, str]
|
1976
|
+
) -> str:
|
1977
|
+
"""
|
1978
|
+
Get SQL of drop view.
|
1979
|
+
|
1980
|
+
Parameters
|
1981
|
+
----------
|
1982
|
+
path : Path.
|
1983
|
+
- `str`: Table name.
|
1984
|
+
- `tuple[str, str]`: Database name and table name.
|
1985
|
+
execute : Whether directly execute.
|
1986
|
+
|
1987
|
+
Returns
|
1988
|
+
-------
|
1989
|
+
SQL.
|
1990
|
+
"""
|
1991
|
+
|
1992
|
+
# Set parameter.
|
1993
|
+
if type(path) == str:
|
1994
|
+
database, table = self.db.database, path
|
1995
|
+
else:
|
1996
|
+
database, table = path
|
1997
|
+
|
1998
|
+
# Generate SQL.
|
1999
|
+
sql = 'DROP VIEW `%s`.`%s`' % (database, table)
|
2000
|
+
|
2001
|
+
return sql
|
2002
|
+
|
2003
|
+
|
2004
|
+
def get_sql_alter_database(
|
2005
|
+
self,
|
2006
|
+
database: str,
|
2007
|
+
character: str | None = None,
|
2008
|
+
collate: str | None = None
|
2009
|
+
) -> str:
|
2010
|
+
"""
|
2011
|
+
Get SQL of alter database.
|
2012
|
+
|
2013
|
+
Parameters
|
2014
|
+
----------
|
2015
|
+
database : Database name.
|
2016
|
+
character : Character set.
|
2017
|
+
- `None`: Not alter.
|
2018
|
+
- `str`: Alter to this value.
|
2019
|
+
collate : Collate rule.
|
2020
|
+
- `None`: Not alter.
|
2021
|
+
- `str`: Alter to this value.
|
2022
|
+
execute : Whether directly execute.
|
2023
|
+
|
2024
|
+
Returns
|
2025
|
+
-------
|
2026
|
+
SQL.
|
2027
|
+
"""
|
2028
|
+
|
2029
|
+
# Generate.
|
2030
|
+
|
2031
|
+
## Character.
|
2032
|
+
if character is None:
|
2033
|
+
sql_character = ''
|
2034
|
+
else:
|
2035
|
+
sql_character = f' CHARACTER SET {character}'
|
2036
|
+
|
2037
|
+
## Collate.
|
2038
|
+
if collate is None:
|
2039
|
+
sql_collate = ''
|
2040
|
+
else:
|
2041
|
+
sql_collate = f' COLLATE {collate}'
|
2042
|
+
|
2043
|
+
## Join.
|
2044
|
+
sql = f'ALTER DATABASE `{database}`{sql_character}{sql_collate}'
|
2045
|
+
|
2046
|
+
return sql
|
2047
|
+
|
2048
|
+
|
2049
|
+
def get_sql_alter_table_add(
|
2050
|
+
self,
|
2051
|
+
path: str | tuple[str, str],
|
2052
|
+
fields: FieldSet | list[FieldSet] | None = None,
|
2053
|
+
primary: str | list[str] | None = None,
|
2054
|
+
indexes: IndexSet | list[IndexSet] | None = None
|
2055
|
+
) -> str:
|
2056
|
+
"""
|
2057
|
+
Get SQL of alter table add filed.
|
2058
|
+
|
2059
|
+
Parameters
|
2060
|
+
----------
|
2061
|
+
path : Path.
|
2062
|
+
- `str`: Table name.
|
2063
|
+
- `tuple[str, str]`: Database name and table name.
|
2064
|
+
fields : Fields set table.
|
2065
|
+
- `Key 'name'`: Field name, required.
|
2066
|
+
- `Key 'type'`: Field type, required.
|
2067
|
+
- `Key 'constraint'`: Field constraint.
|
2068
|
+
`Empty or None`: Use 'DEFAULT NULL'.
|
2069
|
+
`str`: Use this value.
|
2070
|
+
- `Key 'comment'`: Field comment.
|
2071
|
+
`Empty or None`: Not comment.
|
2072
|
+
`str`: Use this value.
|
2073
|
+
- `Key 'position'`: Field position.
|
2074
|
+
`None`: Last.
|
2075
|
+
`Literal['first']`: First.
|
2076
|
+
`str`: After this field.
|
2077
|
+
primary : Primary key fields.
|
2078
|
+
- `str`: One field.
|
2079
|
+
- `list[str]`: Multiple fileds.
|
2080
|
+
indexes : Index set table.
|
2081
|
+
- `Key 'name'`: Index name, required.
|
2082
|
+
- `Key 'fields'`: Index fields, required.
|
2083
|
+
`str`: One field.
|
2084
|
+
`list[str]`: Multiple fileds.
|
2085
|
+
- `Key 'type'`: Index type.
|
2086
|
+
`Literal['noraml']`: Noraml key.
|
2087
|
+
`Literal['unique']`: Unique key.
|
2088
|
+
`Literal['fulltext']`: Full text key.
|
2089
|
+
`Literal['spatial']`: Spatial key.
|
2090
|
+
- `Key 'comment'`: Field comment.
|
2091
|
+
`Empty or None`: Not comment.
|
2092
|
+
`str`: Use this value.
|
2093
|
+
|
2094
|
+
Returns
|
2095
|
+
-------
|
2096
|
+
SQL.
|
2097
|
+
"""
|
2098
|
+
|
2099
|
+
# Set parameter.
|
2100
|
+
if type(path) == str:
|
2101
|
+
database, table = self.db.database, path
|
2102
|
+
else:
|
2103
|
+
database, table = path
|
2104
|
+
if fields.__class__ == dict:
|
2105
|
+
fields = [fields]
|
2106
|
+
if primary.__class__ == str:
|
2107
|
+
primary = [primary]
|
2108
|
+
if primary in ([], ['']):
|
2109
|
+
primary = None
|
2110
|
+
if indexes.__class__ == dict:
|
2111
|
+
indexes = [indexes]
|
2112
|
+
|
2113
|
+
## Compatible dictionary key name.
|
2114
|
+
fields = deepcopy(fields)
|
2115
|
+
for row in fields:
|
2116
|
+
row['type_'] = row.pop('type')
|
2117
|
+
if indexes is not None:
|
2118
|
+
indexes = deepcopy(indexes)
|
2119
|
+
for row in indexes:
|
2120
|
+
row['type_'] = row.pop('type')
|
2121
|
+
|
2122
|
+
# Generate.
|
2123
|
+
sql_content = []
|
2124
|
+
|
2125
|
+
## Fields.
|
2126
|
+
if fields is not None:
|
2127
|
+
sql_fields = [
|
2128
|
+
'COLUMN ' + self.__get_field_sql(**field)
|
2129
|
+
for field in fields
|
2130
|
+
]
|
2131
|
+
sql_content.extend(sql_fields)
|
2132
|
+
|
2133
|
+
## Primary.
|
2134
|
+
if primary is not None:
|
2135
|
+
keys = ', '.join(
|
2136
|
+
[
|
2137
|
+
f'`{key}`'
|
2138
|
+
for key in primary
|
2139
|
+
]
|
2140
|
+
)
|
2141
|
+
sql_primary = f'PRIMARY KEY ({keys}) USING BTREE'
|
2142
|
+
sql_content.append(sql_primary)
|
2143
|
+
|
2144
|
+
## Indexes.
|
2145
|
+
if indexes is not None:
|
2146
|
+
sql_indexes = [
|
2147
|
+
self.__get_index_sql(**index)
|
2148
|
+
for index in indexes
|
2149
|
+
]
|
2150
|
+
sql_content.extend(sql_indexes)
|
2151
|
+
|
2152
|
+
## Join.
|
2153
|
+
sql_content = ',\n ADD '.join(sql_content)
|
2154
|
+
sql = (
|
2155
|
+
f'ALTER TABLE `{database}`.`{table}`\n'
|
2156
|
+
f' ADD {sql_content}'
|
2157
|
+
)
|
2158
|
+
|
2159
|
+
return sql
|
2160
|
+
|
2161
|
+
|
2162
|
+
def get_sql_alter_table_drop(
|
2163
|
+
self,
|
2164
|
+
path: str | tuple[str, str],
|
2165
|
+
fields: str | list[str] | None = None,
|
2166
|
+
primary: bool = False,
|
2167
|
+
indexes: str | list[str] | None = None
|
2168
|
+
) -> str:
|
2169
|
+
"""
|
2170
|
+
Get SQL of alter table drop field.
|
2171
|
+
|
2172
|
+
Parameters
|
2173
|
+
----------
|
2174
|
+
path : Path.
|
2175
|
+
- `str`: Table name.
|
2176
|
+
- `tuple[str, str]`: Database name and table name.
|
2177
|
+
fields : Delete fields name.
|
2178
|
+
primary : Whether delete primary key.
|
2179
|
+
indexes : Delete indexes name.
|
2180
|
+
execute : Whether directly execute.
|
2181
|
+
|
2182
|
+
Returns
|
2183
|
+
-------
|
2184
|
+
SQL.
|
2185
|
+
"""
|
2186
|
+
|
2187
|
+
# Set parameter.
|
2188
|
+
if type(path) == str:
|
2189
|
+
database, table = self.db.database, path
|
2190
|
+
else:
|
2191
|
+
database, table = path
|
2192
|
+
if fields.__class__ == str:
|
2193
|
+
fields = [fields]
|
2194
|
+
if indexes.__class__ == str:
|
2195
|
+
indexes = [indexes]
|
2196
|
+
|
2197
|
+
# Generate.
|
2198
|
+
sql_content = []
|
2199
|
+
|
2200
|
+
## Fields.
|
2201
|
+
if fields is not None:
|
2202
|
+
sql_fields = [
|
2203
|
+
'COLUMN ' + field
|
2204
|
+
for field in fields
|
2205
|
+
]
|
2206
|
+
sql_content.extend(sql_fields)
|
2207
|
+
|
2208
|
+
## Primary.
|
2209
|
+
if primary:
|
2210
|
+
sql_primary = 'PRIMARY KEY'
|
2211
|
+
sql_content.append(sql_primary)
|
2212
|
+
|
2213
|
+
## Indexes.
|
2214
|
+
if indexes is not None:
|
2215
|
+
sql_indexes = [
|
2216
|
+
'INDEX ' + index
|
2217
|
+
for index in indexes
|
2218
|
+
]
|
2219
|
+
sql_content.extend(sql_indexes)
|
2220
|
+
|
2221
|
+
## Join.
|
2222
|
+
sql_content = ',\n DROP '.join(sql_content)
|
2223
|
+
sql = (
|
2224
|
+
f'ALTER TABLE `{database}`.`{table}`\n'
|
2225
|
+
f' DROP {sql_content}'
|
2226
|
+
)
|
2227
|
+
|
2228
|
+
return sql
|
2229
|
+
|
2230
|
+
|
2231
|
+
def get_sql_alter_table_change(
|
2232
|
+
self,
|
2233
|
+
path: str | tuple[str, str],
|
2234
|
+
fields: FieldSet | list[FieldSet] | None = None,
|
2235
|
+
rename: str | None = None,
|
2236
|
+
engine: str | None = None,
|
2237
|
+
increment: int | None = None,
|
2238
|
+
charset: str | None = None,
|
2239
|
+
collate: str | None = None
|
2240
|
+
) -> str:
|
2241
|
+
"""
|
2242
|
+
Get SQL of alter database.
|
2243
|
+
|
2244
|
+
Parameters
|
2245
|
+
----------
|
2246
|
+
path : Path.
|
2247
|
+
- `str`: Table name.
|
2248
|
+
- `tuple[str, str]`: Database name and table name.
|
2249
|
+
fields : Fields set table.
|
2250
|
+
- `Key 'name'`: Field name, required.
|
2251
|
+
- `Key 'type'`: Field type, required.
|
2252
|
+
- `Key 'constraint'`: Field constraint.
|
2253
|
+
`Empty or None`: Use 'DEFAULT NULL'.
|
2254
|
+
`str`: Use this value.
|
2255
|
+
- `Key 'comment'`: Field comment.
|
2256
|
+
`Empty or None`: Not comment.
|
2257
|
+
`str`: Use this value.
|
2258
|
+
- `Key 'position'`: Field position.
|
2259
|
+
`None`: Last.
|
2260
|
+
`Literal['first']`: First.
|
2261
|
+
`str`: After this field.
|
2262
|
+
- `Key 'old_name'`: Field old name.
|
2263
|
+
rename : Table new name.
|
2264
|
+
engine : Engine type.
|
2265
|
+
increment : Automatic Increment start value.
|
2266
|
+
charset : Charset type.
|
2267
|
+
collate : Collate type.
|
2268
|
+
execute : Whether directly execute.
|
2269
|
+
|
2270
|
+
Returns
|
2271
|
+
-------
|
2272
|
+
SQL.
|
2273
|
+
"""
|
2274
|
+
|
2275
|
+
# Set parameter.
|
2276
|
+
if type(path) == str:
|
2277
|
+
database, table = self.db.database, path
|
2278
|
+
else:
|
2279
|
+
database, table = path
|
2280
|
+
if fields.__class__ == dict:
|
2281
|
+
fields = [fields]
|
2282
|
+
|
2283
|
+
## Compatible dictionary key name.
|
2284
|
+
fields = deepcopy(fields)
|
2285
|
+
for row in fields:
|
2286
|
+
row['type_'] = row.pop('type')
|
2287
|
+
|
2288
|
+
# Generate.
|
2289
|
+
sql_content = []
|
2290
|
+
|
2291
|
+
## Rename.
|
2292
|
+
if rename is not None:
|
2293
|
+
sql_rename = f'RENAME `{database}`.`{rename}`'
|
2294
|
+
sql_content.append(sql_rename)
|
2295
|
+
|
2296
|
+
## Fields.
|
2297
|
+
if fields is not None:
|
2298
|
+
sql_fields = [
|
2299
|
+
'%s %s' % (
|
2300
|
+
(
|
2301
|
+
'MODIFY'
|
2302
|
+
if 'old_name' not in field
|
2303
|
+
else 'CHANGE'
|
2304
|
+
),
|
2305
|
+
self.__get_field_sql(**field)
|
2306
|
+
)
|
2307
|
+
for field in fields
|
2308
|
+
]
|
2309
|
+
sql_content.extend(sql_fields)
|
2310
|
+
|
2311
|
+
## Attribute.
|
2312
|
+
sql_attr = []
|
2313
|
+
|
2314
|
+
### Engine.
|
2315
|
+
if engine is not None:
|
2316
|
+
sql_engine = f'ENGINE={engine}'
|
2317
|
+
sql_attr.append(sql_engine)
|
2318
|
+
|
2319
|
+
### Increment.
|
2320
|
+
if increment is not None:
|
2321
|
+
sql_increment = f'AUTO_INCREMENT={increment}'
|
2322
|
+
sql_attr.append(sql_increment)
|
2323
|
+
|
2324
|
+
### Charset.
|
2325
|
+
if charset is not None:
|
2326
|
+
sql_charset = f'CHARSET={charset}'
|
2327
|
+
sql_attr.append(sql_charset)
|
2328
|
+
|
2329
|
+
### Collate.
|
2330
|
+
if collate is not None:
|
2331
|
+
sql_collate = f'COLLATE={collate}'
|
2332
|
+
sql_attr.append(sql_collate)
|
2333
|
+
|
2334
|
+
if sql_attr != []:
|
2335
|
+
sql_attr = ' '.join(sql_attr)
|
2336
|
+
sql_content.append(sql_attr)
|
2337
|
+
|
2338
|
+
## Join.
|
2339
|
+
sql_content = ',\n '.join(sql_content)
|
2340
|
+
sql = (
|
2341
|
+
f'ALTER TABLE `{database}`.`{table}`\n'
|
2342
|
+
f' {sql_content}'
|
2343
|
+
)
|
2344
|
+
|
2345
|
+
return sql
|
2346
|
+
|
2347
|
+
|
2348
|
+
def get_sql_alter_view(
|
2349
|
+
self,
|
2350
|
+
path: str | tuple[str, str],
|
2351
|
+
select: str
|
2352
|
+
) -> str:
|
2353
|
+
"""
|
2354
|
+
Get SQL of alter view.
|
2355
|
+
|
2356
|
+
Parameters
|
2357
|
+
----------
|
2358
|
+
path : Path.
|
2359
|
+
- `str`: Table name.
|
2360
|
+
- `tuple[str, str]`: Database name and table name.
|
2361
|
+
select : View select SQL.
|
2362
|
+
execute : Whether directly execute.
|
2363
|
+
|
2364
|
+
Returns
|
2365
|
+
-------
|
2366
|
+
SQL.
|
2367
|
+
"""
|
2368
|
+
|
2369
|
+
# Set parameter.
|
2370
|
+
if type(path) == str:
|
2371
|
+
database, table = self.db.database, path
|
2372
|
+
else:
|
2373
|
+
database, table = path
|
2374
|
+
|
2375
|
+
# Generate SQL.
|
2376
|
+
sql = 'ALTER VIEW `%s`.`%s` AS\n%s' % (database, table, select)
|
2377
|
+
|
2378
|
+
return sql
|
2379
|
+
|
2380
|
+
|
2381
|
+
def get_sql_truncate_table(
|
2382
|
+
self,
|
2383
|
+
path: str | tuple[str, str]
|
2384
|
+
) -> str:
|
2385
|
+
"""
|
2386
|
+
Get SQL of truncate table.
|
2387
|
+
|
2388
|
+
Parameters
|
2389
|
+
----------
|
2390
|
+
path : Path.
|
2391
|
+
- `str`: Table name.
|
2392
|
+
- `tuple[str, str]`: Database name and table name.
|
2393
|
+
execute : Whether directly execute.
|
2394
|
+
|
2395
|
+
Returns
|
2396
|
+
-------
|
2397
|
+
SQL.
|
2398
|
+
"""
|
2399
|
+
|
2400
|
+
# Set parameter.
|
2401
|
+
if type(path) == str:
|
2402
|
+
database, table = self.db.database, path
|
2403
|
+
else:
|
2404
|
+
database, table = path
|
2405
|
+
|
2406
|
+
# Generate.
|
2407
|
+
sql = f'TRUNCATE TABLE `{database}`.`{table}`'
|
2408
|
+
|
2409
|
+
return sql
|
2410
|
+
|
2411
|
+
|
2412
|
+
def input_confirm_build(
|
2413
|
+
self,
|
2414
|
+
sql: str
|
2415
|
+
) -> None:
|
2416
|
+
"""
|
2417
|
+
Print tip text, and confirm execute SQL. If reject, throw exception.
|
2418
|
+
|
2419
|
+
Parameters
|
2420
|
+
----------
|
2421
|
+
sql : SQL.
|
2422
|
+
"""
|
2423
|
+
|
2424
|
+
# Confirm.
|
2425
|
+
text = 'Do you want to execute SQL to build the database? Otherwise stop program. (y/n) '
|
2426
|
+
command = ask(
|
2427
|
+
sql,
|
2428
|
+
text,
|
2429
|
+
title='SQL',
|
2430
|
+
frame='top'
|
2431
|
+
)
|
2432
|
+
|
2433
|
+
# Check.
|
2434
|
+
while True:
|
2435
|
+
command = command.lower()
|
2436
|
+
match command:
|
2437
|
+
|
2438
|
+
## Confirm.
|
2439
|
+
case 'y':
|
2440
|
+
break
|
2441
|
+
|
2442
|
+
## Stop.
|
2443
|
+
case 'n':
|
2444
|
+
raise AssertionError('program stop')
|
2445
|
+
|
2446
|
+
## Reenter.
|
2447
|
+
case _:
|
2448
|
+
text = 'Incorrect input, reenter. (y/n) '
|
2449
|
+
command = input(text)
|
2450
|
+
|
2451
|
+
|
2452
|
+
def get_orm_table_text(self, model: DatabaseORMModel) -> str:
|
2453
|
+
"""
|
2454
|
+
Get table text from ORM model.
|
2455
|
+
|
2456
|
+
Parameters
|
2457
|
+
----------
|
2458
|
+
model : ORM model instances.
|
2459
|
+
|
2460
|
+
Returns
|
2461
|
+
-------
|
2462
|
+
Table text.
|
2463
|
+
"""
|
2464
|
+
|
2465
|
+
# Get.
|
2466
|
+
table = model._table()
|
2467
|
+
text = f'TABLE `{self.db.database}`.`{table}`'
|
2468
|
+
if 'mysql_charset' in table.kwargs:
|
2469
|
+
text += f" | CHARSET '{table.kwargs['mysql_charset']}'"
|
2470
|
+
if table.comment:
|
2471
|
+
text += f" | COMMENT '{table.comment}'"
|
2472
|
+
|
2473
|
+
## Field.
|
2474
|
+
text += '\n' + '\n'.join(
|
2475
|
+
[
|
2476
|
+
f' FIELD `{column.name}` : {column.type}' + (
|
2477
|
+
' | NOT NULL'
|
2478
|
+
if (
|
2479
|
+
not column.nullable
|
2480
|
+
or column.primary_key
|
2481
|
+
)
|
2482
|
+
else ' | NULL'
|
2483
|
+
) + (
|
2484
|
+
''
|
2485
|
+
if not column.primary_key
|
2486
|
+
else ' | KEY AUTO'
|
2487
|
+
if column.autoincrement
|
2488
|
+
else ' | KEY'
|
2489
|
+
) + (
|
2490
|
+
f" | DEFAULT '{column.server_default.arg}'"
|
2491
|
+
if column.server_default
|
2492
|
+
else ''
|
2493
|
+
) + (
|
2494
|
+
f" | COMMMENT '{column.comment}'"
|
2495
|
+
if column.comment
|
2496
|
+
else ''
|
2497
|
+
)
|
2498
|
+
for column in table.columns
|
2499
|
+
]
|
2500
|
+
)
|
2501
|
+
|
2502
|
+
## Index.
|
2503
|
+
if (table.indexes):
|
2504
|
+
text += '\n' + '\n'.join(
|
2505
|
+
[
|
2506
|
+
(
|
2507
|
+
' UNIQUE'
|
2508
|
+
if index.unique
|
2509
|
+
else ' NORMAL'
|
2510
|
+
) + f' INDEX `{index.name}` : ' + ', '.join(
|
2511
|
+
[
|
2512
|
+
f'`{column.name}`'
|
2513
|
+
for column in index.expressions
|
2514
|
+
]
|
2515
|
+
)
|
2516
|
+
for index in table.indexes
|
2517
|
+
]
|
2518
|
+
)
|
2519
|
+
|
2520
|
+
return text
|
2521
|
+
|
2522
|
+
|
2523
|
+
class DatabaseBuild(DatabaseBuildSuper['rdb.Database']):
|
2524
|
+
"""
|
2525
|
+
Database build type.
|
2526
|
+
"""
|
2527
|
+
|
2528
|
+
|
2529
|
+
def create_orm_table(
|
2530
|
+
self,
|
2531
|
+
*models: Type[DatabaseORMModel] | DatabaseORMModel,
|
2532
|
+
skip: bool = False
|
2533
|
+
) -> None:
|
2534
|
+
"""
|
2535
|
+
Create tables by ORM model.
|
2536
|
+
|
2537
|
+
Parameters
|
2538
|
+
----------
|
2539
|
+
models : ORM model instances.
|
2540
|
+
skip : Whether skip existing table.
|
2541
|
+
"""
|
2542
|
+
|
2543
|
+
# Create.
|
2544
|
+
self.db.orm.create(*models, skip=skip)
|
2545
|
+
|
2546
|
+
|
2547
|
+
def drop_orm_table(
|
2548
|
+
self,
|
2549
|
+
*models: Type[DatabaseORMModel] | DatabaseORMModel,
|
2550
|
+
skip: bool = False
|
2551
|
+
) -> None:
|
2552
|
+
"""
|
2553
|
+
Delete tables by model.
|
2554
|
+
|
2555
|
+
Parameters
|
2556
|
+
----------
|
2557
|
+
models : ORM model instances.
|
2558
|
+
skip : Skip not exist table.
|
2559
|
+
"""
|
2560
|
+
|
2561
|
+
# Drop.
|
2562
|
+
self.db.orm.drop(*models, skip=skip)
|
2563
|
+
|
2564
|
+
|
2565
|
+
def build(
|
2566
|
+
self,
|
2567
|
+
databases: list[dict] | None = None,
|
2568
|
+
tables: list[dict | Type[DatabaseORMModel] | DatabaseORMModel] | None = None,
|
2569
|
+
views: list[dict] | None = None,
|
2570
|
+
views_stats: list[dict] | None = None,
|
2571
|
+
ask: bool = True,
|
2572
|
+
skip: bool = False
|
2573
|
+
) -> None:
|
2574
|
+
"""
|
2575
|
+
Build databases or tables.
|
2576
|
+
|
2577
|
+
Parameters
|
2578
|
+
----------
|
2579
|
+
databases : Database build parameters, equivalent to the parameters of method `self.create_database`.
|
2580
|
+
tables : Tables build parameters or model, equivalent to the parameters of method `self.create_table` or `self.create_orm_table`.
|
2581
|
+
views : Views build parameters, equivalent to the parameters of method `self.create_view`.
|
2582
|
+
views_stats : Views stats build parameters, equivalent to the parameters of method `self.create_view_stats`.
|
2583
|
+
ask : Whether ask confirm execute.
|
2584
|
+
skip : Whether skip existing table.
|
2585
|
+
"""
|
2586
|
+
|
2587
|
+
# Set parameter.
|
2588
|
+
databases = databases or []
|
2589
|
+
tables = tables or []
|
2590
|
+
views = views or []
|
2591
|
+
views_stats = views_stats or []
|
2592
|
+
refresh_schema = False
|
2593
|
+
|
2594
|
+
# Database.
|
2595
|
+
for params in databases:
|
2596
|
+
database = params['name']
|
2597
|
+
|
2598
|
+
## Exist.
|
2599
|
+
if (
|
2600
|
+
skip
|
2601
|
+
and self.db.schema.exist(database)
|
2602
|
+
):
|
2603
|
+
continue
|
2604
|
+
|
2605
|
+
## SQL.
|
2606
|
+
sql = self.get_sql_create_database(**params)
|
2607
|
+
|
2608
|
+
## Confirm.
|
2609
|
+
if ask:
|
2610
|
+
self.input_confirm_build(sql)
|
2611
|
+
|
2612
|
+
## Execute.
|
2613
|
+
self.db.execute(sql)
|
2614
|
+
|
2615
|
+
## Report.
|
2616
|
+
text = f"Database '{database}' build completed."
|
2617
|
+
print(text)
|
2618
|
+
|
2619
|
+
# Table.
|
2620
|
+
for params in tables:
|
2621
|
+
|
2622
|
+
## ORM.
|
2623
|
+
if (
|
2624
|
+
is_instance(params)
|
2625
|
+
and isinstance(params, DatabaseORMModel)
|
2626
|
+
or issubclass(params, DatabaseORMModel)
|
2627
|
+
):
|
2628
|
+
database = self.db.database
|
2629
|
+
table = params._table().name
|
2630
|
+
|
2631
|
+
## Exist.
|
2632
|
+
if (
|
2633
|
+
skip
|
2634
|
+
and self.db.schema.exist(self.db.database, table)
|
2635
|
+
):
|
2636
|
+
continue
|
2637
|
+
|
2638
|
+
## Confirm.
|
2639
|
+
if ask:
|
2640
|
+
text = self.get_orm_table_text(params)
|
2641
|
+
self.input_confirm_build(text)
|
2642
|
+
|
2643
|
+
## Execute.
|
2644
|
+
self.create_orm_table(params)
|
2645
|
+
|
2646
|
+
## Parameter.
|
2647
|
+
else:
|
2648
|
+
path: str | tuple[str, str] = params['path']
|
2649
|
+
if type(path) == str:
|
2650
|
+
database, table = self.db.database, path
|
2651
|
+
else:
|
2652
|
+
database, table = path
|
2653
|
+
|
2654
|
+
### Exist.
|
2655
|
+
if (
|
2656
|
+
skip
|
2657
|
+
and self.db.schema.exist(database, table)
|
2658
|
+
):
|
2659
|
+
continue
|
2660
|
+
|
2661
|
+
### SQL.
|
2662
|
+
sql = self.get_sql_create_table(**params)
|
2663
|
+
|
2664
|
+
### Confirm.
|
2665
|
+
if ask:
|
2666
|
+
self.input_confirm_build(sql)
|
2667
|
+
|
2668
|
+
### Execute.
|
2669
|
+
self.db.execute(sql)
|
2670
|
+
|
2671
|
+
## Report.
|
2672
|
+
text = f"Table '{table}' of database '{database}' build completed."
|
2673
|
+
print(text)
|
2674
|
+
refresh_schema = True
|
2675
|
+
|
2676
|
+
# Refresh schema.
|
2677
|
+
if refresh_schema:
|
2678
|
+
self.db.schema()
|
2679
|
+
|
1200
2680
|
# View.
|
1201
2681
|
for params in views:
|
1202
2682
|
path = params['path']
|
@@ -1324,7 +2804,7 @@ class DatabaseBuildAsync(DatabaseBuildSuper['rdb.DatabaseAsync']):
|
|
1324
2804
|
skip : Whether skip existing table.
|
1325
2805
|
"""
|
1326
2806
|
|
1327
|
-
#
|
2807
|
+
# Set parameter.
|
1328
2808
|
databases = databases or []
|
1329
2809
|
tables = tables or []
|
1330
2810
|
tables_orm = tables_orm or []
|