YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/postgres/src/backend/commands/createas.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * createas.c
4
 *    Execution of CREATE TABLE ... AS, a/k/a SELECT INTO.
5
 *    Since CREATE MATERIALIZED VIEW shares syntax and most behaviors,
6
 *    we implement that here, too.
7
 *
8
 * We implement this by diverting the query's normal output to a
9
 * specialized DestReceiver type.
10
 *
11
 * Formerly, CTAS was implemented as a variant of SELECT, which led
12
 * to assorted legacy behaviors that we still try to preserve, notably that
13
 * we must return a tuples-processed count in the completionTag.  (We no
14
 * longer do that for CTAS ... WITH NO DATA, however.)
15
 *
16
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
17
 * Portions Copyright (c) 1994, Regents of the University of California
18
 *
19
 *
20
 * IDENTIFICATION
21
 *    src/backend/commands/createas.c
22
 *
23
 *-------------------------------------------------------------------------
24
 */
25
#include "postgres.h"
26
27
#include "access/reloptions.h"
28
#include "access/htup_details.h"
29
#include "access/sysattr.h"
30
#include "access/xact.h"
31
#include "access/xlog.h"
32
#include "catalog/namespace.h"
33
#include "catalog/toasting.h"
34
#include "commands/createas.h"
35
#include "commands/matview.h"
36
#include "commands/prepare.h"
37
#include "commands/tablecmds.h"
38
#include "commands/view.h"
39
#include "miscadmin.h"
40
#include "nodes/makefuncs.h"
41
#include "nodes/nodeFuncs.h"
42
#include "parser/parse_clause.h"
43
#include "rewrite/rewriteHandler.h"
44
#include "storage/smgr.h"
45
#include "tcop/tcopprot.h"
46
#include "utils/builtins.h"
47
#include "utils/lsyscache.h"
48
#include "utils/rel.h"
49
#include "utils/rls.h"
50
#include "utils/snapmgr.h"
51
52
/*  YB includes. */
53
#include "executor/ybcModifyTable.h"
54
#include "pg_yb_utils.h"
55
56
typedef struct
57
{
58
  DestReceiver pub;     /* publicly-known function pointers */
59
  IntoClause *into;     /* target relation specification */
60
  /* These fields are filled by intorel_startup: */
61
  Relation  rel;      /* relation to write to */
62
  ObjectAddress reladdr;    /* address of rel, for ExecCreateTableAs */
63
  CommandId output_cid;   /* cmin to insert in output tuples */
64
  int     hi_options;   /* heap_insert performance options */
65
  BulkInsertState bistate;  /* bulk insert state */
66
} DR_intorel;
67
68
/* utility functions for CTAS definition creation */
69
static ObjectAddress create_ctas_internal(List *attrList, IntoClause *into);
70
static ObjectAddress create_ctas_nodata(List *tlist, IntoClause *into);
71
72
/* DestReceiver routines for collecting data */
73
static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
74
static bool intorel_receive(TupleTableSlot *slot, DestReceiver *self);
75
static void intorel_shutdown(DestReceiver *self);
76
static void intorel_destroy(DestReceiver *self);
77
78
79
/*
80
 * create_ctas_internal
81
 *
82
 * Internal utility used for the creation of the definition of a relation
83
 * created via CREATE TABLE AS or a materialized view.  Caller needs to
84
 * provide a list of attributes (ColumnDef nodes).
85
 */
