PLSQLWKS quick start

PLSQLWKS quick start

This guide goes from a source checkout to a first Oracle query in about five minutes. It then covers a few useful query workflows, the most common startup problems, and a small installed plugin. For the complete key reference and runtime behavior, see README.md; for the public extension contract, see API.md, with packaging guidance in PLUGINS.md.

1. Check the prerequisites

You need:

The DSN in this guide has the form host:port/service_name, for example db.example.test:1521/freepdb1. Ask the database administrator for the exact value when in doubt; a service name is not necessarily the same as an Oracle SID.

2. Install PLSQLWKS

From a checkout of the repository, create an isolated environment and install the application:

python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install .

Confirm that the console command is available:

plsqlwks --help
plsqlwks --version

For development, replace the final install command with python3 -m pip install -e '.[dev]'.

3. Configure the Oracle connection

The following Bash example prompts without echoing the password, creates a private password file, and exports the connection for the current shell. Change the user and DSN to match the target database.

config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/plsqlwks"
mkdir -p "$config_dir"
chmod 700 "$config_dir"
umask 077
IFS= read -r -s -p "Oracle password: " oracle_password
printf '\n'
printf '%s' "$oracle_password" > "$config_dir/orapass"
unset oracle_password

export ORACLE_USER='hr'
export ORACLE_PASSWORD_FILE="$config_dir/orapass"
export ORACLE_DSN='127.0.0.1:1521/free'

PLSQLWKS preserves spaces in the password and removes only final CR/LF line endings. Keep the password file private; on POSIX systems its mode should be 0600.

These variables override the built-in defaults. To keep them across terminal sessions, add only the three export lines to the shell profile; do not put the password itself in the profile.

4. Run the first query

Start in manual transaction mode:

plsqlwks --manual

The header should say db connected. Enter this query in the upper editor:

select
    sys_context('USERENV', 'SESSION_USER') as session_user,
    sys_context('USERENV', 'DB_NAME') as database_name,
    to_char(systimestamp, 'YYYY-MM-DD HH24:MI:SS TZH:TZM') as database_time
from dual;

Press F5, Ctrl-Enter, or Alt-X. The lower pane shows one row from the connected database. Press Tab to focus the grid, arrow keys to move between cells, and Tab again to return to the editor. Press Ctrl-Q to quit.

PLSQLWKS editor and result workspace

The result is a connection check that does not depend on application tables or sample schemas. The displayed user and database name should match the intended target before running any write statement.

Practical query examples

Prompt for a bind value

Execute this statement with F5:

select :name as supplied_name,
       'Hello, ' || :name as greeting
from dual;

PLSQLWKS prompts once for :name and passes the answer as an Oracle bind value. Set [database] remember_bind_values = yes in the active config.ini if the same bind should be prefilled later in the application session.

Run a script and inspect DBMS_OUTPUT

Use F11 for a multi-statement script:

begin
    dbms_output.put_line(
        'Connected as ' || sys_context('USERENV', 'SESSION_USER')
    );
end;
/

select count(*) as visible_objects
from user_objects;

F11 runs the whole buffer (or the selection). Press F6 to switch between normal results and captured DBMS_OUTPUT.

Page through a larger result

If the connected schema has the Oracle HR sample tables, try:

select employee_id, first_name, last_name, department_id
from employees
order by employee_id;

PLSQLWKS_MAX_ROWS controls the number of rows loaded per page. With the result grid focused, use Ctrl-PageDown and Ctrl-PageUp to page. The status and result label say when more rows are available.

Run independent queries in multiple tabs

The default database session is serialized. To let queries in different editor tabs overlap, add this to the active config.ini and restart:

[database]
threaded_queries = yes

This setting is config-only: there is no CLI or environment override. Each used tab then owns its Oracle session and transaction; & marks a tab with running database work. The first Ctrl-C requests cancellation for the active tab, and a second press during a query or fetch force-disconnects that tab’s client session without stopping work in other tabs.

Export query rows

The CSV, HTML, and XLSX commands are maintained managed packages, not automatic parts of the application install. From a checkout or unpacked source distribution, install the formats you want and restart:

plsqlwks plugin install plugins/packages/csv-export-1.0.0.plwgn
plsqlwks plugin install plugins/packages/html-export-1.0.0.plwgn
python3 -m pip install 'plsqlwks[xlsx]'
plsqlwks plugin install plugins/packages/xlsx-export-1.0.0.plwgn

Newly installed packages are enabled by default unless this workspace already explicitly disabled the same ID. With a table result visible, press Alt-O, type export, and choose an installed format. A second picker defaults to Loaded rows only, which writes exactly the rows already in the grid without another database fetch. Choose All available rows to fetch every available continuation page into a private buffer while keeping the result grid unchanged. The status bar shows fetch and write progress; press Ctrl-C to cancel either phase without installing a partial destination file. An interrupted fetch keeps the original grid rows but detaches its cursor and editing state. Output names default to the workspace results/ directory. XLSX requires both the optional plsqlwks[xlsx] dependency extra and the managed xlsx-export package.

Import data with csv2oratab

The maintained csv2oratab package is also available from the catalog. Install its cryptographic-verification dependency and managed package, then restart PLSQLWKS:

python3 -m pip install 'plsqlwks[csv2oratab]'
plsqlwks plugin install plugins/packages/csv2oratab-0.1.0.plwgn

