plsqlwks 0.1.3__tar.gz → 0.1.5__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.
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/PKG-INFO +87 -14
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/README.md +86 -13
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/__init__.py +1 -1
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app_db.py +48 -7
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app_input.py +26 -1
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks.egg-info/PKG-INFO +87 -14
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_oracle_integration.py +65 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_ui_keys.py +309 -1
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/license.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/__main__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/config/__init__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/config/loader.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/config/models.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/config/paths.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/config/session.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/config/settings.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/__init__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/editing.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/execution.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/explain.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/health.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/metadata.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/models.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/session.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/sql_analysis.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/db/transactions.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/sqlbinds.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/sqlsplit.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/__init__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app_editor.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app_files.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app_render.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app_results.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/app_tabs_browser.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/browser.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/buffer.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/clipboard.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/commands.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/completion.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/constants.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/db_worker.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/display.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/errors.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/help.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/keys.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/menu.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/results.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/sql.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/state.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/ui/syntax.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks/workspace.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks.egg-info/SOURCES.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks.egg-info/dependency_links.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks.egg-info/entry_points.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks.egg-info/requires.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/plsqlwks.egg-info/top_level.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/pyproject.toml +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/setup.cfg +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_config_workspace.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_db_editing.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_db_helpers.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_db_worker.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_packaging.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_pty_keys.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_rchar.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_sqlbinds.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_sqlsplit.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.5}/tests/test_ui_flows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plsqlwks
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: An ncurses SQL and PL/SQL workspace for Oracle databases.
|
|
5
5
|
License-Expression: LicenseRef-plsqlwks-Donationware
|
|
6
6
|
Project-URL: Repository, https://gitlab.com/unununu/plsqlwks
|
|
@@ -23,7 +23,7 @@ Dynamic: license-file
|
|
|
23
23
|
|
|
24
24
|
# plsqlwks
|
|
25
25
|
|
|
26
|
-
An ncurses SQL and PL/SQL workspace for
|
|
26
|
+
An ncurses SQL and PL/SQL workspace for Oracle databases.
|
|
27
27
|
|
|
28
28
|

