1 /* Copyright (C) 2014 InfiniDB, Inc.
2 
3    This program is free software; you can redistribute it and/or
4    modify it under the terms of the GNU General Public License
5    as published by the Free Software Foundation; version 2 of
6    the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16    MA 02110-1301, USA. */
17 
18 /*
19 * $Id: viewtablelock.cpp 2101 2013-01-21 14:12:52Z rdempsey $
20 */
21 
22 
23 #include <ctime>
24 #include <iostream>
25 #include <vector>
26 #include <stdexcept>
27 #include <sstream>
28 #include <string>
29 
30 #include "calpontsystemcatalog.h"
31 #include "dbrm.h"
32 #include "exceptclasses.h"
33 
34 using namespace execplan;
35 
36 namespace
37 {
38 
39 //------------------------------------------------------------------------------
40 // Print command line usage
41 //------------------------------------------------------------------------------
usage()42 void usage()
43 {
44     std::cout << "Usage: viewtablelock [schemaname tablename]" << std::endl
45               << "  If schema/table are specified, then that table's lock " <<
46               "information is displayed." << std::endl
47               << "  If no schema/table are specified, then all table locks "
48               "are displayed." << std::endl;
49 }
50 
51 //------------------------------------------------------------------------------
52 // Print table locks.
53 // This function closely resembles CommandPackageProcessor::viewTableLock().
54 //------------------------------------------------------------------------------
printTableLocks(boost::shared_ptr<CalpontSystemCatalog> systemCatalogPtr,const std::string & tableNameIn,const std::vector<BRM::TableLockInfo> tableLocks)55 int printTableLocks( boost::shared_ptr<CalpontSystemCatalog> systemCatalogPtr,
56                      const std::string& tableNameIn,
57                      const std::vector<BRM::TableLockInfo> tableLocks )
58 {
59     if (tableLocks.size() == 1)
60         std::cout << " There is " <<  tableLocks.size() <<
61                   " table lock" << std::endl << std::endl;
62     else
63         std::cout << " There are " <<  tableLocks.size() <<
64                   " table locks" << std::endl << std::endl;
65 
66     std::string tableName(tableNameIn);
67 
68     // Make preliminary pass through the table locks in order to determine our
69     // output column widths based on the data.  Min column widths are based on
70     // the width of the column heading (except for the 'state' column).
71     uint64_t maxLockID                = 0;
72     uint32_t maxPID                   = 0;
73     int32_t  maxSessionID             = 0;
74     int32_t  minSessionID             = 0;
75     int32_t  maxTxnID                 = 0;
76 
77     unsigned int tableNameColumnWidth = 5; // "Table"
78     unsigned int lockIDColumnWidth    = 6; // "LockID"
79     unsigned int ownerColumnWidth     = 7; // "Process"
80     unsigned int pidColumnWidth       = 3; // "PID"
81     unsigned int sessionIDColumnWidth = 7; // "Session"
82     unsigned int txnIDColumnWidth     = 3; // "Txn"
83     unsigned int createTimeColumnWidth = 12; // "CreationTime"
84     unsigned int pmColumnWidth        = 7; // "DBRoots"
85     std::vector<std::string> createTimes;
86     char cTimeBuffer[1024];
87     std::vector<std::string> tableNames;
88 
89     std::ostringstream errMsgs;
90 
91     for (unsigned int i = 0; i < tableLocks.size(); i++)
92     {
93         if (tableNameIn.empty())
94         {
95             try
96             {
97                 CalpontSystemCatalog::TableName tableNameStruct;
98                 tableNameStruct = systemCatalogPtr->tableName(
99                                       tableLocks[i].tableOID);
100                 tableName = tableNameStruct.toString();
101             }
102             catch ( logging::IDBExcept&)
103             {
104                 tableName.clear();
105 
106                 errMsgs << "Table with oid " << tableLocks[i].tableOID <<
107                         " (Lock " << tableLocks[i].id << ")" <<
108                         " is not in systable." << std::endl;
109             }
110             catch (std::runtime_error& e)
111             {
112                 tableName.clear();
113 
114                 errMsgs <<
115                         "Error searching for table " << tableLocks[i].tableOID <<
116                         " (Lock " << tableLocks[i].id << ")" <<
117                         " in system catalog. " << e.what() << std::endl;
118             }
119             catch (...)
120             {
121                 tableName.clear();
122 
123                 errMsgs <<
124                         "Unknown error searching for table " <<
125                         tableLocks[i].tableOID <<
126                         " (Lock " << tableLocks[i].id << ")" <<
127                         " in system catalog. " << std::endl;
128             }
129         }
130 
131         tableNames.push_back( tableName );
132 
133         tableNameColumnWidth = std::max(tableNameColumnWidth,
134                                         static_cast<unsigned int>(tableName.length()));
135         maxLockID = std::max(maxLockID, tableLocks[i].id);
136         ownerColumnWidth = std::max(ownerColumnWidth,
137                                     static_cast<unsigned int>(tableLocks[i].ownerName.length()));
138         maxPID = std::max(maxPID, tableLocks[i].ownerPID);
139         maxSessionID = std::max(maxSessionID, tableLocks[i].ownerSessionID);
140         minSessionID = std::min(minSessionID, tableLocks[i].ownerSessionID);
141         maxTxnID = std::max(maxTxnID, tableLocks[i].ownerTxnID);
142 
143         ctime_r( &tableLocks[i].creationTime, cTimeBuffer );
144         cTimeBuffer[ strlen(cTimeBuffer) - 1 ] = '\0'; // strip trailing '\n'
145         std::string cTimeStr( cTimeBuffer );
146         createTimeColumnWidth = std::max(createTimeColumnWidth,
147                                          static_cast<unsigned int>(cTimeStr.length()));
148         createTimes.push_back( cTimeStr );
149 
150         std::ostringstream pms;
151 
152         for (unsigned k = 0; k < tableLocks[i].dbrootList.size(); k++)
153         {
154             if (k > 0)
155                 pms << ',';
156 
157             pms << tableLocks[i].dbrootList[k];
158         }
159 
160         pmColumnWidth = std::max(pmColumnWidth,
161                                  static_cast<unsigned int>(pms.str().length()));
162     }
163 
164     tableNameColumnWidth  += 2;
165     ownerColumnWidth      += 2;
166     pmColumnWidth         += 2;
167     createTimeColumnWidth += 2;
168 
169     std::ostringstream idString;
170     idString << maxLockID;
171     lockIDColumnWidth = std::max(lockIDColumnWidth,
172                                  static_cast<unsigned int>(idString.str().length()));
173     lockIDColumnWidth += 2;
174 
175     std::ostringstream pidString;
176     pidString << maxPID;
177     pidColumnWidth = std::max(pidColumnWidth,
178                               static_cast<unsigned int>(pidString.str().length()));
179     pidColumnWidth += 2;
180 
181     const std::string sessionNoneStr("BulkLoad");
182     std::ostringstream sessionString;
183     sessionString << maxSessionID;
184     sessionIDColumnWidth = std::max(sessionIDColumnWidth,
185                                     static_cast<unsigned int>(sessionString.str().length()));
186 
187     if (minSessionID < 0)
188         sessionIDColumnWidth = std::max(sessionIDColumnWidth,
189                                         static_cast<unsigned int>(sessionNoneStr.length()));
190 
191     sessionIDColumnWidth += 2;
192 
193     const std::string txnNoneStr("n/a");
194     std::ostringstream txnString;
195     txnString << maxTxnID;
196     txnIDColumnWidth = std::max(txnIDColumnWidth,
197                                 static_cast<unsigned int>(txnString.str().length()));
198     txnIDColumnWidth += 2;
199 
200     std::cout.setf(std::ios::left, std::ios::adjustfield);
201     std::cout << "  " <<
202               std::setw(tableNameColumnWidth) << "Table"        <<
203               std::setw(lockIDColumnWidth)    << "LockID"       <<
204               std::setw(ownerColumnWidth)     << "Process"      <<
205               std::setw(pidColumnWidth)       << "PID"          <<
206               std::setw(sessionIDColumnWidth) << "Session"      <<
207               std::setw(txnIDColumnWidth)     << "Txn"          <<
208               std::setw(createTimeColumnWidth) << "CreationTime" <<
209               std::setw(9)                    << "State"        <<
210               std::setw(pmColumnWidth)        << "DBRoots"      << std::endl;
211 
212     // Make second pass through the table locks to display our result.
213     for (unsigned idx = 0; idx < tableLocks.size(); idx++)
214     {
215         std::ostringstream pms; //dbroots now
216 
217         for (unsigned k = 0; k < tableLocks[idx].dbrootList.size(); k++)
218         {
219             if (k > 0)
220                 pms << ',';
221 
222             pms << tableLocks[idx].dbrootList[k];
223         }
224 
225         std::cout << "  " <<
226                   std::setw(tableNameColumnWidth) << tableNames[idx]          <<
227                   std::setw(lockIDColumnWidth)    << tableLocks[idx].id       <<
228                   std::setw(ownerColumnWidth)     << tableLocks[idx].ownerName <<
229                   std::setw(pidColumnWidth)       << tableLocks[idx].ownerPID;
230 
231         // Log session ID, or "BulkLoad" if session is -1
232         if (tableLocks[idx].ownerSessionID < 0)
233             std::cout << std::setw(sessionIDColumnWidth) << sessionNoneStr;
234         else
235             std::cout << std::setw(sessionIDColumnWidth) <<
236                       tableLocks[idx].ownerSessionID;
237 
238         // Log txn ID, or "n/a" if txn is -1
239         if (tableLocks[idx].ownerTxnID < 0)
240             std::cout << std::setw(txnIDColumnWidth) << txnNoneStr;
241         else
242             std::cout << std::setw(txnIDColumnWidth) <<
243                       tableLocks[idx].ownerTxnID;
244 
245         std::cout <<
246                   std::setw(createTimeColumnWidth) <<
247                   createTimes[idx]       <<
248                   std::setw(9) << ((tableLocks[idx].state == BRM::LOADING) ?
249                                    "LOADING" : "CLEANUP") <<
250                   std::setw(pmColumnWidth)        << pms.str() << std::endl;
251     }
252 
253     if (!errMsgs.str().empty())
254         std::cerr << std::endl << errMsgs.str() << std::endl;
255 
256     return 0;
257 }
258 
259 }
260 
261 //------------------------------------------------------------------------------
262 // Main entry point to this program
263 //------------------------------------------------------------------------------
main(int argc,char ** argv)264 int main(int argc, char** argv)
265 {
266     int c;
267 
268     while ((c = getopt(argc, argv, "h")) != EOF)
269     {
270         switch (c)
271         {
272             case 'h':
273             case '?':
274             default:
275                 usage();
276                 return (c == 'h' ? 0 : 1);
277                 break;
278         }
279     }
280 
281     int nargs = argc - optind;
282 
283     if ((nargs > 2) || (nargs == 1))
284     {
285         usage();
286         return 1;
287     }
288 
289     BRM::DBRM dbrm;
290     std::vector<BRM::TableLockInfo> tableLocks;
291 
292     try
293     {
294         tableLocks = dbrm.getAllTableLocks();
295     }
296     catch (std::exception& ex)
297     {
298         std::cerr << "Error getting list of table locks: " << ex.what() <<
299                   std::endl;
300         return 2;
301     }
302 
303     int rc = 0;
304 
305     if (nargs == 2) // List table lock information for a given table
306     {
307         std::string schema(argv[optind++]);
308         std::string table( argv[optind++]);
309 
310         // Get table oid
311         boost::shared_ptr<CalpontSystemCatalog> systemCatalogPtr =
312             CalpontSystemCatalog::makeCalpontSystemCatalog(1);
313         systemCatalogPtr->identity(CalpontSystemCatalog::EC);
314         CalpontSystemCatalog::TableName tableName;
315         tableName.schema = schema;
316         tableName.table = table;
317         CalpontSystemCatalog::ROPair roPair;
318 
319         try
320         {
321             roPair = systemCatalogPtr->tableRID( tableName );
322         }
323         catch (logging::IDBExcept& e)
324         {
325             std::cerr << e.what() << std::endl;
326             return 3;
327         }
328         catch (std::runtime_error& e)
329         {
330             std::cerr << "Error searching for table in system catalog. " <<
331                       e.what() << std::endl;
332             return 4;
333         }
334         catch (...)
335         {
336             std::cerr << "Unknown error searching for table in system catalog."
337                       << std::endl;
338             return 5;
339         }
340 
341         // Keep in mind the same table could have more than 1 lock
342         // (on different PMs), so we don't exit loop after "first" match.
343         std::vector<BRM::TableLockInfo> matchingTableLocks;
344 
345         for (unsigned int i = 0; i < tableLocks.size(); i++)
346         {
347             if (roPair.objnum == (CalpontSystemCatalog::OID)
348                     tableLocks[i].tableOID)
349             {
350                 matchingTableLocks.push_back( tableLocks[i] );
351             }
352         }
353 
354         if (matchingTableLocks.size() > 0)
355         {
356             std::string tableName(schema);
357             tableName += '.';
358             tableName += table;
359             rc = printTableLocks( systemCatalogPtr,
360                                   tableName, matchingTableLocks );
361         }
362         else
363         {
364             std::cout << " Table " << schema << "." << table <<
365                       " is not locked by any process. " << std::endl;
366         }
367     }
368     else // List table lock information for all table locks
369     {
370         //All table lock info required
371         if (tableLocks.size() == 0)
372         {
373             std::cout << " No tables are locked in the database." << std::endl;
374         }
375         else
376         {
377             boost::shared_ptr<CalpontSystemCatalog> systemCatalogPtr =
378                 CalpontSystemCatalog::makeCalpontSystemCatalog();
379             systemCatalogPtr->identity(CalpontSystemCatalog::EC);
380 
381             std::string tableName;
382             rc = printTableLocks( systemCatalogPtr,
383                                   tableName, tableLocks );
384         }
385     }
386 
387     return rc;
388 }
389