86
static ObjectAddress
87
create_ctas_internal(List *attrList, IntoClause *into)
88
28
{
89
28
  CreateStmt *create = makeNode(CreateStmt);
90
28
  bool    is_matview;
91
28
  char    relkind;
92
28
  Datum   toast_options;
93
28
  static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
94
28
  ObjectAddress intoRelationAddr;
95
96
  /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
97
28
  is_matview = (into->viewQuery != NULL);
98
28
  relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION;
99
100
  /*
101
   * Create the target relation by faking up a CREATE TABLE parsetree and
102
   * passing it to DefineRelation.
103
   */
104
28
  create->relation = into->rel;
105
28
  create->tableElts = attrList;
106
28
  create->inhRelations = NIL;
107
28
  create->ofTypename = NULL;
108
28
  create->constraints = NIL;
109
28
  create->options = into->options;
110
28
  create->oncommit = into->onCommit;
111
28
  create->tablespacename = into->tableSpaceName;
112
28
  create->if_not_exists = false;
113
114
  /*
115
   * Create the relation.  (This will error out if there's an existing view,
116
   * so we don't need more code to complain if "replace" is false.)
117
   */
118
28
  intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
119
120
  /* TOAST tables are not needed in YugaByte database */
121
28
  if (!IsYugaByteEnabled())
122
0
  {
123
    /*
124
     * If necessary, create a TOAST table for the target table.  Note that
125
     * NewRelationCreateToastTable ends with CommandCounterIncrement(), so
126
     * that the TOAST table will be visible for insertion.
127
     */
128
0
    CommandCounterIncrement();
129
130
    /* parse and validate reloptions for the toast table */
131
0
    toast_options = transformRelOptions((Datum) 0,
132
0
                                        create->options,
133
0
                                        "toast",
134
0
                                        validnsps,
135
0
                                        true,
136
0
                                        false);
137
138
0
    (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
139
140
0
    NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
141
0
  }
142
143
  /* Create the "view" part of a materialized view. */
144
28
  if (is_matview)
145
0
  {
146
    /* StoreViewQuery scribbles on tree, so make a copy */
147
0
    Query    *query = (Query *) copyObject(into->viewQuery);
148
149
0
    StoreViewQuery(intoRelationAddr.objectId, query, false);
150
0
    CommandCounterIncrement();
151
0
  }
152
153
28
  return intoRelationAddr;
154
28
}
155
156
157
/*
158
 * create_ctas_nodata
159
 *
160
 * Create CTAS or materialized view when WITH NO DATA is used, starting from
161
 * the targetlist of the SELECT or view definition.
162
 */
163
static ObjectAddress
164
create_ctas_nodata(List *tlist, IntoClause *into)
165
0
{
166
0
  List     *attrList;
167
0
  ListCell   *t,
168
0
         *lc;
169
170
  /*
171
   * Build list of ColumnDefs from non-junk elements of the tlist.  If a
172
   * column name list was specified in CREATE TABLE AS, override the column
173
   * names in the query.  (Too few column names are OK, too many are not.)
174
   */
175
0
  attrList = NIL;
176
0
  lc = list_head(into->colNames);
177
0
  foreach(t, tlist)
178
0
  {
179
0
    TargetEntry *tle = (TargetEntry *) lfirst(t);
180
181
0
    if (!tle->resjunk)
182
0
    {
183
0
      ColumnDef  *col;
184
0
      char     *colname;
185
186
0
      if (lc)
187
0
      {
188
0
        colname = strVal(lfirst(lc));
189
0
        lc = lnext(lc);
190
0
      }
191
0
      else
192
0
        colname = tle->resname;
193
194
0
      col = makeColumnDef(colname,
195
0
                exprType((Node *) tle->expr),
196
0
                exprTypmod((Node *) tle->expr),
197
0
                exprCollation((Node *) tle->expr));
198
199
      /*
200
       * It's possible that the column is of a collatable type but the
201
       * collation could not be resolved, so double-check.  (We must
202
       * check this here because DefineRelation would adopt the type's
203
       * default collation rather than complaining.)
204
       */
205
0
      if (!OidIsValid(col->collOid) &&
206
0
        type_is_collatable(col->typeName->typeOid))
207
0
        ereport(ERROR,
208
0
            (errcode(ERRCODE_INDETERMINATE_COLLATION),
209
0
             errmsg("no collation was derived for column \"%s\" with collatable type %s",
210
0
                col->colname,
211
0
                format_type_be(col->typeName->typeOid)),
212
0
             errhint("Use the COLLATE clause to set the collation explicitly.")));
213
214
0
      attrList = lappend(attrList, col);
215
0
    }
216
0
  }
217
218
0
  if (lc != NULL)
219
0
    ereport(ERROR,
220
0
        (errcode(ERRCODE_SYNTAX_ERROR),
221
0
         errmsg("too many column names were specified")));
222
223
  /* Create the relation definition using the ColumnDef list */
224
0
  return create_ctas_internal(attrList, into);
225
0
}
226
227
228
/*
229
 * ExecCreateTableAs -- execute a CREATE TABLE AS command
230
 */
231
ObjectAddress
232
ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
233
          ParamListInfo params, QueryEnvironment *queryEnv,
234
          char *completionTag)
