dbbridgekit 0.1.0__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.
- dbbridge/__init__.py +22 -0
- dbbridge/cli.py +177 -0
- dbbridge/codemod/__init__.py +3 -0
- dbbridge/codemod/codemod.py +194 -0
- dbbridge/compat/__init__.py +3 -0
- dbbridge/compat/runtime.py +193 -0
- dbbridge/core/__init__.py +2 -0
- dbbridge/core/ir.py +106 -0
- dbbridge/core/parser.py +60 -0
- dbbridge/core/planner.py +44 -0
- dbbridge/core/renderer.py +57 -0
- dbbridge/core/sql_lex.py +61 -0
- dbbridge/core/types.py +47 -0
- dbbridge/dialects/__init__.py +6 -0
- dbbridge/dialects/_stub.py +31 -0
- dbbridge/dialects/mysql.py +16 -0
- dbbridge/dialects/postgres.py +292 -0
- dbbridge/dialects/sqlite.py +279 -0
- dbbridge/dialects/sqlserver.py +15 -0
- dbbridge/migration/__init__.py +11 -0
- dbbridge/migration/data_migration.py +76 -0
- dbbridge/migration/rollback.py +57 -0
- dbbridge/migration/schema_migration.py +56 -0
- dbbridge/migration/validator.py +85 -0
- dbbridge/reports/__init__.py +3 -0
- dbbridge/reports/report.py +58 -0
- dbbridge/scanner/__init__.py +3 -0
- dbbridge/scanner/scanner.py +176 -0
- dbbridgekit-0.1.0.dist-info/METADATA +203 -0
- dbbridgekit-0.1.0.dist-info/RECORD +34 -0
- dbbridgekit-0.1.0.dist-info/WHEEL +5 -0
- dbbridgekit-0.1.0.dist-info/entry_points.txt +2 -0
- dbbridgekit-0.1.0.dist-info/licenses/LICENSE +21 -0
- dbbridgekit-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dbbridgekit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Plataforma genérica de conversão/análise/migração entre bancos de dados via Intermediate Representation (IR) canônica — SQLite, PostgreSQL, MySQL (em progresso), SQL Server (planejado).
|
|
5
|
+
Author-email: Otavio R Santana <tvostrodrigues8@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tvost2/dbbridge
|
|
8
|
+
Project-URL: Repository, https://github.com/tvost2/dbbridge
|
|
9
|
+
Project-URL: Issues, https://github.com/tvost2/dbbridge/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/tvost2/dbbridge/blob/main/docs/guide.md
|
|
11
|
+
Project-URL: Changelog, https://github.com/tvost2/dbbridge/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: sql,sqlite,postgresql,mysql,migration,ir,database
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Database
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: psycopg[binary]>=3.1
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# DBBridge
|
|
38
|
+
|
|
39
|
+
Plataforma genérica de conversão, análise e migração entre bancos de dados, baseada numa
|
|
40
|
+
**Intermediate Representation (IR)** canônica — não um conversor pareado por combinação de bancos.
|
|
41
|
+
|
|
42
|
+
```txt
|
|
43
|
+
Banco origem → Parser do dialeto origem → IR canônica → Renderer do dialeto destino → Banco destino
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Evolução do [PostgresModel](../postgresmodel) generalizada: em vez de assumir SQLite→PostgreSQL
|
|
47
|
+
sempre, qualquer par de dialetos suportados passa pelo mesmo pipeline.
|
|
48
|
+
|
|
49
|
+
## Status (Fase 1)
|
|
50
|
+
|
|
51
|
+
| Dialeto | Parser | Renderer |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| SQLite | ✅ | ✅ |
|
|
54
|
+
| PostgreSQL | ✅ | ✅ |
|
|
55
|
+
| MySQL | estrutura registrada (Fase 2) | estrutura registrada (Fase 2) |
|
|
56
|
+
| SQL Server | estrutura registrada (Fase 5) | estrutura registrada (Fase 5) |
|
|
57
|
+
|
|
58
|
+
`dbbridge.dialects.mysql`/`sqlserver` já registram um dialeto "conhecido, ainda não implementado" —
|
|
59
|
+
`dbbridge dialects` lista os dois, e chamar `.parse()`/`.render_schema()` neles levanta
|
|
60
|
+
`NotImplementedError` com a fase planejada, em vez de um erro genérico de dialeto desconhecido.
|
|
61
|
+
|
|
62
|
+
167 testes, 99% de cobertura (`pytest --cov=dbbridge`). Os poucos pontos não cobertos são código
|
|
63
|
+
genuinamente inalcançável (stubs de método abstrato, guard `if __name__ == "__main__"`) — ver
|
|
64
|
+
[docs/guide.md](docs/guide.md#cobertura-de-testes).
|
|
65
|
+
|
|
66
|
+
## Instalação
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install -e .
|
|
70
|
+
# ou, pra rodar contra PostgreSQL de verdade:
|
|
71
|
+
pip install -e ".[dev]"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Uso rápido
|
|
75
|
+
|
|
76
|
+
### Traduzir um schema
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
dbbridge translate --from sqlite --to postgres schema.sql
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Casos que o parser/renderer não reconhecem com segurança viram `REVIEW_REQUIRED` no stderr — nunca
|
|
83
|
+
uma tradução inventada.
|
|
84
|
+
|
|
85
|
+
### Escanear um projeto Python
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
dbbridge scan --from sqlite --to postgres ./meu_projeto
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Encontra `execute()`/`executemany()` com SQL potencialmente incompatível entre os dois dialetos:
|
|
92
|
+
placeholders (`?` vs `%s`), `sqlite_master`, `PRAGMA`, `INSERT OR IGNORE`/`OR REPLACE`,
|
|
93
|
+
case-sensitivity de `LIKE`, autoincrement, e mais.
|
|
94
|
+
|
|
95
|
+
### Migrar código-fonte automaticamente
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
dbbridge patch --from sqlite --to postgres ./meu_projeto # dry-run, mostra o plano
|
|
99
|
+
dbbridge apply --from sqlite --to postgres ./meu_projeto # aplica com backup automático
|
|
100
|
+
dbbridge rollback ./meu_projeto # desfaz, restaura do backup
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Reescreve só literais de string simples sem ambiguidade (troca de placeholder). `f-strings`,
|
|
104
|
+
concatenação e casos ambíguos (aspas duplas, `INSERT OR REPLACE`, etc.) nunca são alterados
|
|
105
|
+
automaticamente — viram `REVIEW_REQUIRED`/`SKIPPED_COMPLEX_EXPRESSION` no plano.
|
|
106
|
+
|
|
107
|
+
### Migrar dados entre bancos de verdade
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
dbbridge migrate-data --from sqlite --to postgres \
|
|
111
|
+
--source-dsn ./app.db --target-dsn "host=localhost dbname=app" --tables users leads
|
|
112
|
+
dbbridge validate --from sqlite --to postgres \
|
|
113
|
+
--source-dsn ./app.db --target-dsn "host=localhost dbname=app" --tables users leads
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Copia em lotes, nunca faz `DROP`/`TRUNCATE`, sempre acrescenta. `validate` compara contagens e
|
|
117
|
+
checksum (SHA-256) linha a linha quando a tabela é pequena o bastante.
|
|
118
|
+
|
|
119
|
+
### Compatibility Mode (código antigo continua rodando)
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from dbbridge import connect
|
|
123
|
+
|
|
124
|
+
db = connect(source_dialect="sqlite", target_dialect="postgres", dsn="host=localhost dbname=app")
|
|
125
|
+
db.execute("SELECT * FROM users WHERE id=?", (1,)) # vira %s por baixo, roda no Postgres de verdade
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Permite migrar a aplicação por partes: o código continua escrito na sintaxe de origem enquanto o
|
|
129
|
+
banco de verdade já é o destino.
|
|
130
|
+
|
|
131
|
+
## Exemplos executáveis
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
python examples/translate_schema.py # traduz um schema via API Python
|
|
135
|
+
python examples/scan_and_patch_project.py # scan -> patch -> apply -> rollback num mini-app
|
|
136
|
+
python examples/compat_mode.py # Compatibility Mode de ponta a ponta
|
|
137
|
+
DBBRIDGE_EXAMPLE_PG_DSN="host=localhost dbname=..." python examples/full_data_migration.py
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Todos os quatro rodam de verdade (não são pseudocódigo) — os três primeiros só precisam do SQLite
|
|
141
|
+
da stdlib; o último precisa de um PostgreSQL acessível via a variável de ambiente indicada.
|
|
142
|
+
|
|
143
|
+
## Arquitetura
|
|
144
|
+
|
|
145
|
+
```txt
|
|
146
|
+
dbbridge/
|
|
147
|
+
cli.py # scan|report|translate|patch|apply|migrate-data|validate|rollback|doctor|dialects
|
|
148
|
+
core/
|
|
149
|
+
ir.py # Schema, Table, Column, ForeignKey, UniqueConstraint, CheckConstraint, Index, View, Trigger, Enum
|
|
150
|
+
types.py # CanonicalType, Ambiguity
|
|
151
|
+
parser.py / renderer.py # contratos + registry (register_parser/get_parser/available_parsers, idem renderer)
|
|
152
|
+
planner.py # translate(sql, source, target) — o pipeline completo numa função
|
|
153
|
+
dialects/
|
|
154
|
+
sqlite.py, postgres.py # implementados
|
|
155
|
+
mysql.py, sqlserver.py # stubs registrados (NotImplementedError com a fase planejada)
|
|
156
|
+
scanner/scanner.py # AST — encontra SQL arriscado por par de dialetos
|
|
157
|
+
codemod/codemod.py # AST — reescreve só literais simples sem ambiguidade
|
|
158
|
+
migration/
|
|
159
|
+
schema_migration.py # traduz + aplica DDL (aditivo, nunca DROP/TRUNCATE)
|
|
160
|
+
data_migration.py # copia dados em lotes
|
|
161
|
+
validator.py # contagens + checksum
|
|
162
|
+
rollback.py # restaura código a partir de backup
|
|
163
|
+
compat/runtime.py # connect() — Compatibility Mode
|
|
164
|
+
reports/report.py # normaliza qualquer resultado pra texto/JSON
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Por que IR em vez de conversores pareados: com N dialetos, um conversor direto por par cresce O(N²)
|
|
168
|
+
e duplica a mesma lógica de tipos/constraints em cada combinação. Com um modelo canônico no meio,
|
|
169
|
+
cada dialeto novo precisa de só 1 parser + 1 renderer (O(N)) pra já converter de/para todos os
|
|
170
|
+
outros já implementados.
|
|
171
|
+
|
|
172
|
+
## Segurança
|
|
173
|
+
|
|
174
|
+
- Toda alteração de código gera backup antes de escrever, e pode ser revertida via `dbbridge rollback`.
|
|
175
|
+
- Migração de schema/dados é sempre aditiva — nunca `DROP`/`TRUNCATE`/`DELETE`.
|
|
176
|
+
- Casos ambíguos (aspas duplas em literal, `INSERT OR REPLACE`, f-strings, expressões dinâmicas)
|
|
177
|
+
nunca são "adivinhados" — viram `REVIEW_REQUIRED` pra revisão manual.
|
|
178
|
+
- `apply_changes` verifica `ast.parse()` do arquivo resultante antes de considerar sucesso; se o
|
|
179
|
+
resultado tiver `SyntaxError`, reverte sozinho a partir do backup.
|
|
180
|
+
|
|
181
|
+
## Fases
|
|
182
|
+
|
|
183
|
+
1. **IR + SQLite ↔ PostgreSQL** — atual, completo: parser/renderer dos dois dialetos, scanner,
|
|
184
|
+
codemod, migração de schema/dados, validação por checksum, rollback, CLI, Compatibility Mode.
|
|
185
|
+
2. MySQL (parser + renderer reais).
|
|
186
|
+
3. Codemod genérico validado contra os 3 dialetos.
|
|
187
|
+
4. Data migration validada nos 4 pares centrais (SQLite/Postgres/MySQL cruzados).
|
|
188
|
+
5. SQL Server.
|
|
189
|
+
6. **Fases futuras (levantadas, ainda não priorizadas):** shadow migration (rodar origem e destino
|
|
190
|
+
em paralelo comparando resultados antes do cutover final), query replay (reproduzir tráfego real
|
|
191
|
+
de produção contra o destino como teste de carga/compatibilidade), dashboard web, relatórios em
|
|
192
|
+
HTML/PDF (hoje só texto/JSON via `reports/report.py`), mecanismo de plugin formal pra dialetos
|
|
193
|
+
externos (hoje a extensão já é possível via `register_parser`/`register_renderer` — falta só
|
|
194
|
+
empacotar como plugin instalável separadamente, ex. entry points do Python), e suporte a bancos
|
|
195
|
+
não-relacionais como MongoDB (exigiria uma IR paralela pra documentos/coleções — ver
|
|
196
|
+
[docs/compatibility-matrix.md](docs/compatibility-matrix.md#roadmap-além-do-sql-relacional)).
|
|
197
|
+
|
|
198
|
+
Cada fase só avança depois da anterior validada — ver [docs/guide.md](docs/guide.md) para detalhes
|
|
199
|
+
de arquitetura e extensão pra novos dialetos, [docs/cli-reference.md](docs/cli-reference.md) pra
|
|
200
|
+
todos os comandos, [docs/compatibility-matrix.md](docs/compatibility-matrix.md) pro que já
|
|
201
|
+
funciona vs. planejado, [docs/migration-tutorial.md](docs/migration-tutorial.md) pro passo a passo
|
|
202
|
+
completo, e [docs/production-checklist.md](docs/production-checklist.md) antes de rodar contra
|
|
203
|
+
dados reais.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
dbbridge/__init__.py,sha256=mqAYHp2tFx35S1_q7h9Rz1TslbUszTuWbfZHaOJ_Fig,1055
|
|
2
|
+
dbbridge/cli.py,sha256=J5J1_FLSy13dyyAJfbm2vDYbaKT7QFT95VsRJpw6CTw,8269
|
|
3
|
+
dbbridge/codemod/__init__.py,sha256=biRfAiFhJP8XxfwlBmyCoUniwzGumL0vGFhxE6VXgSk,212
|
|
4
|
+
dbbridge/codemod/codemod.py,sha256=ocQ1Der4f3w8ODDtMIpYZhsHIy3x3RPx1fGlUDd5H6M,7444
|
|
5
|
+
dbbridge/compat/__init__.py,sha256=gUNC_UnfYKbIw70nFjUKuc9bXiEGmX0vIT0F2hlz0kI,144
|
|
6
|
+
dbbridge/compat/runtime.py,sha256=dzasZz1S1r0kWpkE_1lwMIwkIGAYo3I98OGXl6FD7Ak,6993
|
|
7
|
+
dbbridge/core/__init__.py,sha256=Y1rv3nLtwXhqEXZHS67uc6JxzcH6G_WENJjigX0Fif4,204
|
|
8
|
+
dbbridge/core/ir.py,sha256=4aer_ShsgYcaxzNjgsIKH7EN1BtfLV1jhwO24xY_JO0,3197
|
|
9
|
+
dbbridge/core/parser.py,sha256=x7EgboyXGxvDTsfsZkbDv-j8qqHo52RX4Z4JFgJcA9Q,1723
|
|
10
|
+
dbbridge/core/planner.py,sha256=YLlCOEdLX4_bY_yRPSCb8R52VIVdjorwxpbUdSlT-7Q,1635
|
|
11
|
+
dbbridge/core/renderer.py,sha256=qE3Rw8hfvLWcmn59wRj7KfSK9zmlguPZjpYX5_dXFc4,1615
|
|
12
|
+
dbbridge/core/sql_lex.py,sha256=-2_ecX78srF0khEfewqa-kKg8i8ZXXo_CrXKNDI5tss,2214
|
|
13
|
+
dbbridge/core/types.py,sha256=C8ecH3TIG9LfEdQx07aocOp_EBO8QGTlmLhWtmjVwig,1915
|
|
14
|
+
dbbridge/dialects/__init__.py,sha256=fpvZlp6Ss3xWdqpwy55pNLb4AqH_MpHc71Yxu93KPyA,578
|
|
15
|
+
dbbridge/dialects/_stub.py,sha256=M7ZjZ0WGKKzC5l4Tl1iz9mxaNCDezxlkiDb68xmsHho,1494
|
|
16
|
+
dbbridge/dialects/mysql.py,sha256=rMv_4p69-CEwDT9em_L3VSfHS1Tozszou2Bt3p6p3sk,882
|
|
17
|
+
dbbridge/dialects/postgres.py,sha256=JanJ1-8oLOTtE5RMEVh-9UeYylGWIrnkfLJxjZsQP8M,14715
|
|
18
|
+
dbbridge/dialects/sqlite.py,sha256=8BaFqYB8mBFBkeXOTKjd6TuCpEV06CBdpPWZnEoUUwY,13370
|
|
19
|
+
dbbridge/dialects/sqlserver.py,sha256=5b7lXKcgVaBGz_SfORjxg-zSXC3QTZAqhFdG9oYPw-U,827
|
|
20
|
+
dbbridge/migration/__init__.py,sha256=mIijSHU-YySvK8P2REJjURP2OuFpBp3A9KE878cwmfg,660
|
|
21
|
+
dbbridge/migration/data_migration.py,sha256=b7oc7jZoygo7JlrGdfjBmSH5ARGkyffBQhiU_m81oV0,3081
|
|
22
|
+
dbbridge/migration/rollback.py,sha256=OfFO6m6oYoYZM0DF-R-va9u3xVqvowBWw4ReNvyZrzk,2773
|
|
23
|
+
dbbridge/migration/schema_migration.py,sha256=PwULtiY-yzm4_J_Y1TnGiqYyW2Tf-qfAmKCbjFUMGwE,2563
|
|
24
|
+
dbbridge/migration/validator.py,sha256=0ToAA5I_v_E9MqNldYIlXF-WMp8QX1mosD6aClLkVZU,3533
|
|
25
|
+
dbbridge/reports/__init__.py,sha256=FkbDWy0IoFG9j7j5TfyVHt_ty2kajqml-jOeAwdCCoM,63
|
|
26
|
+
dbbridge/reports/report.py,sha256=DEJgUpp8SAz0ubfWn8T4XJvBAIPACppYIZJF2XYyrUI,2174
|
|
27
|
+
dbbridge/scanner/__init__.py,sha256=jxSHb0B0cc0VsQ1SrbZugnzpBk-rMwlPzrSnI-7hrQk,132
|
|
28
|
+
dbbridge/scanner/scanner.py,sha256=yNX8tJvY7X1kt50ovK_00Rm0CjqdFBPDvVkETqmPN7A,7363
|
|
29
|
+
dbbridgekit-0.1.0.dist-info/licenses/LICENSE,sha256=w420EVFiwiE-sHoWuBWBFU43utsywuNB1j6lxMhixnM,1073
|
|
30
|
+
dbbridgekit-0.1.0.dist-info/METADATA,sha256=F-PCsUyBbicojFNVH5lzu2ZIsY61axC4Y2VaGwjOgCw,9885
|
|
31
|
+
dbbridgekit-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
32
|
+
dbbridgekit-0.1.0.dist-info/entry_points.txt,sha256=LeXVMCSu5O3QLbE7VsZAMW-9rwkA-cqu5buh5fkNq3A,47
|
|
33
|
+
dbbridgekit-0.1.0.dist-info/top_level.txt,sha256=0lasTC02xVIOZh_nx7IXUjPQuVYr3nx-Vcw3rs9rxYI,9
|
|
34
|
+
dbbridgekit-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Otavio R Santana
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dbbridge
|