plsqlwks 0.1.3__tar.gz → 0.1.4__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.4}/PKG-INFO +87 -11
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/README.md +86 -10
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/__init__.py +1 -1
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app_db.py +48 -7
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app_input.py +26 -1
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks.egg-info/PKG-INFO +87 -11
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_oracle_integration.py +65 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_ui_keys.py +309 -1
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/license.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/__main__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/config/__init__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/config/loader.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/config/models.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/config/paths.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/config/session.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/config/settings.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/__init__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/editing.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/execution.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/explain.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/health.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/metadata.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/models.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/session.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/sql_analysis.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/db/transactions.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/sqlbinds.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/sqlsplit.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/__init__.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app_editor.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app_files.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app_render.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app_results.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/app_tabs_browser.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/browser.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/buffer.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/clipboard.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/commands.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/completion.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/constants.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/db_worker.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/display.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/errors.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/help.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/keys.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/menu.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/results.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/sql.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/state.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/ui/syntax.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks/workspace.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks.egg-info/SOURCES.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks.egg-info/dependency_links.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks.egg-info/entry_points.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks.egg-info/requires.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/plsqlwks.egg-info/top_level.txt +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/pyproject.toml +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/setup.cfg +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_config_workspace.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_db_editing.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_db_helpers.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_db_worker.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_packaging.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_pty_keys.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_rchar.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_sqlbinds.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/tests/test_sqlsplit.py +0 -0
- {plsqlwks-0.1.3 → plsqlwks-0.1.4}/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.4
|
|
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
|
|
@@ -74,6 +83,21 @@ For a regular local install, use:
|
|
|
74
83
|
python3 -m pip install .
|
|
75
84
|
```
|
|
76
85
|
|
|
86
|
+
Run the default test and lint checks from a development checkout with:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
python3 -m pytest -q
|
|
90
|
+
python3 -m ruff check .
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Oracle integration tests require the connection environment variables shown
|
|
94
|
+
above and an accessible password file. Enable every test group with:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
PLSQLWKS_TEST_ORACLE=1 PLSQLWKS_TEST_PTY=1 PLSQLWKS_TEST_SLOW=1 \
|
|
98
|
+
python3 -m pytest -q -rs
|
|
99
|
+
```
|
|
100
|
+
|
|
77
101
|
## Run
|
|
78
102
|
|
|
79
103
|
After installation, start the workspace with:
|
|
@@ -92,6 +116,15 @@ The CLI option takes precedence over `PLSQLWKS_WORKSPACE`. For compatibility, a
|
|
|
92
116
|
source checkout that already contains a configured `workspace/` continues to
|
|
93
117
|
use it and displays a migration notice; files are never moved automatically.
|
|
94
118
|
|
|
119
|
+
Choose the initial transaction mode for one invocation with `--manual` or
|
|
120
|
+
`--autocommit`. These options override `[database] autocommit` from the active
|
|
121
|
+
`config.ini`:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
plsqlwks --manual
|
|
125
|
+
plsqlwks --autocommit
|
|
126
|
+
```
|
|
127
|
+
|
|
95
128
|
You can also run it directly as a module from the source tree:
|
|
96
129
|
|
|
97
130
|
```bash
|
|
@@ -104,7 +137,25 @@ The app opens a split-screen terminal workspace:
|
|
|
104
137
|
- results/messages below
|
|
105
138
|
- command/status bar at the bottom
|
|
106
139
|
|
|
107
|
-
The terminal UI uses the active
|
|
140
|
+
The terminal UI uses the active locale for keyboard input and display. If that
|
|
141
|
+
locale cannot be initialized, the app tries `C.UTF-8`, `en_US.UTF-8`, and
|
|
142
|
+
`UTF-8`. A valid plain `C` locale remains active, so configure a UTF-8 locale in
|
|
143
|
+
the shell when non-ASCII input or display is required.
|
|
144
|
+
|
|
145
|
+
Oracle operations run serially on one persistent background worker. The UI
|
|
146
|
+
continues to redraw and accept input while an operation runs, but it rejects a
|
|
147
|
+
second database operation until the active one finishes. `Ctrl-C` requests
|
|
148
|
+
interruption of the active operation. Reconnecting closes open result pages and
|
|
149
|
+
clears query results loaded by the previous connection. If a manual transaction
|
|
150
|
+
has pending changes, reconnect first asks whether to commit, roll back, discard
|
|
151
|
+
the session, or cancel. Commit or rollback must succeed before reconnect closes
|
|
152
|
+
the old connection. If either action fails, plsqlwks does not close the current
|
|
153
|
+
connection or clear its loaded results; reconnect can be tried again with an
|
|
154
|
+
explicit discard. Discard closes the session without resolving the transaction
|
|
155
|
+
explicitly, so Oracle rolls its uncommitted work back.
|
|
156
|
+
If the old connection is already dead and reports an error while closing,
|
|
157
|
+
plsqlwks still attempts the replacement connection and reports the close error
|
|
158
|
+
as a warning after a successful reconnect.
|
|
108
159
|
|
|
109
160
|
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
161
|
|
|
@@ -127,7 +178,7 @@ After a successful quit, plsqlwks records the open file-backed tabs, active tab,
|
|
|
127
178
|
| `Alt-1`..`Alt-9` | Jump to visible file tab |
|
|
128
179
|
| `Ctrl-Q` | Quit |
|
|
129
180
|
| `Ctrl-C` while running | Interrupt the active database operation |
|
|
130
|
-
| `Ctrl-Alt-C` |
|
|
181
|
+
| `Ctrl-Alt-C` | Insert the active result-grid draft, or commit when no draft is active |
|
|
131
182
|
| `Ctrl-Alt-R` | Roll back the current transaction |
|
|
132
183
|
|
|
133
184
|
### Editor
|
|
@@ -182,7 +233,12 @@ Set `[database] remember_bind_values = yes` in the active `config.ini` to prefil
|
|
|
182
233
|
|
|
183
234
|
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
235
|
|
|
185
|
-
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
236
|
+
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
237
|
+
current-schema object names, and table/view columns from cached or lazily loaded
|
|
238
|
+
database metadata; multiple matches open a picker. Database-object and column
|
|
239
|
+
completion currently supports conventional unquoted identifiers; quoted
|
|
240
|
+
mixed-case names are not preserved. Use `Alt-+` to reload schema-object metadata
|
|
241
|
+
and clear cached column metadata.
|
|
186
242
|
|
|
187
243
|
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
244
|
|
|
@@ -225,16 +281,35 @@ Read-only mode is a client-side guardrail that rejects statements which the SQL
|
|
|
225
281
|
| `PageUp` / `PageDown` | Scroll explain-plan lines by page |
|
|
226
282
|
| `Home` / `End` | Move to the first/last explain-plan line |
|
|
227
283
|
|
|
228
|
-
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
284
|
+
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
285
|
+
appends the next result page. For simple queries against one conventional
|
|
286
|
+
unquoted current-schema table that include `ROWID`, press `Enter` on a directly
|
|
287
|
+
selected unquoted table column to update it by rowid. Press `INS` in the grid to
|
|
288
|
+
prepare a draft row at the top of the grid, edit its cells with `Enter`, use
|
|
289
|
+
`Ctrl-Alt-C` to insert it, or use `Esc` to cancel it.
|
|
229
290
|
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
|
-
|
|
291
|
+
Grid edits compare the selected cell's originally loaded typed value as well as
|
|
292
|
+
the `ROWID`. If another session changes that cell or deletes the row first, the
|
|
293
|
+
edit is rejected and the query must be refreshed. Changes to other cells in the
|
|
294
|
+
same row do not cause a conflict. NUMBER input uses decimal notation with a
|
|
295
|
+
period; DATE and TIMESTAMP input uses ISO
|
|
296
|
+
`YYYY-MM-DD[ HH:MM:SS[.ffffff]]`; RAW and BLOB input uses hexadecimal bytes.
|
|
297
|
+
Character and CLOB input is preserved as entered. Types that cannot be compared
|
|
298
|
+
without losing information, including time-zone timestamps and timestamps with
|
|
299
|
+
precision above six, are read-only in the grid.
|
|
300
|
+
Displayed CLOB and BLOB values are limited to the first 65,536 characters or
|
|
301
|
+
bytes and include a truncation marker with the full size. Truncated LOB cells
|
|
302
|
+
cannot be edited safely. Schema-browser DDL is always read in full.
|
|
232
303
|
`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
304
|
|
|
234
305
|
### Schema Browser
|
|
235
306
|
|
|
236
307
|
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
308
|
|
|
309
|
+
Definition loading supports conventional unquoted identifiers. Quoted
|
|
310
|
+
mixed-case objects can appear in the browser but their definitions cannot
|
|
311
|
+
currently be loaded from it.
|
|
312
|
+
|
|
238
313
|
| Key | Action |
|
|
239
314
|
| --- | --- |
|
|
240
315
|
| Printable text | Filter object names |
|
|
@@ -272,7 +347,8 @@ The `F9` schema browser groups tables, views, procedures, functions, packages, t
|
|
|
272
347
|
workspace/
|
|
273
348
|
sql/ saved SQL files
|
|
274
349
|
plsql/ saved PL/SQL files
|
|
275
|
-
results/
|
|
350
|
+
results/ reserved output directory
|
|
276
351
|
```
|
|
277
352
|
|
|
278
|
-
The first launch creates the workspace folders and starter SQL/PLSQL files.
|
|
353
|
+
The first launch creates the workspace folders and starter SQL/PLSQL files. The
|
|
354
|
+
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
|
|
@@ -51,6 +60,21 @@ For a regular local install, use:
|
|
|
51
60
|
python3 -m pip install .
|
|
52
61
|
```
|
|
53
62
|
|
|
63
|
+
Run the default test and lint checks from a development checkout with:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python3 -m pytest -q
|
|
67
|
+
python3 -m ruff check .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Oracle integration tests require the connection environment variables shown
|
|
71
|
+
above and an accessible password file. Enable every test group with:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
PLSQLWKS_TEST_ORACLE=1 PLSQLWKS_TEST_PTY=1 PLSQLWKS_TEST_SLOW=1 \
|
|
75
|
+
python3 -m pytest -q -rs
|
|
76
|
+
```
|
|
77
|
+
|
|
54
78
|
## Run
|
|
55
79
|
|
|
56
80
|
After installation, start the workspace with:
|
|
@@ -69,6 +93,15 @@ The CLI option takes precedence over `PLSQLWKS_WORKSPACE`. For compatibility, a
|
|
|
69
93
|
source checkout that already contains a configured `workspace/` continues to
|
|
70
94
|
use it and displays a migration notice; files are never moved automatically.
|
|
71
95
|
|
|
96
|
+
Choose the initial transaction mode for one invocation with `--manual` or
|
|
97
|
+
`--autocommit`. These options override `[database] autocommit` from the active
|
|
98
|
+
`config.ini`:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
plsqlwks --manual
|
|
102
|
+
plsqlwks --autocommit
|
|
103
|
+
```
|
|
104
|
+
|
|
72
105
|
You can also run it directly as a module from the source tree:
|
|
73
106
|
|
|
74
107
|
```bash
|
|
@@ -81,7 +114,25 @@ The app opens a split-screen terminal workspace:
|
|
|
81
114
|
- results/messages below
|
|
82
115
|
- command/status bar at the bottom
|
|
83
116
|
|
|
84
|
-
The terminal UI uses the active
|
|
117
|
+
The terminal UI uses the active locale for keyboard input and display. If that
|
|
118
|
+
locale cannot be initialized, the app tries `C.UTF-8`, `en_US.UTF-8`, and
|
|
119
|
+
`UTF-8`. A valid plain `C` locale remains active, so configure a UTF-8 locale in
|
|
120
|
+
the shell when non-ASCII input or display is required.
|
|
121
|
+
|
|
122
|
+
Oracle operations run serially on one persistent background worker. The UI
|
|
123
|
+
continues to redraw and accept input while an operation runs, but it rejects a
|
|
124
|
+
second database operation until the active one finishes. `Ctrl-C` requests
|
|
125
|
+
interruption of the active operation. Reconnecting closes open result pages and
|
|
126
|
+
clears query results loaded by the previous connection. If a manual transaction
|
|
127
|
+
has pending changes, reconnect first asks whether to commit, roll back, discard
|
|
128
|
+
the session, or cancel. Commit or rollback must succeed before reconnect closes
|
|
129
|
+
the old connection. If either action fails, plsqlwks does not close the current
|
|
130
|
+
connection or clear its loaded results; reconnect can be tried again with an
|
|
131
|
+
explicit discard. Discard closes the session without resolving the transaction
|
|
132
|
+
explicitly, so Oracle rolls its uncommitted work back.
|
|
133
|
+
If the old connection is already dead and reports an error while closing,
|
|
134
|
+
plsqlwks still attempts the replacement connection and reports the close error
|
|
135
|
+
as a warning after a successful reconnect.
|
|
85
136
|
|
|
86
137
|
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
138
|
|
|
@@ -104,7 +155,7 @@ After a successful quit, plsqlwks records the open file-backed tabs, active tab,
|
|
|
104
155
|
| `Alt-1`..`Alt-9` | Jump to visible file tab |
|
|
105
156
|
| `Ctrl-Q` | Quit |
|
|
106
157
|
| `Ctrl-C` while running | Interrupt the active database operation |
|
|
107
|
-
| `Ctrl-Alt-C` |
|
|
158
|
+
| `Ctrl-Alt-C` | Insert the active result-grid draft, or commit when no draft is active |
|
|
108
159
|
| `Ctrl-Alt-R` | Roll back the current transaction |
|
|
109
160
|
|
|
110
161
|
### Editor
|
|
@@ -159,7 +210,12 @@ Set `[database] remember_bind_values = yes` in the active `config.ini` to prefil
|
|
|
159
210
|
|
|
160
211
|
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
212
|
|
|
162
|
-
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
213
|
+
Editor autocomplete is available with `Shift-Tab`. It completes PL/SQL keywords,
|
|
214
|
+
current-schema object names, and table/view columns from cached or lazily loaded
|
|
215
|
+
database metadata; multiple matches open a picker. Database-object and column
|
|
216
|
+
completion currently supports conventional unquoted identifiers; quoted
|
|
217
|
+
mixed-case names are not preserved. Use `Alt-+` to reload schema-object metadata
|
|
218
|
+
and clear cached column metadata.
|
|
163
219
|
|
|
164
220
|
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
221
|
|
|
@@ -202,16 +258,35 @@ Read-only mode is a client-side guardrail that rejects statements which the SQL
|
|
|
202
258
|
| `PageUp` / `PageDown` | Scroll explain-plan lines by page |
|
|
203
259
|
| `Home` / `End` | Move to the first/last explain-plan line |
|
|
204
260
|
|
|
205
|
-
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
261
|
+
When more result rows are available, `PageDown` at the loaded end fetches and
|
|
262
|
+
appends the next result page. For simple queries against one conventional
|
|
263
|
+
unquoted current-schema table that include `ROWID`, press `Enter` on a directly
|
|
264
|
+
selected unquoted table column to update it by rowid. Press `INS` in the grid to
|
|
265
|
+
prepare a draft row at the top of the grid, edit its cells with `Enter`, use
|
|
266
|
+
`Ctrl-Alt-C` to insert it, or use `Esc` to cancel it.
|
|
206
267
|
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
|
-
|
|
268
|
+
Grid edits compare the selected cell's originally loaded typed value as well as
|
|
269
|
+
the `ROWID`. If another session changes that cell or deletes the row first, the
|
|
270
|
+
edit is rejected and the query must be refreshed. Changes to other cells in the
|
|
271
|
+
same row do not cause a conflict. NUMBER input uses decimal notation with a
|
|
272
|
+
period; DATE and TIMESTAMP input uses ISO
|
|
273
|
+
`YYYY-MM-DD[ HH:MM:SS[.ffffff]]`; RAW and BLOB input uses hexadecimal bytes.
|
|
274
|
+
Character and CLOB input is preserved as entered. Types that cannot be compared
|
|
275
|
+
without losing information, including time-zone timestamps and timestamps with
|
|
276
|
+
precision above six, are read-only in the grid.
|
|
277
|
+
Displayed CLOB and BLOB values are limited to the first 65,536 characters or
|
|
278
|
+
bytes and include a truncation marker with the full size. Truncated LOB cells
|
|
279
|
+
cannot be edited safely. Schema-browser DDL is always read in full.
|
|
209
280
|
`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
281
|
|
|
211
282
|
### Schema Browser
|
|
212
283
|
|
|
213
284
|
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
285
|
|
|
286
|
+
Definition loading supports conventional unquoted identifiers. Quoted
|
|
287
|
+
mixed-case objects can appear in the browser but their definitions cannot
|
|
288
|
+
currently be loaded from it.
|
|
289
|
+
|
|
215
290
|
| Key | Action |
|
|
216
291
|
| --- | --- |
|
|
217
292
|
| Printable text | Filter object names |
|
|
@@ -249,7 +324,8 @@ The `F9` schema browser groups tables, views, procedures, functions, packages, t
|
|
|
249
324
|
workspace/
|
|
250
325
|
sql/ saved SQL files
|
|
251
326
|
plsql/ saved PL/SQL files
|
|
252
|
-
results/
|
|
327
|
+
results/ reserved output directory
|
|
253
328
|
```
|
|
254
329
|
|
|
255
|
-
The first launch creates the workspace folders and starter SQL/PLSQL files.
|
|
330
|
+
The first launch creates the workspace folders and starter SQL/PLSQL files. The
|
|
331
|
+
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)
|