Add a PostgreSQL psql interactive terminal shell with backslash meta-commands, SQL statement handling with multi-line buffering, and canned responses for common queries. Add username-based shell routing via [shell.username_routes] config (second priority after credential- specific shell, before random selection). Bump version to 0.13.0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
156 lines
7.5 KiB
Go
156 lines
7.5 KiB
Go
package psql
|
|
|
|
import "fmt"
|
|
|
|
func startupBanner(version string) string {
|
|
return fmt.Sprintf("psql (%s)\nType \"help\" for help.\n", version)
|
|
}
|
|
|
|
func listTables() string {
|
|
return ` List of relations
|
|
Schema | Name | Type | Owner
|
|
--------+---------------+-------+----------
|
|
public | audit_log | table | postgres
|
|
public | credentials | table | postgres
|
|
public | sessions | table | postgres
|
|
public | users | table | postgres
|
|
(4 rows)`
|
|
}
|
|
|
|
func listDatabases() string {
|
|
return ` List of databases
|
|
Name | Owner | Encoding | Collate | Ctype | Access privileges
|
|
-----------+----------+----------+-------------+-------------+-----------------------
|
|
app_db | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
|
|
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
|
|
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
|
|
| | | | | postgres=CTc/postgres
|
|
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
|
|
| | | | | postgres=CTc/postgres
|
|
(4 rows)`
|
|
}
|
|
|
|
func listRoles() string {
|
|
return ` List of roles
|
|
Role name | Attributes | Member of
|
|
-----------+------------------------------------------------------------+-----------
|
|
app_user | | {}
|
|
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
|
|
readonly | Cannot login | {}`
|
|
}
|
|
|
|
func describeTable(name string) string {
|
|
switch name {
|
|
case "users":
|
|
return ` Table "public.users"
|
|
Column | Type | Collation | Nullable | Default
|
|
------------+-----------------------------+-----------+----------+-----------------------------------
|
|
id | integer | | not null | nextval('users_id_seq'::regclass)
|
|
username | character varying(255) | | not null |
|
|
email | character varying(255) | | not null |
|
|
password | character varying(255) | | not null |
|
|
created_at | timestamp without time zone | | | now()
|
|
updated_at | timestamp without time zone | | | now()
|
|
Indexes:
|
|
"users_pkey" PRIMARY KEY, btree (id)
|
|
"users_email_key" UNIQUE, btree (email)
|
|
"users_username_key" UNIQUE, btree (username)`
|
|
case "sessions":
|
|
return ` Table "public.sessions"
|
|
Column | Type | Collation | Nullable | Default
|
|
------------+-----------------------------+-----------+----------+--------------------------------------
|
|
id | integer | | not null | nextval('sessions_id_seq'::regclass)
|
|
user_id | integer | | |
|
|
token | character varying(255) | | not null |
|
|
ip_address | inet | | |
|
|
created_at | timestamp without time zone | | | now()
|
|
expires_at | timestamp without time zone | | not null |
|
|
Indexes:
|
|
"sessions_pkey" PRIMARY KEY, btree (id)
|
|
"sessions_token_key" UNIQUE, btree (token)
|
|
Foreign-key constraints:
|
|
"sessions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)`
|
|
case "credentials":
|
|
return ` Table "public.credentials"
|
|
Column | Type | Collation | Nullable | Default
|
|
-----------+-----------------------------+-----------+----------+-----------------------------------------
|
|
id | integer | | not null | nextval('credentials_id_seq'::regclass)
|
|
user_id | integer | | |
|
|
type | character varying(50) | | not null |
|
|
value | text | | not null |
|
|
created_at| timestamp without time zone | | | now()
|
|
Indexes:
|
|
"credentials_pkey" PRIMARY KEY, btree (id)
|
|
Foreign-key constraints:
|
|
"credentials_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)`
|
|
case "audit_log":
|
|
return ` Table "public.audit_log"
|
|
Column | Type | Collation | Nullable | Default
|
|
------------+-----------------------------+-----------+----------+---------------------------------------
|
|
id | integer | | not null | nextval('audit_log_id_seq'::regclass)
|
|
user_id | integer | | |
|
|
action | character varying(100) | | not null |
|
|
details | text | | |
|
|
ip_address | inet | | |
|
|
created_at | timestamp without time zone | | | now()
|
|
Indexes:
|
|
"audit_log_pkey" PRIMARY KEY, btree (id)
|
|
Foreign-key constraints:
|
|
"audit_log_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)`
|
|
default:
|
|
return fmt.Sprintf("Did not find any relation named \"%s\".", name)
|
|
}
|
|
}
|
|
|
|
func connInfo(dbName string) string {
|
|
return fmt.Sprintf("You are connected to database \"%s\" as user \"postgres\" via socket in \"/var/run/postgresql\" at port \"5432\".", dbName)
|
|
}
|
|
|
|
func backslashHelp() string {
|
|
return `General
|
|
\copyright show PostgreSQL usage and distribution terms
|
|
\crosstabview [COLUMNS] execute query and display result in crosstab
|
|
\errverbose show most recent error message at maximum verbosity
|
|
\g [(OPTIONS)] [FILE] execute query (and send result to file or |pipe)
|
|
\gdesc describe result of query, without executing it
|
|
\gexec execute query, then execute each value in its result
|
|
\gset [PREFIX] execute query and store result in psql variables
|
|
\gx [(OPTIONS)] [FILE] as \g, but forces expanded output mode
|
|
\q quit psql
|
|
\watch [SEC] execute query every SEC seconds
|
|
|
|
Informational
|
|
(options: S = show system objects, + = additional detail)
|
|
\d[S+] list tables, views, and sequences
|
|
\d[S+] NAME describe table, view, sequence, or index
|
|
\da[S] [PATTERN] list aggregates
|
|
\dA[+] [PATTERN] list access methods
|
|
\dt[S+] [PATTERN] list tables
|
|
\du[S+] [PATTERN] list roles
|
|
\l[+] [PATTERN] list databases`
|
|
}
|
|
|
|
func sqlHelp() string {
|
|
return `Available help:
|
|
ABORT CREATE LANGUAGE
|
|
ALTER AGGREGATE CREATE MATERIALIZED VIEW
|
|
ALTER COLLATION CREATE OPERATOR
|
|
ALTER CONVERSION CREATE POLICY
|
|
ALTER DATABASE CREATE PROCEDURE
|
|
ALTER DEFAULT PRIVILEGES CREATE PUBLICATION
|
|
ALTER DOMAIN CREATE ROLE
|
|
ALTER EVENT TRIGGER CREATE RULE
|
|
ALTER EXTENSION CREATE SCHEMA
|
|
ALTER FOREIGN DATA WRAPPER CREATE SEQUENCE
|
|
ALTER FOREIGN TABLE CREATE SERVER
|
|
ALTER FUNCTION CREATE STATISTICS
|
|
ALTER GROUP CREATE SUBSCRIPTION
|
|
ALTER INDEX CREATE TABLE
|
|
ALTER LANGUAGE CREATE TABLESPACE
|
|
BEGIN DELETE
|
|
COMMIT DROP TABLE
|
|
CREATE DATABASE INSERT
|
|
CREATE INDEX ROLLBACK
|
|
SELECT UPDATE`
|
|
}
|