|
|
29
29
|
|
|
@@ -48,15 +48,24 @@ Override the defaults with environment variables:
|
|
|
48
48
|
export ORACLE_USER=hr
|
|
49
49
|
export ORACLE_PASSWORD_FILE=~/.config/plsqlwks/orapass
|
|
50
50
|
export ORACLE_DSN='127.0.0.1:1521/free'
|
|
51
|
-
export PLSQLWKS_WORKSPACE=/
|
|
51
|
+
export PLSQLWKS_WORKSPACE=/path/to/workspace
|
|
52
|
+
export PLSQLWKS_MAX_ROWS=200
|
|
53
|
+
export PLSQLWKS_ARRAYSIZE=100
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
Password files preserve leading and trailing spaces; only final CR/LF line
|
|
55
57
|
endings are removed. On POSIX systems, create the file for the current user and
|
|
56
58
|
protect it with `chmod 600`.
|
|
57
59
|
|
|
60
|
+
`PLSQLWKS_MAX_ROWS` sets the maximum number of rows in each result page, while
|
|
61
|
+
`PLSQLWKS_ARRAYSIZE` sets the Oracle cursor array size. Both values must be
|
|
62
|
+
positive integers.
|
|
63
|
+
|
|
58
64
|
## Install
|
|
59
65
|
|
|
66
|
+
Python 3.10 or newer is required, along with a compatible terminal and a Python
|
|
67
|
+
build that provides the standard-library `curses` module.
|
|
68
|
+
|
|
60
69
|
For development, install the package in editable mode:
|
|
61
70
|
|
|
62
71
|
```bash
|
|
@@ -65,15 +74,27 @@ python3 -m pip install -e '.[dev]'
|
|
|
65
74
|
|
|
66
75
|
`oracledb` is the only runtime dependency. Supporting functionality uses the Python standard library.
|
|
67
76
|
|
|
68
|
-
See the [release checklist](https://gitlab.com/unununu/plsqlwks/-/blob/main/RELEASING.md)
|
|
69
|
-
for the quality, packaging, and GitLab release process.
|
|
70
|
-
|
|
71
77
|
For a regular local install, use:
|
|
72
78
|
|
|
73
79
|
```bash
|
|
74
80
|
python3 -m pip install .
|
|
75
81
|
```
|
|
76
82
|
|
|
83
|
+
Run the default test and lint checks from a development checkout with:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
python3 -m pytest -q
|
|
87
|
+
python3 -m ruff check .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Oracle integration tests require the connection environment variables shown
|
|
91
|
+
above and an accessible password file. Enable every test group with:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
PLSQLWKS_TEST_ORACLE=1 PLSQLWKS_TEST_PTY=1 PLSQLWKS_TEST_SLOW=1 \
|
|
95
|
+
python3 -m pytest -q -rs
|
|
96
|
+
```
|
|
97
|
+
|
|
77
98
|
## Run
|
|
78
99
|
|
|
79
100
|
After installation, start the workspace with:
|
|
@@ -92,6 +113,15 @@ The CLI option takes precedence over `PLSQLWKS_WORKSPACE`. For compatibility, a
|
|
|
92
113
|
source checkout that already contains a configured `workspace/` continues to
|
|
93
114
|
use it and displays a migration notice; files are never moved automatically.
|
|
94
115
|
|
|
116
|
+
Choose the initial transaction mode for one invocation with `--manual` or
|
|
117
|
+
`--autocommit`. These options override `[database] autocommit` from the active
|
|
118
|
+
`config.ini`:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
plsqlwks --manual
|
|
122
|
+
plsqlwks --autocommit
|
|
123
|
+
```
|
|
124
|
+
|
|
95
125
|
You can also run it directly as a module from the source tree:
|
|
96
126
|
|
|
97
127
|
```bash
|
|
@@ -104,7 +134,25 @@ The app opens a split-screen terminal workspace:
|
|
|
104
134
|
- results/messages below
|
|
105
135
|
- command/status bar at the bottom
|
|
106
136
|
|
|
107
|
-
The terminal UI uses the active
|
|
137
|
+
The terminal UI uses the active locale for keyboard input and display. If that
|
|
138
|
+
locale cannot be initialized, the app tries `C.UTF-8`, `en_US.UTF-8`, and
|
|
139
|
+
`UTF-8`. A valid plain `C` locale remains active, so configure a UTF-8 locale in
|
|
140
|
+
the shell when non-ASCII input or display is required.
|
|
141
|
+
|
|
142
|
+
Oracle operations run serially on one persistent background worker. The UI
|
|
143
|
+
continues to redraw and accept input while an operation runs, but it rejects a
|
|
144
|
+
second database operation until the active one finishes. `Ctrl-C` requests
|
|
145
|
+
interruption of the active operation. Reconnecting closes open result pages and
|
|
146
|
+
clears query results loaded by the previous connection. If a manual transaction
|
|
147
|
+
has pending changes, reconnect first asks whether to commit, roll back, discard
|
|
148
|
+
the session, or cancel. Commit or rollback must succeed before reconnect closes
|
|
149
|
+
the old connection. If either action fails, plsqlwks does not close the current
|
|
150
|
+
connection or clear its loaded results; reconnect can be tried again with an
|
|
151
|
+
explicit discard. Discard closes the session without resolving the transaction
|
|
152
|
+
explicitly, so Oracle rolls its uncommitted work back.
|
|
153
|
+
If the old connection is already dead and reports an error while closing,
|
|
154
|
+
plsqlwks still attempts the replacement connection and reports the close error
|
|
155
|
+
as a warning after a successful reconnect.
|
|
108
156
|
|
|
109
157
|
After a successful quit, plsqlwks records the open file-backed tabs, active tab, and cursor positions in the managed `[session.tabs]` section of `config.ini`. Fresh platform-default workspaces use the user-config directory; explicitly selected and legacy workspaces keep `config.ini` inside the workspace for compatibility. On the next start the app tries each saved path independently, silently skips files that are missing or unreadable, and leaves the initial empty tab in place when none can be opened. A saved cursor position that no longer exists in its file starts at the beginning instead. Untitled, template, and generated schema tabs are not persisted because they do not have a file to reopen.
|
|
110
158
|
|
|
@@ -127,7 +175,7 @@ After a successful quit, plsqlwks records the open file-backed tabs, active tab,
|
|
|
127
175
|
| `Alt-1`..`Alt-9` | Jump to visible file tab |
|
|
128
176
|
| `Ctrl-Q` | Quit |
|
|
129
177
|
| `Ctrl-C` while running | Interrupt the active database operation |
|
|
130
|
-
| `Ctrl-Alt-C` |
|
|
178
|
+
| `Ctrl-Alt-C` | Insert the active result-grid draft, or commit when no draft is active |
|
|
131
179
|
| `Ctrl-Alt-R` | Roll back the current transaction |
|
|
132
180
|
|
|
133
181
|
### Editor
|
|
@@ -182,7 +230,12 @@ Set `[database] remember_bind_values = yes` in the active `config.ini` to prefil
|
|
|
182
230
|
|
|
183
231
|
Editor search is literal and case-insensitive. `Ctrl-F` prompts for text and selects the found occurrence; `Ctrl-N` and `Ctrl-P` repeat the last search forward or backward with wraparound.
|
|
184
232
|
|
|
185
|
-
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
233
|
+
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
234
|
+
current-schema object names, and table/view columns from cached or lazily loaded
|
|
235
|
+
database metadata; multiple matches open a picker. Database-object and column
|
|
236
|
+
completion currently supports conventional unquoted identifiers; quoted
|
|
237
|
+
mixed-case names are not preserved. Use `Alt-+` to reload schema-object metadata
|
|
238
|
+
and clear cached column metadata.
|
|
186
239
|
|
|
187
240
|
Editor syntax and explain-plan colors can be overridden in the active `config.ini` with color names or numeric curses color indexes. Unsupported values fall back to the built-in palette for the current terminal:
|
|
188
241
|
|
|
@@ -225,16 +278,35 @@ Read-only mode is a client-side guardrail that rejects statements which the SQL
|
|
|
225
278
|
| `PageUp` / `PageDown` | Scroll explain-plan lines by page |
|
|
226
279
|
| `Home` / `End` | Move to the first/last explain-plan line |
|
|
227
280
|
|
|
228
|
-
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
281
|
+
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
282
|
+
appends the next result page. For simple queries against one conventional
|
|
283
|
+
unquoted current-schema table that include `ROWID`, press `Enter` on a directly
|
|
284
|
+
selected unquoted table column to update it by rowid. Press `INS` in the grid to
|
|
285
|
+
prepare a draft row at the top of the grid, edit its cells with `Enter`, use
|
|
286
|
+
`Ctrl-Alt-C` to insert it, or use `Esc` to cancel it.
|
|
229
287
|
Database null values are displayed and entered as `<NULL>` in editable results. Plain `NULL` is stored as literal text.
|
|
230
|
-
Grid edits compare the
|
|
231
|
-
|
|
288
|
+
Grid edits compare the selected cell's originally loaded typed value as well as
|
|
289
|
+
the `ROWID`. If another session changes that cell or deletes the row first, the
|
|
290
|
+
edit is rejected and the query must be refreshed. Changes to other cells in the
|
|
291
|
+
same row do not cause a conflict. NUMBER input uses decimal notation with a
|
|
292
|
+
period; DATE and TIMESTAMP input uses ISO
|
|
293
|
+
`YYYY-MM-DD[ HH:MM:SS[.ffffff]]`; RAW and BLOB input uses hexadecimal bytes.
|
|
294
|
+
Character and CLOB input is preserved as entered. Types that cannot be compared
|
|
295
|
+
without losing information, including time-zone timestamps and timestamps with
|
|
296
|
+
precision above six, are read-only in the grid.
|
|
297
|
+
Displayed CLOB and BLOB values are limited to the first 65,536 characters or
|
|
298
|
+
bytes and include a truncation marker with the full size. Truncated LOB cells
|
|
299
|
+
cannot be edited safely. Schema-browser DDL is always read in full.
|
|
232
300
|
`F7` cycles a grid fullscreen view that starts with the table header on the first terminal line and uses the last line for data, an editor-only fullscreen view, and a split layout with 2/3 editor and 1/3 data grid.
|
|
233
301
|
|
|
234
302
|
### Schema Browser
|
|
235
303
|
|
|
236
304
|
The `F9` schema browser groups tables, views, procedures, functions, packages, triggers, sequences, indexes, and private synonyms from the current schema. Type while the browser is focused to filter object names with a case-insensitive substring match; groups without matches are hidden and matching groups expand automatically without changing their saved expansion state.
|
|
237
305
|
|
|
306
|
+
Definition loading supports conventional unquoted identifiers. Quoted
|
|
307
|
+
mixed-case objects can appear in the browser but their definitions cannot
|
|
308
|
+
currently be loaded from it.
|
|
309
|
+
|
|
238
310
|
| Key | Action |
|
|
239
311
|
| --- | --- |
|
|
240
312
|
| Printable text | Filter object names |
|
|
@@ -272,7 +344,8 @@ The `F9` schema browser groups tables, views, procedures, functions, packages, t
|
|
|
272
344
|
workspace/
|
|
273
345
|
sql/ saved SQL files
|
|
274
346
|
plsql/ saved PL/SQL files
|
|
275
|
-
results/
|
|
347
|
+
results/ reserved output directory
|
|
276
348
|
```
|
|
277
349
|
|
|
278
|
-
The first launch creates the workspace folders and starter SQL/PLSQL files.
|
|
350
|
+
The first launch creates the workspace folders and starter SQL/PLSQL files. The
|
|
351
|
+
terminal UI does not currently provide a query-export command.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# plsqlwks
|
|
2
2
|
|
|
3
|
-
An ncurses SQL and PL/SQL workspace for
|
|
3
|
+
An ncurses SQL and PL/SQL workspace for Oracle databases.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
@@ -25,15 +25,24 @@ Override the defaults with environment variables:
|
|
|
25
25
|
export ORACLE_USER=hr
|
|
26
26
|
export ORACLE_PASSWORD_FILE=~/.config/plsqlwks/orapass
|
|
27
27
|
export ORACLE_DSN='127.0.0.1:1521/free'
|
|
28
|
-
export PLSQLWKS_WORKSPACE=/
|
|
28
|
+
export PLSQLWKS_WORKSPACE=/path/to/workspace
|
|
29
|
+
export PLSQLWKS_MAX_ROWS=200
|
|
30
|
+
export PLSQLWKS_ARRAYSIZE=100
|
|
29
31
|
```
|
|
30
32
|
|
|
31
33
|
Password files preserve leading and trailing spaces; only final CR/LF line
|
|
32
34
|
endings are removed. On POSIX systems, create the file for the current user and
|
|
33
35
|
protect it with `chmod 600`.
|
|
34
36
|
|
|
37
|
+
`PLSQLWKS_MAX_ROWS` sets the maximum number of rows in each result page, while
|
|
38
|
+
`PLSQLWKS_ARRAYSIZE` sets the Oracle cursor array size. Both values must be
|
|
39
|
+
positive integers.
|
|
40
|
+
|
|
35
41
|
## Install
|
|
36
42
|
|
|
43
|
+
Python 3.10 or newer is required, along with a compatible terminal and a Python
|
|
44
|
+
build that provides the standard-library `curses` module.
|
|
45
|
+
|
|
37
46
|
For development, install the package in editable mode:
|
|
38
47
|
|
|
39
48
|
```bash
|
|
@@ -42,15 +51,27 @@ python3 -m pip install -e '.[dev]'
|
|
|
42
51
|
|
|
43
52
|
`oracledb` is the only runtime dependency. Supporting functionality uses the Python standard library.
|
|
44
53
|
|
|
45
|
-
See the [release checklist](https://gitlab.com/unununu/plsqlwks/-/blob/main/RELEASING.md)
|
|
46
|
-
for the quality, packaging, and GitLab release process.
|
|
47
|
-
|
|
48
54
|
For a regular local install, use:
|
|
49
55
|
|
|
50
56
|
```bash
|
|
51
57
|
python3 -m pip install .
|
|
52
58
|
```
|
|
53
59
|
|
|
60
|
+
Run the default test and lint checks from a development checkout with:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python3 -m pytest -q
|
|
64
|
+
python3 -m ruff check .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Oracle integration tests require the connection environment variables shown
|
|
68
|
+
above and an accessible password file. Enable every test group with:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
PLSQLWKS_TEST_ORACLE=1 PLSQLWKS_TEST_PTY=1 PLSQLWKS_TEST_SLOW=1 \
|
|
72
|
+
python3 -m pytest -q -rs
|
|
73
|
+
```
|
|
74
|
+
|
|
54
75
|
## Run
|
|
55
76
|
|
|
56
77
|
After installation, start the workspace with:
|
|
@@ -69,6 +90,15 @@ The CLI option takes precedence over `PLSQLWKS_WORKSPACE`. For compatibility, a
|
|
|
69
90
|
source checkout that already contains a configured `workspace/` continues to
|
|
70
91
|
use it and displays a migration notice; files are never moved automatically.
|
|
71
92
|
|
|
93
|
+
Choose the initial transaction mode for one invocation with `--manual` or
|
|
94
|
+
`--autocommit`. These options override `[database] autocommit` from the active
|
|
95
|
+
`config.ini`:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
plsqlwks --manual
|
|
99
|
+
plsqlwks --autocommit
|
|
100
|
+
```
|
|
101
|
+
|
|
72
102
|
You can also run it directly as a module from the source tree:
|
|
73
103
|
|
|
74
104
|
```bash
|
|
@@ -81,7 +111,25 @@ The app opens a split-screen terminal workspace:
|
|
|
81
111
|
- results/messages below
|
|
82
112
|
- command/status bar at the bottom
|
|
83
113
|
|
|
84
|
-
The terminal UI uses the active
|
|
114
|
+
The terminal UI uses the active locale for keyboard input and display. If that
|
|
115
|
+
locale cannot be initialized, the app tries `C.UTF-8`, `en_US.UTF-8`, and
|
|
116
|
+
`UTF-8`. A valid plain `C` locale remains active, so configure a UTF-8 locale in
|
|
117
|
+
the shell when non-ASCII input or display is required.
|
|
118
|
+
|
|
119
|
+
Oracle operations run serially on one persistent background worker. The UI
|
|
120
|
+
continues to redraw and accept input while an operation runs, but it rejects a
|
|
121
|
+
second database operation until the active one finishes. `Ctrl-C` requests
|
|
122
|
+
interruption of the active operation. Reconnecting closes open result pages and
|
|
123
|
+
clears query results loaded by the previous connection. If a manual transaction
|
|
124
|
+
has pending changes, reconnect first asks whether to commit, roll back, discard
|
|
125
|
+
the session, or cancel. Commit or rollback must succeed before reconnect closes
|
|
126
|
+
the old connection. If either action fails, plsqlwks does not close the current
|
|
127
|
+
connection or clear its loaded results; reconnect can be tried again with an
|
|
128
|
+
explicit discard. Discard closes the session without resolving the transaction
|
|
129
|
+
explicitly, so Oracle rolls its uncommitted work back.
|
|
130
|
+
If the old connection is already dead and reports an error while closing,
|
|
131
|
+
plsqlwks still attempts the replacement connection and reports the close error
|
|
132
|
+
as a warning after a successful reconnect.
|
|
85
133
|
|
|
86
134
|
After a successful quit, plsqlwks records the open file-backed tabs, active tab, and cursor positions in the managed `[session.tabs]` section of `config.ini`. Fresh platform-default workspaces use the user-config directory; explicitly selected and legacy workspaces keep `config.ini` inside the workspace for compatibility. On the next start the app tries each saved path independently, silently skips files that are missing or unreadable, and leaves the initial empty tab in place when none can be opened. A saved cursor position that no longer exists in its file starts at the beginning instead. Untitled, template, and generated schema tabs are not persisted because they do not have a file to reopen.
|
|
87
135
|
|
|
@@ -104,7 +152,7 @@ After a successful quit, plsqlwks records the open file-backed tabs, active tab,
|
|
|
104
152
|
| `Alt-1`..`Alt-9` | Jump to visible file tab |
|
|
105
153
|
| `Ctrl-Q` | Quit |
|
|
106
154
|
| `Ctrl-C` while running | Interrupt the active database operation |
|
|
107
|
-
| `Ctrl-Alt-C` |
|
|
155
|
+
| `Ctrl-Alt-C` | Insert the active result-grid draft, or commit when no draft is active |
|
|
108
156
|
| `Ctrl-Alt-R` | Roll back the current transaction |
|
|
109
157
|
|
|
110
158
|
### Editor
|
|
@@ -159,7 +207,12 @@ Set `[database] remember_bind_values = yes` in the active `config.ini` to prefil
|
|
|
159
207
|
|
|
160
208
|
Editor search is literal and case-insensitive. `Ctrl-F` prompts for text and selects the found occurrence; `Ctrl-N` and `Ctrl-P` repeat the last search forward or backward with wraparound.
|
|
161
209
|
|
|
162
|
-
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
210
|
+
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
211
|
+
current-schema object names, and table/view columns from cached or lazily loaded
|
|
212
|
+
database metadata; multiple matches open a picker. Database-object and column
|
|
213
|
+
completion currently supports conventional unquoted identifiers; quoted
|
|
214
|
+
mixed-case names are not preserved. Use `Alt-+` to reload schema-object metadata
|
|
215
|
+
and clear cached column metadata.
|
|
163
216
|
|
|
164
217
|
Editor syntax and explain-plan colors can be overridden in the active `config.ini` with color names or numeric curses color indexes. Unsupported values fall back to the built-in palette for the current terminal:
|
|
165
218
|
|
|
@@ -202,16 +255,35 @@ Read-only mode is a client-side guardrail that rejects statements which the SQL
|
|
|
202
255
|
| `PageUp` / `PageDown` | Scroll explain-plan lines by page |
|
|
203
256
|
| `Home` / `End` | Move to the first/last explain-plan line |
|
|
204
257
|
|
|
205
|
-
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
258
|
+
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
259
|
+
appends the next result page. For simple queries against one conventional
|
|
260
|
+
unquoted current-schema table that include `ROWID`, press `Enter` on a directly
|
|
261
|
+
selected unquoted table column to update it by rowid. Press `INS` in the grid to
|
|
262
|
+
prepare a draft row at the top of the grid, edit its cells with `Enter`, use
|
|
263
|
+
`Ctrl-Alt-C` to insert it, or use `Esc` to cancel it.
|
|
206
264
|
Database null values are displayed and entered as `<NULL>` in editable results. Plain `NULL` is stored as literal text.
|
|
207
|
-
Grid edits compare the
|
|
208
|
-
|
|
265
|
+
Grid edits compare the selected cell's originally loaded typed value as well as
|
|
266
|
+
the `ROWID`. If another session changes that cell or deletes the row first, the
|
|
267
|
+
edit is rejected and the query must be refreshed. Changes to other cells in the
|
|
268
|
+
same row do not cause a conflict. NUMBER input uses decimal notation with a
|
|
269
|
+
period; DATE and TIMESTAMP input uses ISO
|
|
270
|
+
`YYYY-MM-DD[ HH:MM:SS[.ffffff]]`; RAW and BLOB input uses hexadecimal bytes.
|
|
271
|
+
Character and CLOB input is preserved as entered. Types that cannot be compared
|
|
272
|
+
without losing information, including time-zone timestamps and timestamps with
|
|
273
|
+
precision above six, are read-only in the grid.
|
|
274
|
+
Displayed CLOB and BLOB values are limited to the first 65,536 characters or
|
|
275
|
+
bytes and include a truncation marker with the full size. Truncated LOB cells
|
|
276
|
+
cannot be edited safely. Schema-browser DDL is always read in full.
|
|
209
277
|
`F7` cycles a grid fullscreen view that starts with the table header on the first terminal line and uses the last line for data, an editor-only fullscreen view, and a split layout with 2/3 editor and 1/3 data grid.
|
|
210
278
|
|
|
211
279
|
### Schema Browser
|
|
212
280
|
|
|
213
281
|
The `F9` schema browser groups tables, views, procedures, functions, packages, triggers, sequences, indexes, and private synonyms from the current schema. Type while the browser is focused to filter object names with a case-insensitive substring match; groups without matches are hidden and matching groups expand automatically without changing their saved expansion state.
|
|
214
282
|
|
|
283
|
+
Definition loading supports conventional unquoted identifiers. Quoted
|
|
284
|
+
mixed-case objects can appear in the browser but their definitions cannot
|
|
285
|
+
currently be loaded from it.
|
|
286
|
+
|
|
215
287
|
| Key | Action |
|
|
216
288
|
| --- | --- |
|
|
217
289
|
| Printable text | Filter object names |
|
|
@@ -249,7 +321,8 @@ The `F9` schema browser groups tables, views, procedures, functions, packages, t
|
|
|
249
321
|
workspace/
|
|
250
322
|
sql/ saved SQL files
|
|
251
323
|
plsql/ saved PL/SQL files
|
|
252
|
-
results/
|
|
324
|
+
results/ reserved output directory
|
|
253
325
|
```
|
|
254
326
|
|
|
255
|
-
The first launch creates the workspace folders and starter SQL/PLSQL files.
|
|
327
|
+
The first launch creates the workspace folders and starter SQL/PLSQL files. The
|
|
328
|
+
terminal UI does not currently provide a query-export command.
|
|
@@ -41,6 +41,11 @@ class TransactionCompletion:
|
|
|
41
41
|
cleanup_error: Exception | None = None
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class ConnectionCompletion:
|
|
46
|
+
old_session_close_error: Exception | None = None
|
|
47
|
+
|
|
48
|
+
|
|
44
49
|
@dataclass(frozen=True)
|
|
45
50
|
class TransactionModeChange:
|
|
46
51
|
resolution: str | None
|
|
@@ -224,6 +229,12 @@ class AppDbMixin:
|
|
|
224
229
|
worker.shutdown(timeout=timeout)
|
|
225
230
|
|
|
226
231
|
def try_connect(self, force: bool = False) -> None:
|
|
232
|
+
if force:
|
|
233
|
+
self.reconnect_database()
|
|
234
|
+
return
|
|
235
|
+
self._start_connect(force=False)
|
|
236
|
+
|
|
237
|
+
def _start_connect(self, *, force: bool) -> None:
|
|
227
238
|
if self.reject_if_db_operation_active():
|
|
228
239
|
return
|
|
229
240
|
|
|
@@ -231,13 +242,23 @@ class AppDbMixin:
|
|
|
231
242
|
self.close_all_result_continuations()
|
|
232
243
|
self.invalidate_results_after_rollback()
|
|
233
244
|
|
|
234
|
-
def connect(db: Any, progress: Callable[[str], None]) ->
|
|
245
|
+
def connect(db: Any, progress: Callable[[str], None]) -> ConnectionCompletion:
|
|
246
|
+
close_error = None
|
|
235
247
|
if force:
|
|
236
|
-
|
|
248
|
+
try:
|
|
249
|
+
db.close()
|
|
250
|
+
except Exception as exc:
|
|
251
|
+
close_error = exc
|
|
237
252
|
db.ensure_connected()
|
|
253
|
+
return ConnectionCompletion(close_error)
|
|
238
254
|
|
|
239
|
-
def connected(
|
|
255
|
+
def connected(result: ConnectionCompletion) -> None:
|
|
240
256
|
self.state.status = f"Connected as {self.state.config.user}"
|
|
257
|
+
if result.old_session_close_error is not None:
|
|
258
|
+
self.state.status += (
|
|
259
|
+
" (warning: old session close failed: "
|
|
260
|
+
f"{short_error(result.old_session_close_error)})"
|
|
261
|
+
)
|
|
241
262
|
|
|
242
263
|
def connect_failed(exc: Exception) -> None:
|
|
243
264
|
self.state.status = "Connection failed"
|
|
@@ -273,11 +294,19 @@ class AppDbMixin:
|
|
|
273
294
|
return
|
|
274
295
|
self.state.status = "Transaction mode unchanged"
|
|
275
296
|
|
|
276
|
-
def prompt_pending_transaction(
|
|
297
|
+
def prompt_pending_transaction(
|
|
298
|
+
self,
|
|
299
|
+
cancel_status: str,
|
|
300
|
+
*,
|
|
301
|
+
allow_discard: bool = False,
|
|
302
|
+
) -> str | None:
|
|
277
303
|
if not has_uncommitted_changes(self.state.db):
|
|
278
304
|
return "none"
|
|
305
|
+
choices = "c=commit, r=rollback, x=cancel"
|
|
306
|
+
if allow_discard:
|
|
307
|
+
choices = "c=commit, r=rollback, d=discard session, x=cancel"
|
|
279
308
|
answer = self.prompt(
|
|
280
|
-
"Pending transaction:
|
|
309
|
+
f"Pending transaction: {choices}",
|
|
281
310
|
"",
|
|
282
311
|
)
|
|
283
312
|
if answer is None:
|
|
@@ -288,6 +317,8 @@ class AppDbMixin:
|
|
|
288
317
|
return "commit"
|
|
289
318
|
if normalized in {"r", "rollback"}:
|
|
290
319
|
return "rollback"
|
|
320
|
+
if allow_discard and normalized in {"d", "discard", "discard session"}:
|
|
321
|
+
return "discard"
|
|
291
322
|
self.state.status = cancel_status
|
|
292
323
|
return None
|
|
293
324
|
|
|
@@ -369,6 +400,8 @@ class AppDbMixin:
|
|
|
369
400
|
self,
|
|
370
401
|
after_success: Callable[[], None] | None = None,
|
|
371
402
|
after_error: Callable[[], None] | None = None,
|
|
403
|
+
*,
|
|
404
|
+
preserve_results_on_error: bool = False,
|
|
372
405
|
) -> bool:
|
|
373
406
|
if self.reject_if_db_operation_active():
|
|
374
407
|
return False
|
|
@@ -380,7 +413,10 @@ class AppDbMixin:
|
|
|
380
413
|
|
|
381
414
|
def commit_failed(exc: Exception) -> None:
|
|
382
415
|
self.state.status = "Commit failed"
|
|
383
|
-
self.set_results(
|
|
416
|
+
self.set_results(
|
|
417
|
+
["ERROR committing transaction:", *wrap_error(exc)],
|
|
418
|
+
clear_table=not preserve_results_on_error,
|
|
419
|
+
)
|
|
384
420
|
if after_error is not None:
|
|
385
421
|
after_error()
|
|
386
422
|
|
|
@@ -396,6 +432,8 @@ class AppDbMixin:
|
|
|
396
432
|
self,
|
|
397
433
|
after_success: Callable[[], None] | None = None,
|
|
398
434
|
after_error: Callable[[], None] | None = None,
|
|
435
|
+
*,
|
|
436
|
+
preserve_results_on_error: bool = False,
|
|
399
437
|
) -> bool:
|
|
400
438
|
if self.reject_if_db_operation_active():
|
|
401
439
|
return False
|
|
@@ -423,7 +461,10 @@ class AppDbMixin:
|
|
|
423
461
|
|
|
424
462
|
def rollback_failed(exc: Exception) -> None:
|
|
425
463
|
self.state.status = "Rollback failed"
|
|
426
|
-
self.set_results(
|
|
464
|
+
self.set_results(
|
|
465
|
+
["ERROR rolling back transaction:", *wrap_error(exc)],
|
|
466
|
+
clear_table=not preserve_results_on_error,
|
|
467
|
+
)
|
|
427
468
|
if after_error is not None:
|
|
428
469
|
after_error()
|
|
429
470
|
|
|
@@ -161,7 +161,32 @@ class AppInputMixin:
|
|
|
161
161
|
self.commit_transaction()
|
|
162
162
|
|
|
163
163
|
def reconnect_database(self) -> None:
|
|
164
|
-
self.
|
|
164
|
+
if self.reject_if_db_operation_active():
|
|
165
|
+
return
|
|
166
|
+
resolution = self.prompt_pending_transaction(
|
|
167
|
+
"Reconnect cancelled",
|
|
168
|
+
allow_discard=True,
|
|
169
|
+
)
|
|
170
|
+
if resolution is None:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
def reconnect_after_resolution() -> None:
|
|
174
|
+
self._start_connect(force=True)
|
|
175
|
+
|
|
176
|
+
if resolution == "commit":
|
|
177
|
+
self.commit_transaction(
|
|
178
|
+
after_success=reconnect_after_resolution,
|
|
179
|
+
preserve_results_on_error=True,
|
|
180
|
+
)
|
|
181
|
+
elif resolution == "rollback":
|
|
182
|
+
self.rollback_transaction(
|
|
183
|
+
after_success=reconnect_after_resolution,
|
|
184
|
+
preserve_results_on_error=True,
|
|
185
|
+
)
|
|
186
|
+
elif resolution in {"discard", "none"}:
|
|
187
|
+
reconnect_after_resolution()
|
|
188
|
+
else:
|
|
189
|
+
self.state.status = "Reconnect cancelled"
|
|
165
190
|
|
|
166
191
|
def refresh_workspace_file_list(self) -> None:
|
|
167
192
|
self.state.files = _legacy_ui_attr("list_workspace_files", list_workspace_files)(self.state.config)
|