@@ -172,6 +172,7 @@ typedef struct repack_index
172
172
} repack_index ;
173
173
174
174
static bool is_superuser (void );
175
+ static void check_tablespace (void );
175
176
static void repack_all_databases (const char * order_by );
176
177
static bool repack_one_database (const char * order_by , char * errbuf , size_t errsize );
177
178
static void repack_one_table (const repack_table * table , const char * order_by );
@@ -197,6 +198,8 @@ static bool alldb = false;
197
198
static bool noorder = false;
198
199
static SimpleStringList table_list = {NULL , NULL };
199
200
static char * orderby = NULL ;
201
+ static char * tablespace = NULL ;
202
+ static bool moveidx = false;
200
203
static int wait_timeout = 60 ; /* in seconds */
201
204
static int jobs = 0 ; /* number of concurrent worker conns. */
202
205
@@ -214,6 +217,8 @@ static pgut_option options[] =
214
217
{ 'l' , 't' , "table" , & table_list },
215
218
{ 'b' , 'n' , "no-order" , & noorder },
216
219
{ 's' , 'o' , "order-by" , & orderby },
220
+ { 's' , 's' , "tablespace" , & tablespace },
221
+ { 'b' , 'S' , "moveidx" , & moveidx },
217
222
{ 'i' , 'T' , "wait-timeout" , & wait_timeout },
218
223
{ 'B' , 'Z' , "no-analyze" , & analyze },
219
224
{ 'i' , 'j' , "jobs" , & jobs },
@@ -234,6 +239,8 @@ main(int argc, char *argv[])
234
239
(errcode (EINVAL ),
235
240
errmsg ("too many arguments" )));
236
241
242
+ check_tablespace ();
243
+
237
244
if (noorder )
238
245
orderby = "" ;
239
246
@@ -281,6 +288,56 @@ is_superuser(void)
281
288
return false;
282
289
}
283
290
291
+ /*
292
+ * Check if the tablespace requested exists.
293
+ *
294
+ * Raise an exception on error.
295
+ */
296
+ void
297
+ check_tablespace ()
298
+ {
299
+ PGresult * res = NULL ;
300
+ const char * params [1 ];
301
+
302
+ if (tablespace == NULL )
303
+ {
304
+ /* nothing to check, but let's see the options */
305
+ if (moveidx )
306
+ {
307
+ ereport (ERROR ,
308
+ (errcode (EINVAL ),
309
+ errmsg ("cannot specify --moveidx (-S) without --tablespace (-s)" )));
310
+ }
311
+ return ;
312
+ }
313
+
314
+ /* check if the tablespace exists */
315
+ reconnect (ERROR );
316
+ params [0 ] = tablespace ;
317
+ res = execute_elevel (
318
+ "select spcname from pg_tablespace where spcname = $1" ,
319
+ 1 , params , DEBUG2 );
320
+
321
+ if (PQresultStatus (res ) == PGRES_TUPLES_OK )
322
+ {
323
+ if (PQntuples (res ) == 0 )
324
+ {
325
+ ereport (ERROR ,
326
+ (errcode (EINVAL ),
327
+ errmsg ("the tablespace \"%s\" doesn't exist" , tablespace )));
328
+ }
329
+ }
330
+ else
331
+ {
332
+ ereport (ERROR ,
333
+ (errcode (EINVAL ),
334
+ errmsg ("error checking the namespace: %s" ,
335
+ PQerrorMessage (connection ))));
336
+ }
337
+
338
+ CLEARPGRES (res );
339
+ }
340
+
284
341
285
342
/*
286
343
* Call repack_one_database for each database.
@@ -360,10 +417,15 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
360
417
StringInfoData sql ;
361
418
SimpleStringListCell * cell ;
362
419
const char * * params = NULL ;
363
- size_t num_params = simple_string_list_size (table_list );
420
+ int iparam = 0 ;
421
+ size_t num_tables ;
422
+ size_t num_params ;
423
+
424
+ num_tables = simple_string_list_size (table_list );
364
425
365
- if (num_params )
366
- params = pgut_malloc (num_params * sizeof (char * ));
426
+ /* 1st param is the user-specified tablespace */
427
+ num_params = num_tables + 1 ;
428
+ params = pgut_malloc (num_params * sizeof (char * ));
367
429
368
430
initStringInfo (& sql );
369
431
@@ -442,29 +504,46 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
442
504
command ("SET client_min_messages = warning" , 0 , NULL );
443
505
444
506
/* acquire target tables */
445
- appendStringInfoString (& sql , "SELECT * FROM repack.tables WHERE " );
446
- if (num_params )
507
+ appendStringInfoString (& sql ,
508
+ "SELECT t.*,"
509
+ " coalesce(v.tablespace, t.tablespace_orig) as tablespace_dest"
510
+ " FROM repack.tables t, "
511
+ " (VALUES (quote_ident($1::text))) as v (tablespace)"
512
+ " WHERE " );
513
+
514
+ params [iparam ++ ] = tablespace ;
515
+ if (num_tables )
447
516
{
448
517
appendStringInfoString (& sql , "(" );
449
- for (i = 0 , cell = table_list .head ; cell ; cell = cell -> next , i ++ )
518
+ for (cell = table_list .head ; cell ; cell = cell -> next )
450
519
{
451
520
/* Construct table name placeholders to be used by PQexecParams */
452
- appendStringInfo (& sql , "relid = $%d::regclass" , i + 1 );
453
- params [i ] = cell -> val ;
521
+ appendStringInfo (& sql , "relid = $%d::regclass" , iparam + 1 );
522
+ params [iparam ++ ] = cell -> val ;
454
523
if (cell -> next )
455
524
appendStringInfoString (& sql , " OR " );
456
525
}
457
526
appendStringInfoString (& sql , ")" );
458
- res = execute_elevel (sql .data , (int ) num_params , params , DEBUG2 );
459
527
}
460
528
else
461
529
{
462
530
appendStringInfoString (& sql , "pkid IS NOT NULL" );
463
531
if (!orderby )
464
532
appendStringInfoString (& sql , " AND ckid IS NOT NULL" );
465
- res = execute_elevel (sql .data , 0 , NULL , DEBUG2 );
466
533
}
467
534
535
+ /* double check the parameters array is sane */
536
+ if (iparam != num_params )
537
+ {
538
+ if (errbuf )
539
+ snprintf (errbuf , errsize ,
540
+ "internal error: bad parameters count: %i instead of %zi" ,
541
+ iparam , num_params );
542
+ goto cleanup ;
543
+ }
544
+
545
+ res = execute_elevel (sql .data , (int ) num_params , params , DEBUG2 );
546
+
468
547
/* on error skip the database */
469
548
if (PQresultStatus (res ) != PGRES_TUPLES_OK )
470
549
{
@@ -489,7 +568,9 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
489
568
for (i = 0 ; i < num ; i ++ )
490
569
{
491
570
repack_table table ;
492
- const char * create_table ;
571
+ const char * create_table_1 ;
572
+ const char * create_table_2 ;
573
+ const char * tablespace ;
493
574
const char * ckey ;
494
575
int c = 0 ;
495
576
@@ -512,45 +593,51 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
512
593
table .create_trigger = getstr (res , i , c ++ );
513
594
table .enable_trigger = getstr (res , i , c ++ );
514
595
515
- create_table = getstr (res , i , c ++ );
596
+ create_table_1 = getstr (res , i , c ++ );
597
+ tablespace = getstr (res , i , c ++ ); /* to be clobbered */
598
+ create_table_2 = getstr (res , i , c ++ );
516
599
table .drop_columns = getstr (res , i , c ++ );
517
600
table .delete_log = getstr (res , i , c ++ );
518
601
table .lock_table = getstr (res , i , c ++ );
519
602
ckey = getstr (res , i , c ++ );
603
+ table .sql_peek = getstr (res , i , c ++ );
604
+ table .sql_insert = getstr (res , i , c ++ );
605
+ table .sql_delete = getstr (res , i , c ++ );
606
+ table .sql_update = getstr (res , i , c ++ );
607
+ table .sql_pop = getstr (res , i , c ++ );
608
+ tablespace = getstr (res , i , c ++ );
520
609
521
610
resetStringInfo (& sql );
611
+ appendStringInfoString (& sql , create_table_1 );
612
+ appendStringInfoString (& sql , tablespace );
613
+ appendStringInfoString (& sql , create_table_2 );
522
614
if (!orderby )
523
615
{
524
616
if (ckey != NULL )
525
617
{
526
618
/* CLUSTER mode */
527
- appendStringInfo (& sql , "%s ORDER BY %s" , create_table , ckey );
619
+ appendStringInfoString (& sql , " ORDER BY " );
620
+ appendStringInfoString (& sql , ckey );
528
621
table .create_table = sql .data ;
529
622
}
530
623
else
531
624
{
532
625
/* VACUUM FULL mode (non-clustered tables) */
533
- table .create_table = create_table ;
626
+ table .create_table = sql . data ;
534
627
}
535
628
}
536
629
else if (!orderby [0 ])
537
630
{
538
631
/* VACUUM FULL mode (for clustered tables too) */
539
- table .create_table = create_table ;
632
+ table .create_table = sql . data ;
540
633
}
541
634
else
542
635
{
543
636
/* User specified ORDER BY */
544
- appendStringInfo (& sql , "%s ORDER BY %s" , create_table , orderby );
545
- table . create_table = sql . data ;
637
+ appendStringInfoString (& sql , " ORDER BY " );
638
+ appendStringInfoString ( & sql , orderby ) ;
546
639
}
547
640
548
- table .sql_peek = getstr (res , i , c ++ );
549
- table .sql_insert = getstr (res , i , c ++ );
550
- table .sql_delete = getstr (res , i , c ++ );
551
- table .sql_update = getstr (res , i , c ++ );
552
- table .sql_pop = getstr (res , i , c ++ );
553
-
554
641
repack_one_table (& table , orderby );
555
642
}
556
643
ret = true;
@@ -594,7 +681,7 @@ static bool
594
681
rebuild_indexes (const repack_table * table )
595
682
{
596
683
PGresult * res ;
597
- const char * params [1 ];
684
+ const char * params [2 ];
598
685
int num_indexes ;
599
686
int i ;
600
687
int num_active_workers ;
@@ -606,6 +693,7 @@ rebuild_indexes(const repack_table *table)
606
693
elog (DEBUG2 , "---- create indexes ----" );
607
694
608
695
params [0 ] = utoa (table -> target_oid , buffer );
696
+ params [1 ] = moveidx ? tablespace : NULL ;
609
697
610
698
/* First, just display a warning message for any invalid indexes
611
699
* which may be on the table (mostly to match the behavior of 1.1.8).
@@ -621,8 +709,9 @@ rebuild_indexes(const repack_table *table)
621
709
}
622
710
623
711
res = execute ("SELECT indexrelid,"
624
- " repack.repack_indexdef(indexrelid, indrelid) "
625
- " FROM pg_index WHERE indrelid = $1 AND indisvalid" , 1 , params );
712
+ " repack.repack_indexdef(indexrelid, indrelid, $2) "
713
+ " FROM pg_index WHERE indrelid = $1 AND indisvalid" ,
714
+ 2 , params );
626
715
627
716
num_indexes = PQntuples (res );
628
717
@@ -1453,6 +1542,8 @@ pgut_help(bool details)
1453
1542
printf ("Options:\n" );
1454
1543
printf (" -a, --all repack all databases\n" );
1455
1544
printf (" -t, --table=TABLE repack specific table only\n" );
1545
+ printf (" -s, --tablespace=TABLESPC move repacked tables to a new tablespace\n" );
1546
+ printf (" -S, --moveidx move repacked indexes to TABLESPC too\n" );
1456
1547
printf (" -o, --order-by=COLUMNS order by columns instead of cluster keys\n" );
1457
1548
printf (" -n, --no-order do vacuum full instead of cluster\n" );
1458
1549
printf (" -j --jobs Use this many parallel jobs for each table\n" );
0 commit comments