29
29
#include "fe_utils/mbprint.h"
30
30
31
31
32
+ static bool DescribeQuery (const char * query , double * elapsed_msec );
32
33
static bool ExecQueryUsingCursor (const char * query , double * elapsed_msec );
33
34
static bool command_no_begin (const char * query );
34
35
static bool is_select_command (const char * query );
@@ -1323,8 +1324,15 @@ SendQuery(const char *query)
1323
1324
}
1324
1325
}
1325
1326
1326
- if (pset .fetch_count <= 0 || pset .gexec_flag ||
1327
- pset .crosstab_flag || !is_select_command (query ))
1327
+ if (pset .gdesc_flag )
1328
+ {
1329
+ /* Describe query's result columns, without executing it */
1330
+ OK = DescribeQuery (query , & elapsed_msec );
1331
+ ResetCancelConn ();
1332
+ results = NULL ; /* PQclear(NULL) does nothing */
1333
+ }
1334
+ else if (pset .fetch_count <= 0 || pset .gexec_flag ||
1335
+ pset .crosstab_flag || !is_select_command (query ))
1328
1336
{
1329
1337
/* Default fetch-it-all-and-print mode */
1330
1338
instr_time before ,
@@ -1467,6 +1475,9 @@ SendQuery(const char *query)
1467
1475
pset .gset_prefix = NULL ;
1468
1476
}
1469
1477
1478
+ /* reset \gdesc trigger */
1479
+ pset .gdesc_flag = false;
1480
+
1470
1481
/* reset \gexec trigger */
1471
1482
pset .gexec_flag = false;
1472
1483
@@ -1482,6 +1493,118 @@ SendQuery(const char *query)
1482
1493
}
1483
1494
1484
1495
1496
+ /*
1497
+ * DescribeQuery: describe the result columns of a query, without executing it
1498
+ *
1499
+ * Returns true if the operation executed successfully, false otherwise.
1500
+ *
1501
+ * If pset.timing is on, total query time (exclusive of result-printing) is
1502
+ * stored into *elapsed_msec.
1503
+ */
1504
+ static bool
1505
+ DescribeQuery (const char * query , double * elapsed_msec )
1506
+ {
1507
+ PGresult * results ;
1508
+ bool OK ;
1509
+ instr_time before ,
1510
+ after ;
1511
+
1512
+ * elapsed_msec = 0 ;
1513
+
1514
+ if (pset .timing )
1515
+ INSTR_TIME_SET_CURRENT (before );
1516
+
1517
+ /*
1518
+ * To parse the query but not execute it, we prepare it, using the unnamed
1519
+ * prepared statement. This is invisible to psql users, since there's no
1520
+ * way to access the unnamed prepared statement from psql user space. The
1521
+ * next Parse or Query protocol message would overwrite the statement
1522
+ * anyway. (So there's no great need to clear it when done, which is a
1523
+ * good thing because libpq provides no easy way to do that.)
1524
+ */
1525
+ results = PQprepare (pset .db , "" , query , 0 , NULL );
1526
+ if (PQresultStatus (results ) != PGRES_COMMAND_OK )
1527
+ {
1528
+ psql_error ("%s" , PQerrorMessage (pset .db ));
1529
+ ClearOrSaveResult (results );
1530
+ return false;
1531
+ }
1532
+ PQclear (results );
1533
+
1534
+ results = PQdescribePrepared (pset .db , "" );
1535
+ OK = AcceptResult (results ) &&
1536
+ (PQresultStatus (results ) == PGRES_COMMAND_OK );
1537
+ if (OK && results )
1538
+ {
1539
+ if (PQnfields (results ) > 0 )
1540
+ {
1541
+ PQExpBufferData buf ;
1542
+ int i ;
1543
+
1544
+ initPQExpBuffer (& buf );
1545
+
1546
+ printfPQExpBuffer (& buf ,
1547
+ "SELECT name AS \"%s\", pg_catalog.format_type(tp, tpm) AS \"%s\"\n"
1548
+ "FROM (VALUES " ,
1549
+ gettext_noop ("Column" ),
1550
+ gettext_noop ("Type" ));
1551
+
1552
+ for (i = 0 ; i < PQnfields (results ); i ++ )
1553
+ {
1554
+ const char * name ;
1555
+ char * escname ;
1556
+
1557
+ if (i > 0 )
1558
+ appendPQExpBufferStr (& buf , "," );
1559
+
1560
+ name = PQfname (results , i );
1561
+ escname = PQescapeLiteral (pset .db , name , strlen (name ));
1562
+
1563
+ if (escname == NULL )
1564
+ {
1565
+ psql_error ("%s" , PQerrorMessage (pset .db ));
1566
+ PQclear (results );
1567
+ termPQExpBuffer (& buf );
1568
+ return false;
1569
+ }
1570
+
1571
+ appendPQExpBuffer (& buf , "(%s, '%u'::pg_catalog.oid, %d)" ,
1572
+ escname ,
1573
+ PQftype (results , i ),
1574
+ PQfmod (results , i ));
1575
+
1576
+ PQfreemem (escname );
1577
+ }
1578
+
1579
+ appendPQExpBufferStr (& buf , ") s(name, tp, tpm)" );
1580
+ PQclear (results );
1581
+
1582
+ results = PQexec (pset .db , buf .data );
1583
+ OK = AcceptResult (results );
1584
+
1585
+ if (pset .timing )
1586
+ {
1587
+ INSTR_TIME_SET_CURRENT (after );
1588
+ INSTR_TIME_SUBTRACT (after , before );
1589
+ * elapsed_msec += INSTR_TIME_GET_MILLISEC (after );
1590
+ }
1591
+
1592
+ if (OK && results )
1593
+ OK = PrintQueryResults (results );
1594
+
1595
+ termPQExpBuffer (& buf );
1596
+ }
1597
+ else
1598
+ fprintf (pset .queryFout ,
1599
+ _ ("The command has no result, or the result has no columns.\n" ));
1600
+ }
1601
+
1602
+ ClearOrSaveResult (results );
1603
+
1604
+ return OK ;
1605
+ }
1606
+
1607
+
1485
1608
/*
1486
1609
* ExecQueryUsingCursor: run a SELECT-like query using a cursor
1487
1610
*
@@ -1627,7 +1750,9 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec)
1627
1750
break ;
1628
1751
}
1629
1752
1630
- /* Note we do not deal with \gexec or \crosstabview modes here */
1753
+ /*
1754
+ * Note we do not deal with \gdesc, \gexec or \crosstabview modes here
1755
+ */
1631
1756
1632
1757
ntuples = PQntuples (results );
1633
1758
0 commit comments