The package is enabled by default. Press Alt-O, type csv2oratab, and choose Data -> Import data with csv2oratab. PLSQLWKS suspends its screen while the readable bundled csv2oratab 1.0.0rc1 TUI runs as a child process, then restores the screen. The child creates its own Oracle connection from csv2oratab’s ORACLE_* settings and license configuration; the host connection and credentials are never transferred. TUI defaults are kept in <workspace>/.csv2oratab.ini, separate from PLSQLWKS config.ini.

The catalog package does not alter csv2oratab’s commercial licensing, legal-review requirements, or database-execution entitlement. See the maintained plug-in catalog for lifecycle, dependency, and source details.

Commit or roll back deliberately

Fresh workspaces use manual transactions. After a DML statement, use Ctrl-Alt-C to commit or Ctrl-Alt-R to roll back. F12 changes transaction mode. The --read-only option adds a client-side guardrail against statements that appear to write, but database privileges remain the security boundary.

Troubleshooting

Symptom What to check
No module named _curses or curses Use a Python build and terminal that provide curses. On native Windows, a supported WSL environment is usually the simplest route.
Password file is missing Check ORACLE_PASSWORD_FILE, create the file, and restart. The path must name a nonempty regular file.
Password permission warning Run chmod 600 "$ORACLE_PASSWORD_FILE"; also keep its parent directory private.
ORA-01017 Recheck ORACLE_USER, rewrite the password file without adding unintended characters, and confirm that the account is valid for this service.
ORA-12514 or ORA-12154 Recheck the service name in ORACLE_DSN. Easy Connect uses host:port/service_name.
ORA-12541, timeout, or connection refused Check host, port, listener availability, VPN, firewall, and DNS from the same terminal.
Header says db disconnected after a network problem Loaded rows remain viewable. Restore connectivity and press Ctrl+= to reconnect; if manual work was pending, treat its outcome as unknown until verified.
Ctrl-Enter inserts a newline or is not recognized Terminal key encodings vary. Use F5 or Alt-X, which run the same current-statement action.
Box drawing or non-ASCII text is garbled Start from a UTF-8 locale such as C.UTF-8; check the available names with locale -a.
The UI is clipped or a picker will not open Enlarge the terminal to at least 80 columns by 24 rows.
A write statement is rejected in read-only mode Restart with --read-write only if the account and task are intended to allow writes.
XLSX export reports that openpyxl is missing Install the optional extra with python3 -m pip install 'plsqlwks[xlsx]', then restart.

Press F1 at any time for the in-application key guide. Connection errors and plugin failures are reported in the lower pane; the final status line provides the shorter summary.

Develop a small command plugin

Plugin API v1 loads trusted Python code in the PLSQLWKS process. This example adds two commands: one reports the loaded-row count, and the other prompts for a label. It needs no dependency beyond PLSQLWKS.

Create this layout outside the PLSQLWKS repository:

result-tools/
  install.plw
  payload/
    __init__.py
    plugin.py

Use this declarative install.plw:

[plugin]
format = 1
id = result-tools
name = Result tools
version = 0.1.0
api_version = 1
factory = plugin:create_plugin

Leave payload/__init__.py empty and put this in payload/plugin.py:

from plsqlwks.plugins import Plugin, PluginCommand, PluginContext


def show_loaded_count(context: PluginContext) -> None:
    result = context.get_active_result()
    if result is None:
        context.set_status("No table result is active")
        return
    suffix = "; more rows exist" if result.has_more else ""
    context.set_status(f"{len(result.rows)} loaded row(s){suffix}")


def label_loaded_result(context: PluginContext) -> None:
    result = context.get_active_result()
    if result is None:
        context.set_status("No table result is active")
        return
    label = context.prompt_text("Result label", result.title)
    if label is not None:
        context.set_status(f"{label}: {len(result.rows)} loaded row(s)")


def create_plugin() -> Plugin:
    return Plugin(
        id="result-tools",
        name="Result tools",
        commands=(
            PluginCommand(
                id="show-loaded-count",
                section="Results",
                title="Show loaded row count",
                handler=show_loaded_count,
                keywords="plugin snapshot rows",
            ),
            PluginCommand(
                id="label-loaded-result",
                section="Results",
                title="Label loaded result",
                handler=label_loaded_result,
                keywords="plugin prompt snapshot",
            ),
        ),
    )

Package and install it:

plsqlwks plugin package /path/to/result-tools
plsqlwks plugin install /path/to/result-tools/dist/result-tools-0.1.0.plwgn
plsqlwks plugin list

Restart PLSQLWKS, open Alt-O, and search for loaded. Plugin factories are discovered only at startup. A bad plugin is skipped with a startup warning, and an exception from a command is shown in the UI rather than ending the session. Use plsqlwks plugin disable result-tools, enable result-tools, or uninstall result-tools for later lifecycle changes; enablement is stored in the selected workspace and every change requires a restart.

Handlers receive an immutable snapshot of already loaded display rows and a small UI-mediated context. API v1 intentionally does not expose database execution, Oracle handles, mutable application state, curses drawing, global shortcuts, background jobs, lifecycle hooks, or hot reload. Installed plugins are not sandboxed, so install them only from sources you trust. Continue with API.md for the normative API and PLUGINS.md for the complete .plwgn package contract, legacy Python entry points, maintained plug-in examples, and plug-in test commands.