2 #include <libex/list.h>
3 #include "libpgcli/pgconn.h"
6 #define NULL_STR "<null>"
8 static void usage (const char *progname
) {
9 dprintf(STDERR_FILENO
, "usage: %s <connection_string>\n", progname
);
13 static void print_server_state (pgconn_t
*conn
) {
14 printf("Date style: %s\n", conn
->date_style
? conn
->date_style
: "");
15 printf("Client encoding: %s\n", conn
->client_encoding
? conn
->client_encoding
: "");
16 printf("Server encoding: %s\n", conn
->server_encoding
? conn
->server_encoding
: "");
17 printf("Session authorization: %s\n", conn
->session_auth
? conn
->session_auth
: "");
18 printf("Integer datetimes: %s\n", 1 == conn
->is_int_datetimes
? "yes" : "no");
19 printf("Superuser: %s\n", 1 == conn
->is_superuser
? "yes" : "no");
20 printf("Server version: %d.%d\n", conn
->major_ver
, conn
->minor_ver
);
21 printf("Server time zone: %s\n", conn
->timezone
);
24 static int print_error (pgconn_t
*conn
) {
27 if (-1 == (rc
= pg_error(conn
, &e
)))
28 dprintf(STDERR_FILENO
, "%s: %s; CODE: %s\n", e
.text
, e
.msg
, e
.code
);
32 static int exec_sql (pgconn_t
*conn
, const char *sql
, size_t sql_len
) {
33 if (pg_execsql(conn
, sql
, sql_len
)) {
38 if (pg_nflds(conn
) > 0) {
39 int *lens
= malloc(sizeof(int) * pg_nflds(conn
));
40 for (int i
= 0; i
< pg_nflds(conn
); ++i
)
41 lens
[i
] = sizeof(NULL_STR
)-1;
42 for (int i
= 0; i
< pg_nrecs(conn
); ++i
) {
43 for (int j
= 0; j
< pg_nflds(conn
); ++j
) {
44 if (!pg_isnull(conn
, i
, j
)) {
45 strptr_t s
= pg_getstr(conn
, i
, j
);
46 if (lens
[j
] < s
.len
) lens
[j
] = s
.len
;
51 for (int i
= 0; i
< pg_nrecs(conn
); ++i
) {
52 for (int j
= 0; j
< pg_nflds(conn
); ++j
) {
54 if (pg_isnull(conn
, i
, j
))
55 str
= mkstr(CONST_STR_LEN(NULL_STR
), 0);
57 strptr_t s
= pg_getstr(conn
, i
, j
);
58 str
= mkstr(s
.ptr
, s
.len
, 0);
61 strpad(&str
, lens
[j
], ' ', STR_LEFT
);
63 printf("|%s|", str
->ptr
);
65 printf("%s|", str
->ptr
);
73 printf("%s\n", conn
->complete
->tag
);
78 static void usage_show () {
83 "show table <table_name>\n");
86 #define SQL_TABLE "select ordinal_position, column_name, is_nullable, udt_name, character_maximum_length " \
87 "from information_schema.columns " \
88 "where table_name = '%s'"
89 static void show_table (pgconn_t
*conn
, char **str
, size_t *len
, strptr_t
*t
) {
90 if (-1 == strntok(str
, len
, CONST_STR_LEN(STR_SPACES
), t
)) {
91 printf("usage: show table_name\n");
94 char *table_name
= strndup(t
->ptr
, t
->len
);
95 str_t
*sql
= strprintf(SQL_TABLE
, table_name
);
96 exec_sql(conn
, sql
->ptr
, sql
->len
);
101 static void show_server (pgconn_t
*conn
, char **str
, size_t *len
, strptr_t
*t
) {
102 if (-1 == strntok(str
, len
, CONST_STR_LEN(STR_SPACES
), t
)) {
103 printf("usage: show server state\n");
106 print_server_state(conn
);
109 #define SQL_SCHEMAS "select * from pg_catalog.pg_namespace"
110 #define SQL_TABLES "select distinct i.table_schema, i.table_name " \
111 "from information_schema.tables i, pg_catalog.pg_tables p " \
112 "where i.table_schema = p.schemaname " \
113 "and tableowner <> 'postgres' " \
114 "and table_type = 'BASE TABLE'"
115 static void show (pgconn_t
*conn
, char **str
) {
116 size_t len
= strlen(*str
);
117 strptr_t t
= { .ptr
= NULL
, .len
= 0 };
118 strntok(str
, &len
, CONST_STR_LEN(STR_SPACES
), &t
);
119 if (-1 == strntok(str
, &len
, CONST_STR_LEN(STR_SPACES
), &t
)) {
123 if (!cmpstr(t
.ptr
, t
.len
, CONST_STR_LEN("schemas")))
124 exec_sql(conn
, CONST_STR_LEN(SQL_SCHEMAS
));
125 else if (!cmpstr(t
.ptr
, t
.len
, CONST_STR_LEN("tables")))
126 exec_sql(conn
, CONST_STR_LEN(SQL_TABLES
));
127 else if (!cmpstr(t
.ptr
, t
.len
, CONST_STR_LEN("table")))
128 show_table(conn
, str
, &len
, &t
);
129 else if (!cmpstr(t
.ptr
, t
.len
, CONST_STR_LEN("server")))
130 show_server(conn
, str
, &len
, &t
);
135 int main (int argc
, const char *argv
[]) {
136 if (argc
< 2 || argc
> 3) usage(argv
[0]);
137 pgconn_t
*conn
= pg_connect(argv
[1]);
138 if (0 == print_error(conn
) && PG_IDLE
== pg_ready(conn
)) {
140 char *homedir
= getenv("HOME");
143 hist
= strprintf("%s/.pgsql_history", homedir
);
144 linenoiseHistoryLoadFile(hist
->ptr
);
147 exec_sql(conn
, argv
[2], strlen(argv
[2]));
148 else while (!is_quit
) {
151 while ((line
= linenoise(sql
? "# " : "> ", NULL
))) {
153 strnadd(&sql
, line
, strlen(line
));
155 if (!strcmp(line
, "quit") || !strcmp(line
, "exit")) {
158 } else if (!strncmp(line
, "show", 4) && isspace(*(line
+4))) {
162 sql
= mkstr(line
, strlen(line
), 0);
164 char *e
= sql
->ptr
+ sql
->len
- 1;
165 while (e
> sql
->ptr
&& isspace(*e
)) --e
;
170 strnadd(&sql
, CONST_STR_LEN(" "));
172 if (!is_quit
&& sql
) {
173 if (0 == exec_sql(conn
, sql
->ptr
, sql
->len
)) {
174 sql
->ptr
[sql
->len
-1] = ';';
175 linenoiseHistoryAdd(sql
->ptr
);
182 linenoiseHistorySaveFile(hist
->ptr
);