235
28
{
236
28
  Query    *query = castNode(Query, stmt->query);
237
28
  IntoClause *into = stmt->into;
238
28
  bool    is_matview = (into->viewQuery != NULL);
239
28
  DestReceiver *dest;
240
28
  Oid     save_userid = InvalidOid;
241
28
  int     save_sec_context = 0;
242
28
  int     save_nestlevel = 0;
243
28
  ObjectAddress address;
244
28
  List     *rewritten;
245
28
  PlannedStmt *plan;
246
28
  QueryDesc  *queryDesc;
247
248
28
  if (stmt->if_not_exists)
249
0
  {
250
0
    Oid     nspid;
251
252
0
    nspid = RangeVarGetCreationNamespace(stmt->into->rel);
253
254
0
    if (get_relname_relid(stmt->into->rel->relname, nspid))
255
0
    {
256
0
      ereport(NOTICE,
257
0
          (errcode(ERRCODE_DUPLICATE_TABLE),
258
0
           errmsg("relation \"%s\" already exists, skipping",
259
0
              stmt->into->rel->relname)));
260
0
      return InvalidObjectAddress;
261
28
    }
262
0
  }
263
264
  /*
265
   * Create the tuple receiver object and insert info it will need
266
   */
267
28
  dest = CreateIntoRelDestReceiver(into);
268
269
  /*
270
   * The contained Query could be a SELECT, or an EXECUTE utility command.
271
   * If the latter, we just pass it off to ExecuteQuery.
272
   */
273
28
  if (query->commandType == CMD_UTILITY &&
274
0
    IsA(query->utilityStmt, ExecuteStmt))
275
0
  {
276
0
    ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
277
278
0
    Assert(!is_matview);  /* excluded by syntax */
279
0
    ExecuteQuery(estmt, into, queryString, params, dest, completionTag);
280
281
    /* get object address that intorel_startup saved for us */
282
0
    address = ((DR_intorel *) dest)->reladdr;
283
284
0
    return address;
285
28
  }
286
28
  Assert(query->commandType == CMD_SELECT);
287
288
  /*
289
   * For materialized views, lock down security-restricted operations and
290
   * arrange to make GUC variable changes local to this command.  This is
291
   * not necessary for security, but this keeps the behavior similar to
292
   * REFRESH MATERIALIZED VIEW.  Otherwise, one could create a materialized
293
   * view not possible to refresh.
294
   */
295
28
  if (is_matview)
296
0
  {
297
0
    GetUserIdAndSecContext(&save_userid, &save_sec_context);
298
0
    SetUserIdAndSecContext(save_userid,
299
0
                 save_sec_context | SECURITY_RESTRICTED_OPERATION);
300
0
    save_nestlevel = NewGUCNestLevel();
301
0
  }
302
303
28
  if (into->skipData)
304
0
  {
305
    /*
306
     * If WITH NO DATA was specified, do not go through the rewriter,
307
     * planner and executor.  Just define the relation using a code path
308
     * similar to CREATE VIEW.  This avoids dump/restore problems stemming
309
     * from running the planner before all dependencies are set up.
310
     */
311
0
    address = create_ctas_nodata(query->targetList, into);
312
0
  }
313
28
  else
314
28
  {
315
    /*
316
     * Parse analysis was done already, but we still have to run the rule
317
     * rewriter.  We do not do AcquireRewriteLocks: we assume the query
318
     * either came straight from the parser, or suitable locks were
319
     * acquired by plancache.c.
320
     *
321
     * Because the rewriter and planner tend to scribble on the input, we
322
     * make a preliminary copy of the source querytree.  This prevents
323
     * problems in the case that CTAS is in a portal or plpgsql function
324
     * and is executed repeatedly.  (See also the same hack in EXPLAIN and
325
     * PREPARE.)
326
     */
327
28
    rewritten = QueryRewrite(copyObject(query));
328
329
    /* SELECT should never rewrite to more or less than one SELECT query */
330
28
    if (list_length(rewritten) != 1)
331
0
      elog(ERROR, "unexpected rewrite result for %s",
332
28
         is_matview ? "CREATE MATERIALIZED VIEW" :
333
28
         "CREATE TABLE AS SELECT");
334
28
    query = linitial_node(Query, rewritten);
335
28
    Assert(query->commandType == CMD_SELECT);
336
337
    /* plan the query */
338
28
    plan = pg_plan_query(query, CURSOR_OPT_PARALLEL_OK, params);
339
340
    /*
341
     * Use a snapshot with an updated command ID to ensure this query sees
342
     * results of any previously executed queries.  (This could only
343
     * matter if the planner executed an allegedly-stable function that
344
     * changed the database contents, but let's do it anyway to be
345
     * parallel to the EXPLAIN code path.)
346
     */
347
28
    PushCopiedSnapshot(GetActiveSnapshot());
348
28
    UpdateActiveSnapshotCommandId();
349
350
    /* Create a QueryDesc, redirecting output to our tuple receiver */
351
28
    queryDesc = CreateQueryDesc(plan, queryString,
352
28
                  GetActiveSnapshot(), InvalidSnapshot,
353
28
                  dest, params, queryEnv, 0);
354
355
    /* call ExecutorStart to prepare the plan for execution */
356
28
    ExecutorStart(queryDesc, GetIntoRelEFlags(into));
357
358
    /* run the plan to completion */
359
28
    ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
360
361
    /* save the rowcount if we're given a completionTag to fill */
362
28
    if (completionTag)
363
28
      snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
364
28
           "SELECT " UINT64_FORMAT,
365
28
           queryDesc->estate->es_processed);
366
367
    /* get object address that intorel_startup saved for us */
368
28
    address = ((DR_intorel *) dest)->reladdr;
369
370
    /* and clean up */
371
28
    ExecutorFinish(queryDesc);
372
28
    ExecutorEnd(queryDesc);
373
374
28
    FreeQueryDesc(queryDesc);
375
376
28
    PopActiveSnapshot();
377
28
  }
