pythia-plsql 0.4.8__tar.gz → 0.4.10__tar.gz
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.
- {pythia_plsql-0.4.8/scripts/pythia_plsql.egg-info → pythia_plsql-0.4.10}/PKG-INFO +1 -1
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/pyproject.toml +1 -1
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/scripts/pythia.py +140 -13
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10/scripts/pythia_plsql.egg-info}/PKG-INFO +1 -1
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-setup/SKILL.md +23 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-write/SKILL.md +3 -1
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/tests/test_install.py +23 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/tests/test_phase1.py +26 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/LICENSE +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/README.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/compile-errors.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/dependencies.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/impact.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/invalid-objects.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/name-occupants.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/object-source.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/plscope-enabled.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/plscope-statements.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/plscope-usages.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/session-privileges.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/similar-candidates.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/queries/source.sql +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/scripts/pythia_plsql.egg-info/SOURCES.txt +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/scripts/pythia_plsql.egg-info/dependency_links.txt +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/scripts/pythia_plsql.egg-info/entry_points.txt +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/scripts/pythia_plsql.egg-info/requires.txt +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/scripts/pythia_plsql.egg-info/top_level.txt +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/setup.cfg +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-apply/SKILL.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-explore/SKILL.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-explore/reference/data-dictionary.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-impact/SKILL.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-review/SKILL.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-review/reference/antipatterns.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-skill-author/SKILL.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-write/reference/patterns.md +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/tests/test_phase2.py +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/tests/test_phase3.py +0 -0
- {pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/tests/test_phase5.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythia-plsql
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.10
|
|
4
4
|
Summary: PL/SQL development for AI agents on Oracle Database - expert data-dictionary queries, impact analysis, and a snapshot-verified write path with honest rollback.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/thaildhe172591/pythia
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pythia-plsql"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.10"
|
|
8
8
|
description = "PL/SQL development for AI agents on Oracle Database - expert data-dictionary queries, impact analysis, and a snapshot-verified write path with honest rollback."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -967,10 +967,46 @@ def json_envelope(command, connection, schema, cols, rows, truncated, **extra):
|
|
|
967
967
|
|
|
968
968
|
# --- database access ---------------------------------------------------------
|
|
969
969
|
|
|
970
|
-
def
|
|
971
|
-
"""
|
|
972
|
-
|
|
973
|
-
|
|
970
|
+
def authenticating_account(user):
|
|
971
|
+
"""The Oracle account that actually authenticates. With proxy
|
|
972
|
+
authentication the connect string is `agent[owner]` and it is the agent
|
|
973
|
+
whose password is checked, whose account locks, and whose name a DBA
|
|
974
|
+
needs — not the schema in front of you."""
|
|
975
|
+
if not user:
|
|
976
|
+
return None
|
|
977
|
+
return str(user).split("[", 1)[0].strip().upper()
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
def connect_failure_message(exc, conn_name, user=None):
|
|
981
|
+
"""A failure to connect should say which entry failed and what to do. For
|
|
982
|
+
the three errors Oracle has already diagnosed precisely, generic advice
|
|
983
|
+
wastes the reader's time, so name the actual next step instead."""
|
|
984
|
+
text = str(exc)
|
|
985
|
+
account = authenticating_account(user)
|
|
986
|
+
who = account or "<the connecting user>"
|
|
987
|
+
head = f"Could not connect using connection {conn_name!r}: {text}"
|
|
988
|
+
if "ORA-28000" in text:
|
|
989
|
+
return (f"{head}\n"
|
|
990
|
+
f"The account {who} is locked. A DBA or superuser unlocks it:\n"
|
|
991
|
+
f" ALTER USER {who} ACCOUNT UNLOCK;\n"
|
|
992
|
+
"Then find out why, or it locks again — a run of wrong "
|
|
993
|
+
"passwords trips\nFAILED_LOGIN_ATTEMPTS:\n"
|
|
994
|
+
f" SELECT account_status, lock_date, profile FROM dba_users "
|
|
995
|
+
f"WHERE username = '{who}';")
|
|
996
|
+
if "ORA-28001" in text or "ORA-28002" in text:
|
|
997
|
+
return (f"{head}\n"
|
|
998
|
+
f"The password for {who} has expired. A DBA sets a new one:\n"
|
|
999
|
+
f" ALTER USER {who} IDENTIFIED BY \"<new password>\";\n"
|
|
1000
|
+
"Update it in connections.json in the same breath. To stop the "
|
|
1001
|
+
"clock for a\nservice account, put it on a profile with "
|
|
1002
|
+
"PASSWORD_LIFE_TIME UNLIMITED.")
|
|
1003
|
+
if "ORA-01017" in text:
|
|
1004
|
+
return (f"{head}\n"
|
|
1005
|
+
f"Wrong username or password for {who}. Check the entry in "
|
|
1006
|
+
"connections.json —\nand check it before retrying: repeated "
|
|
1007
|
+
"attempts trip FAILED_LOGIN_ATTEMPTS and\nlock the account, "
|
|
1008
|
+
"which needs a DBA to undo.")
|
|
1009
|
+
return (f"{head}\n"
|
|
974
1010
|
"Check host/port/service_name, the credentials, and that the database "
|
|
975
1011
|
"is reachable from here. Use --conn NAME to try a different entry.")
|
|
976
1012
|
|
|
@@ -1296,6 +1332,78 @@ def cmd_plscope(conn, schema, ns):
|
|
|
1296
1332
|
emit_table(ns, stmt_cols, *clip(stmt_rows, ns.limit))
|
|
1297
1333
|
|
|
1298
1334
|
|
|
1335
|
+
CONVENTIONS_TEMPLATE = """{
|
|
1336
|
+
"naming": {
|
|
1337
|
+
"TABLE": "^T_[A-Z0-9_]+$",
|
|
1338
|
+
"PROCEDURE": "^P_[A-Z0-9_]+$",
|
|
1339
|
+
"FUNCTION": "^F_[A-Z0-9_]+$",
|
|
1340
|
+
"PACKAGE": "^PKG_[A-Z0-9_]+$",
|
|
1341
|
+
"SEQUENCE": "^S_[A-Z0-9_]+$",
|
|
1342
|
+
"TRIGGER": "^TRG_[A-Z0-9_]+$"
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
"""
|
|
1346
|
+
|
|
1347
|
+
CONVENTIONS_PROSE_TEMPLATE = """# Project conventions
|
|
1348
|
+
|
|
1349
|
+
Rules for anyone — human or agent — writing PL/SQL in this schema. Agents are
|
|
1350
|
+
told to read this before writing, and it outranks the generic patterns the
|
|
1351
|
+
skill pack ships with.
|
|
1352
|
+
|
|
1353
|
+
`conventions.json` next to this file holds the naming patterns as regexes;
|
|
1354
|
+
every `pythia apply` preview warns when a new object's name does not match.
|
|
1355
|
+
Keep the two in step: this file explains, that file enforces.
|
|
1356
|
+
|
|
1357
|
+
## Naming
|
|
1358
|
+
|
|
1359
|
+
Replace the patterns in `conventions.json` with yours, then describe them
|
|
1360
|
+
here so the reasoning survives. `pythia similar <A_TYPICAL_NAME>` shows what
|
|
1361
|
+
the schema already does — copy that rather than inventing a scheme.
|
|
1362
|
+
|
|
1363
|
+
| Kind | Rule | Example |
|
|
1364
|
+
|---|---|---|
|
|
1365
|
+
| Table | | |
|
|
1366
|
+
| Procedure | | |
|
|
1367
|
+
| Function | | |
|
|
1368
|
+
|
|
1369
|
+
## Rules that carry a cost when broken
|
|
1370
|
+
|
|
1371
|
+
State the consequence, not just the rule — a rule with a named cost gets
|
|
1372
|
+
followed. For example: which parameter prefixes the calling layer depends on,
|
|
1373
|
+
which column every query must filter by, where a transaction may commit.
|
|
1374
|
+
|
|
1375
|
+
| Rule | Cost of breaking it |
|
|
1376
|
+
|---|---|
|
|
1377
|
+
| | |
|
|
1378
|
+
|
|
1379
|
+
## Known exceptions
|
|
1380
|
+
|
|
1381
|
+
Objects that break the pattern on purpose, and why renaming them is worse
|
|
1382
|
+
than the warning they produce on every apply.
|
|
1383
|
+
"""
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
def scaffold_conventions(root):
|
|
1387
|
+
"""Write the conventions pair, skipping anything that already exists.
|
|
1388
|
+
Returns the paths actually created.
|
|
1389
|
+
|
|
1390
|
+
The docs used to say "copy examples/conventions.example.json", which is
|
|
1391
|
+
no help to anyone who installed the wheel: there is no examples directory
|
|
1392
|
+
there. The tool carries the templates instead.
|
|
1393
|
+
"""
|
|
1394
|
+
d = pathlib.Path(root) / CONFIG_DIR
|
|
1395
|
+
made = []
|
|
1396
|
+
for name, body in (("conventions.json", CONVENTIONS_TEMPLATE),
|
|
1397
|
+
("conventions.md", CONVENTIONS_PROSE_TEMPLATE)):
|
|
1398
|
+
path = d / name
|
|
1399
|
+
if path.is_file():
|
|
1400
|
+
continue
|
|
1401
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1402
|
+
path.write_text(body, encoding="utf-8")
|
|
1403
|
+
made.append(path)
|
|
1404
|
+
return made
|
|
1405
|
+
|
|
1406
|
+
|
|
1299
1407
|
def load_conventions(root):
|
|
1300
1408
|
"""Project house style from .pythia/conventions.json — the machine half of
|
|
1301
1409
|
the customization surface (the prose half is conventions.md, for agents).
|
|
@@ -1754,23 +1862,39 @@ def cmd_journal(conn, schema, ns):
|
|
|
1754
1862
|
|
|
1755
1863
|
|
|
1756
1864
|
def cmd_conventions(conn, schema, ns):
|
|
1757
|
-
|
|
1865
|
+
root = ns.project_root
|
|
1866
|
+
if getattr(ns, "init", False):
|
|
1867
|
+
made = scaffold_conventions(root)
|
|
1868
|
+
for path in made:
|
|
1869
|
+
print(f"Created {path}")
|
|
1870
|
+
if not made:
|
|
1871
|
+
print(f"Both files already exist in "
|
|
1872
|
+
f"{pathlib.Path(root) / CONFIG_DIR} — left untouched.")
|
|
1873
|
+
else:
|
|
1874
|
+
print(NL + "Edit the patterns to match this schema — "
|
|
1875
|
+
f"`{invocation()} similar <A_TYPICAL_NAME>` shows what "
|
|
1876
|
+
"it already does." + NL + "Every apply preview then "
|
|
1877
|
+
"warns when a name drifts from them.")
|
|
1878
|
+
return
|
|
1879
|
+
conv = load_conventions(root)
|
|
1758
1880
|
if ns.json:
|
|
1759
1881
|
print(json.dumps(conv or {}))
|
|
1760
1882
|
return
|
|
1761
1883
|
if not conv:
|
|
1762
1884
|
print("No project conventions configured.")
|
|
1763
|
-
print(f"Create
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
"Put the prose rules in conventions.md next to it for your agents.")
|
|
1885
|
+
print(f"Create the starter pair with: {invocation()} conventions --init")
|
|
1886
|
+
print(" conventions.json naming patterns; apply previews warn on drift")
|
|
1887
|
+
print(" conventions.md the prose rules your agents read first")
|
|
1767
1888
|
return
|
|
1768
1889
|
print("Naming patterns — apply previews warn when a name drifts:")
|
|
1769
1890
|
for otype, pattern in conv.get("naming", {}).items():
|
|
1770
1891
|
print(f" {otype:<13} {pattern}")
|
|
1771
|
-
md = pathlib.Path(
|
|
1892
|
+
md = pathlib.Path(root) / CONFIG_DIR / "conventions.md"
|
|
1772
1893
|
if md.is_file():
|
|
1773
|
-
print(f"
|
|
1894
|
+
print(NL + f"Prose rules for agents: {md}")
|
|
1895
|
+
else:
|
|
1896
|
+
print(NL + f"No conventions.md yet — {invocation()} conventions "
|
|
1897
|
+
"--init writes one.")
|
|
1774
1898
|
|
|
1775
1899
|
|
|
1776
1900
|
AGENT_USER_SQL = """\
|
|
@@ -2218,8 +2342,11 @@ def build_parser():
|
|
|
2218
2342
|
help="apply without stopping; the full preview still prints and journals")
|
|
2219
2343
|
s.add_argument("--depth", type=int, default=3,
|
|
2220
2344
|
help="impact depth for the preview (default 3)")
|
|
2221
|
-
sub.add_parser("conventions", parents=[common()],
|
|
2345
|
+
s = sub.add_parser("conventions", parents=[common()],
|
|
2222
2346
|
help="show the project's house-style naming patterns")
|
|
2347
|
+
s.add_argument("--init", action="store_true",
|
|
2348
|
+
help="write starter conventions.json and conventions.md "
|
|
2349
|
+
"into .pythia/ (never overwrites)")
|
|
2223
2350
|
s = sub.add_parser("agent-user", parents=[common()],
|
|
2224
2351
|
help="SQL for a least-privilege proxy agent user; "
|
|
2225
2352
|
"--save adds it to connections.json")
|
|
@@ -2298,7 +2425,7 @@ def main(argv=None):
|
|
|
2298
2425
|
except (oracledb.Error, OSError) as e:
|
|
2299
2426
|
# OSError too: a DNS or socket failure arrives raw from the socket layer,
|
|
2300
2427
|
# not wrapped as an oracledb.Error.
|
|
2301
|
-
sys.exit(connect_failure_message(e, name))
|
|
2428
|
+
sys.exit(connect_failure_message(e, name, c.get("user")))
|
|
2302
2429
|
try:
|
|
2303
2430
|
if ns.command == "journal": # only restore reaches here
|
|
2304
2431
|
if not ns.id:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythia-plsql
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.10
|
|
4
4
|
Summary: PL/SQL development for AI agents on Oracle Database - expert data-dictionary queries, impact analysis, and a snapshot-verified write path with honest rollback.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/thaildhe172591/pythia
|
|
@@ -75,6 +75,29 @@ server: command `sql -mcp`. Example client config:
|
|
|
75
75
|
`V$SESSION.ACTION` the LLM's name; generated SQL carries an
|
|
76
76
|
`/* LLM in use */` comment.
|
|
77
77
|
|
|
78
|
+
## 4. What lives in `.pythia/`, and what creates it
|
|
79
|
+
|
|
80
|
+
Only `connections.json` is written by `install`. The rest appear when you ask
|
|
81
|
+
for them, so an empty `.pythia/` is a working one — nothing here is missing
|
|
82
|
+
until you want it.
|
|
83
|
+
|
|
84
|
+
| File | Created by | What it does |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `connections.json` | `pythia install` | Where to connect. Holds credentials; gitignored. |
|
|
87
|
+
| `journal/` | first write or snapshot | Every captured version, with a runnable rollback per entry. |
|
|
88
|
+
| `conventions.json` | `pythia conventions --init` | Naming patterns per object type. Every apply preview warns when a new name drifts. |
|
|
89
|
+
| `conventions.md` | `pythia conventions --init` | The house rules in prose. **`pythia-write` reads this before writing anything**, and it outranks the generic patterns this pack ships with. |
|
|
90
|
+
| `policy.json` | `pythia policy set <group> <value>` | Pins the write policy. Absent means the built-in defaults, which are already the safe ones. |
|
|
91
|
+
| `settings.json` | you, by hand | Optional switches, e.g. `{"auto_snapshot": false}`. |
|
|
92
|
+
|
|
93
|
+
**Capturing a team's house style is the highest-value optional step.** Run
|
|
94
|
+
`pythia conventions --init`, then replace the placeholder patterns with the
|
|
95
|
+
real ones — `pythia similar <A_TYPICAL_NAME>` shows what the schema already
|
|
96
|
+
does, which beats inventing a scheme. Fill in `conventions.md` with the rules
|
|
97
|
+
that carry a cost when broken, and say what the cost is; a rule with a named
|
|
98
|
+
consequence gets followed. Commit both files to the project repo so the whole
|
|
99
|
+
team and every agent session works from the same rules.
|
|
100
|
+
|
|
78
101
|
## Done when
|
|
79
102
|
|
|
80
103
|
- `pythia check` connects, shows the right schema, and prints **no**
|
|
@@ -9,7 +9,9 @@ description: Use when writing or modifying PL/SQL source - a procedure, function
|
|
|
9
9
|
|
|
10
10
|
**Before anything:** if the project has `.pythia/conventions.md`, read it —
|
|
11
11
|
house rules there outrank every generic pattern below, and
|
|
12
|
-
`pythia conventions` shows the naming patterns the apply preview will check.
|
|
12
|
+
`pythia conventions` shows the naming patterns the apply preview will check. If the project has none and the developer
|
|
13
|
+
describes a house style, offer `pythia conventions --init` — captured once,
|
|
14
|
+
it applies to every future session instead of being re-explained.
|
|
13
15
|
|
|
14
16
|
A codebase with thousands of procedures has already decided how procedures
|
|
15
17
|
look. Your job is to write one that a maintainer cannot tell from the
|
|
@@ -7,6 +7,7 @@ Run: python tests/test_install.py
|
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
9
|
import pathlib
|
|
10
|
+
import re
|
|
10
11
|
import sys
|
|
11
12
|
import tempfile
|
|
12
13
|
|
|
@@ -325,6 +326,28 @@ def test_long_path_is_flagged_before_it_silently_truncates():
|
|
|
325
326
|
assert "duplicate" in w.lower() # names the usual cause
|
|
326
327
|
|
|
327
328
|
|
|
329
|
+
def test_conventions_init_writes_both_halves_and_never_clobbers():
|
|
330
|
+
"""Telling a pip user to copy examples/conventions.example.json is no help:
|
|
331
|
+
the wheel does not ship an examples directory. The tool writes the pair."""
|
|
332
|
+
import json
|
|
333
|
+
with tempfile.TemporaryDirectory() as td:
|
|
334
|
+
made = pythia.scaffold_conventions(td)
|
|
335
|
+
d = pathlib.Path(td) / ".pythia"
|
|
336
|
+
assert sorted(p.name for p in made) == ["conventions.json",
|
|
337
|
+
"conventions.md"]
|
|
338
|
+
rules = json.loads((d / "conventions.json").read_text(encoding="utf-8"))
|
|
339
|
+
assert "naming" in rules and rules["naming"] # usable as written
|
|
340
|
+
for otype, pattern in rules["naming"].items():
|
|
341
|
+
re.compile(pattern) # every one valid
|
|
342
|
+
assert pythia.load_conventions(td) is not None # accepted by the loader
|
|
343
|
+
prose = (d / "conventions.md").read_text(encoding="utf-8")
|
|
344
|
+
assert "conventions.json" in prose # the halves reference
|
|
345
|
+
# a second run leaves existing files alone
|
|
346
|
+
(d / "conventions.json").write_text('{"naming": {}}', encoding="utf-8")
|
|
347
|
+
assert pythia.scaffold_conventions(td) == []
|
|
348
|
+
assert (d / "conventions.json").read_text(encoding="utf-8") == '{"naming": {}}'
|
|
349
|
+
|
|
350
|
+
|
|
328
351
|
def main():
|
|
329
352
|
failed = 0
|
|
330
353
|
for name, fn in sorted(globals().items()):
|
|
@@ -301,6 +301,32 @@ def test_json_envelope():
|
|
|
301
301
|
assert d["rows"] == [{"A": 1, "B": "2026-01-02"}]
|
|
302
302
|
|
|
303
303
|
|
|
304
|
+
def test_connect_failure_names_the_fix_for_common_oracle_errors():
|
|
305
|
+
"""Generic advice is useless for an error Oracle already diagnosed. The
|
|
306
|
+
three that actually strand people each have one specific next step."""
|
|
307
|
+
locked = pythia.connect_failure_message(
|
|
308
|
+
Exception("ORA-28000: The account is locked."), "dev", user="agent[owner]")
|
|
309
|
+
assert "ACCOUNT UNLOCK" in locked and "AGENT" in locked # the real account
|
|
310
|
+
assert "DBA" in locked or "superuser" in locked.lower()
|
|
311
|
+
|
|
312
|
+
expired = pythia.connect_failure_message(
|
|
313
|
+
Exception("ORA-28001: the password has expired"), "dev", user="agent")
|
|
314
|
+
assert "PASSWORD EXPIRE" in expired or "new password" in expired.lower()
|
|
315
|
+
|
|
316
|
+
wrong = pythia.connect_failure_message(
|
|
317
|
+
Exception("ORA-01017: invalid username/password"), "dev", user="agent")
|
|
318
|
+
assert "lock the account" in wrong.lower() # warn before they retry into one
|
|
319
|
+
|
|
320
|
+
plain = pythia.connect_failure_message(OSError("getaddrinfo failed"), "dev")
|
|
321
|
+
assert "ORA-" not in plain and "dev" in plain # unchanged for the rest
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def test_oracle_account_taken_from_a_proxy_connect_string():
|
|
325
|
+
assert pythia.authenticating_account("agent[owner]") == "AGENT"
|
|
326
|
+
assert pythia.authenticating_account("plain_user") == "PLAIN_USER"
|
|
327
|
+
assert pythia.authenticating_account(None) is None
|
|
328
|
+
|
|
329
|
+
|
|
304
330
|
def main():
|
|
305
331
|
failed = 0
|
|
306
332
|
for name, fn in sorted(globals().items()):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/scripts/pythia_plsql.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythia_plsql-0.4.8 → pythia_plsql-0.4.10}/skills/pythia-explore/reference/data-dictionary.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|