378
379
28
  if (is_matview)
380
0
  {
381
    /* Roll back any GUC changes */
382
0
    AtEOXact_GUC(false, save_nestlevel);
383
384
    /* Restore userid and security context */
385
0
    SetUserIdAndSecContext(save_userid, save_sec_context);
386
0
  }
387
388
28
  return address;
389
28
}
390
391
/*
392
 * GetIntoRelEFlags --- compute executor flags needed for CREATE TABLE AS
393
 *
394
 * This is exported because EXPLAIN and PREPARE need it too.  (Note: those
395
 * callers still need to deal explicitly with the skipData flag; since they
396
 * use different methods for suppressing execution, it doesn't seem worth
397
 * trying to encapsulate that part.)
398
 */
399
int
400
GetIntoRelEFlags(IntoClause *intoClause)
401
28
{
402
28
  int     flags;
403
404
  /*
405
   * We need to tell the executor whether it has to produce OIDs or not,
406
   * because it doesn't have enough information to do so itself (since we
407
   * can't build the target relation until after ExecutorStart).
408
   *
409
   * Disallow the OIDS option for materialized views.
410
   */
411
28
  if (interpretOidsOption(intoClause->options,
412
28
              (intoClause->viewQuery == NULL)))
413
0
    flags = EXEC_FLAG_WITH_OIDS;
414
28
  else
415
28
    flags = EXEC_FLAG_WITHOUT_OIDS;
416
417
28
  if (intoClause->skipData)
418
0
    flags |= EXEC_FLAG_WITH_NO_DATA;
419
420
28
  return flags;
421
28
}
422
423
/*
424
 * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
425
 *
426
 * intoClause will be NULL if called from CreateDestReceiver(), in which
427
 * case it has to be provided later.  However, it is convenient to allow
428
 * self->into to be filled in immediately for other callers.
429
 */
430
DestReceiver *
431
CreateIntoRelDestReceiver(IntoClause *intoClause)
432
28
{
433
28
  DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
434
435
28
  self->pub.receiveSlot = intorel_receive;
436
28
  self->pub.rStartup = intorel_startup;
437
28
  self->pub.rShutdown = intorel_shutdown;
438
28
  self->pub.rDestroy = intorel_destroy;
439
28
  self->pub.mydest = DestIntoRel;
440
28
  self->into = intoClause;
441
  /* other private fields will be set during intorel_startup */
442
443
28
  return (DestReceiver *) self;
444
28
}
445
446
/*
447
 * intorel_startup --- executor startup
448
 */
449
static void
450
intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
451
28
{
452
28
  DR_intorel *myState = (DR_intorel *) self;
453
28
  IntoClause *into = myState->into;
454
28
  bool    is_matview;
455
28
  char    relkind;
456
28
  List     *attrList;
457
28
  ObjectAddress intoRelationAddr;
458
28
  Relation  intoRelationDesc;
459
28
  RangeTblEntry *rte;
460
28
  ListCell   *lc;
461
28
  int     attnum;
462
463
28
  Assert(into != NULL);   /* else somebody forgot to set it */
464
465
  /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
466
28
  is_matview = (into->viewQuery != NULL);
467
28
  relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION;
468
469
  /*
470
   * Build column definitions using "pre-cooked" type and collation info. If
471
   * a column name list was specified in CREATE TABLE AS, override the
472
   * column names derived from the query.  (Too few column names are OK, too
473
   * many are not.)
474
   */
475
28
  attrList = NIL;
476
28
  lc = list_head(into->colNames);
477
81
  for (attnum = 0; attnum < typeinfo->natts; attnum++)
478
53
  {
479
53
    Form_pg_attribute attribute = TupleDescAttr(typeinfo, attnum);
480
53
    ColumnDef  *col;
481
53
    char     *colname;
482
483
53
    if (lc)
484
3
    {
485
3
      colname = strVal(lfirst(lc));
486
3
      lc = lnext(lc);
487
3
    }
488
50
    else
489
50
      colname = NameStr(attribute->attname);
490
491
53
    col = makeColumnDef(colname,
492
53
              attribute->atttypid,
493
53
              attribute->atttypmod,
494
53
              attribute->attcollation);
495
496
    /*
497
     * It's possible that the column is of a collatable type but the
498
     * collation could not be resolved, so double-check.  (We must check
499
     * this here because DefineRelation would adopt the type's default
500
     * collation rather than complaining.)
501
     */
502
53
    if (!OidIsValid(col->collOid) &&
503
48
      type_is_collatable(col->typeName->typeOid))
504
53
      ereport(ERROR,
505
53
          (errcode(ERRCODE_INDETERMINATE_COLLATION),
506
53
           errmsg("no collation was derived for column \"%s\" with collatable type %s",
507
53
              col->colname,
508
53
              format_type_be(col->typeName->typeOid)),
509
53
           errhint("Use the COLLATE clause to set the collation explicitly.")));
510
511
53
    attrList = lappend(attrList, col);
512
53
  }
513
514
28
  if (lc != NULL)
515
28
    ereport(ERROR,
516
28
        (errcode(ERRCODE_SYNTAX_ERROR),
517
28
         errmsg("too many column names were specified")));
518
519
  /*
520
   * Actually create the target table
521
   */
522
28
  intoRelationAddr = create_ctas_internal(attrList, into);
523
524
  /*
525
   * Finally we can open the target table
526
   */
527
28
  intoRelationDesc = heap_open(intoRelationAddr.objectId, AccessExclusiveLock);
528
529
  /*
530
   * Check INSERT permission on the constructed table.
531
   *
532
   * XXX: It would arguably make sense to skip this check if into->skipData
533
   * is true.
534
   */
535
28
  rte = makeNode(RangeTblEntry);
536
28
  rte->rtekind = RTE_RELATION;
537
28
  rte->relid = intoRelationAddr.objectId;
538
28
  rte->relkind = relkind;
539
28
  rte->requiredPerms = ACL_INSERT;
540
541
79
  for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++)
542
51
    rte->insertedCols = bms_add_member(rte->insertedCols,
543
51
                       attnum - YBGetFirstLowInvalidAttributeNumber(intoRelationDesc));
544
545
28
  ExecCheckRTPerms(list_make1(rte), true);
546
547
  /*
548
   * Make sure the constructed table does not have RLS enabled.
549
   *
550
   * check_enable_rls() will ereport(ERROR) itself if the user has requested
551
   * something invalid, and otherwise will return RLS_ENABLED if RLS should
552
   * be enabled here.  We don't actually support that currently, so throw
553
   * our own ereport(ERROR) if that happens.
554
   */
555
28
  if (check_enable_rls(intoRelationAddr.objectId, InvalidOid, false) == RLS_ENABLED)
556
28
    ereport(ERROR,
557
28
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
558
28
         (errmsg("policies not yet implemented for this command"))));
559
560
  /*
561
   * Tentatively mark the target as populated, if it's a matview and we're
562
   * going to fill it; otherwise, no change needed.
563
   */
564
28
  if (is_matview && !into->skipData)
565
0
    SetMatViewPopulatedState(intoRelationDesc, true);
566
567
  /*
568
   * Fill private fields of myState for use by later routines
569
   */
570
28
  myState->rel = intoRelationDesc;
571
28
  myState->reladdr = intoRelationAddr;
572
28
  myState->output_cid = GetCurrentCommandId(true);
573
574
  /*
575
   * We can skip WAL-logging the insertions, unless PITR or streaming
576
   * replication is in use. We can skip the FSM in any case.
577
   */
578
28
  myState->hi_options = HEAP_INSERT_SKIP_FSM |
579
28
    (XLogIsNeeded() ? 0 : HEAP_INSERT_SKIP_WAL);
580
28
  myState->bistate = GetBulkInsertState();
581
582
  /* Not using WAL requires smgr_targblock be initially invalid */
583
28
  Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
584
28
}
585
586
/*
587
 * intorel_receive --- receive one tuple
588
 */
589
static bool
590
intorel_receive(TupleTableSlot *slot, DestReceiver *self)
591
10.2k
{
592
10.2k
  DR_intorel *myState = (DR_intorel *) self;
593
10.2k
  HeapTuple tuple;
594
595
  /*
596
   * get the heap tuple out of the tuple table slot, making sure we have a
597
   * writable copy
598
   */
599
10.2k
  tuple = ExecMaterializeSlot(slot);
600
601
  /*
602
   * force assignment of new OID (see comments in ExecInsert)
603
   */
604
10.2k
  if (myState->rel->rd_rel->relhasoids)
605
10.2k
    HeapTupleSetOid(tuple, InvalidOid);
606
607
  /*
608
   * if we are creating and inserting into a temporary table,
609
   * we must use PG transaction codepaths as well
610
   */
611
10.2k
  if (myState->rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP
612
0
      && IsYugaByteEnabled())
613
0
    SetTxnWithPGRel();
614
615
10.2k
  if (IsYBRelation(myState->rel))
616
10.2k
  {
617
10.2k
    YBCExecuteInsert(myState->rel, RelationGetDescr(myState->rel), tuple);
618
10.2k
  }
619
0
  else
620
0
  {
621
0
    heap_insert(myState->rel,
622
0
          tuple,
623
0
          myState->output_cid,
624
0
          myState->hi_options,
625
0
          myState->bistate);
626
0
  }
627
628
  /* We know this is a newly created relation, so there are no indexes */
629
630
10.2k
  return true;
631
10.2k
}
632
633
/*
634
 * intorel_shutdown --- executor end
635
 */
636
static void
637
intorel_shutdown(DestReceiver *self)
638
26
{
639
26
  DR_intorel *myState = (DR_intorel *) self;
640
641
26
  FreeBulkInsertState(myState->bistate);
642
643
  /* If we skipped using WAL, must heap_sync before commit */
644
26
  if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
645
0
    heap_sync(myState->rel);
646
647
  /* close rel, but keep lock until commit */
648
26
  heap_close(myState->rel, NoLock);
649
26
  myState->rel = NULL;
650
26
}
651
652
/*
653
 * intorel_destroy --- release DestReceiver object
654
 */
655
static void
656
intorel_destroy(DestReceiver *self)
657
0
{
658
0
  pfree(self);
659
0
}