/* * multi_ExecutorStart is a hook called at at the beginning of any execution * of any query plan. * * If a distributed relation is the target of the query, perform some validity * checks. If a legal statement, start the distributed execution. After that * the to-be-executed query is replaced with the portion executing solely on * the master. */ void multi_ExecutorStart(QueryDesc *queryDesc, int eflags) { PlannedStmt *planStatement = queryDesc->plannedstmt; if (HasCitusToplevelNode(planStatement)) { MultiPlan *multiPlan = GetMultiPlan(planStatement); MultiExecutorType executorType = MULTI_EXECUTOR_INVALID_FIRST; Job *workerJob = multiPlan->workerJob; ExecCheckRTPerms(planStatement->rtable, true); executorType = JobExecutorType(multiPlan); if (executorType == MULTI_EXECUTOR_ROUTER) { Task *task = NULL; List *taskList = workerJob->taskList; TupleDesc tupleDescriptor = ExecCleanTypeFromTL( planStatement->planTree->targetlist, false); List *dependendJobList PG_USED_FOR_ASSERTS_ONLY = workerJob->dependedJobList; /* router executor can only execute distributed plans with a single task */ Assert(list_length(taskList) == 1); Assert(dependendJobList == NIL); task = (Task *) linitial(taskList); /* we need to set tupleDesc in executorStart */ queryDesc->tupDesc = tupleDescriptor; /* drop into the router executor */ RouterExecutorStart(queryDesc, eflags, task); } else { PlannedStmt *masterSelectPlan = MasterNodeSelectPlan(multiPlan); CreateStmt *masterCreateStmt = MasterNodeCreateStatement(multiPlan); List *masterCopyStmtList = MasterNodeCopyStatementList(multiPlan); RangeTblEntry *masterRangeTableEntry = NULL; StringInfo jobDirectoryName = NULL; /* * We create a directory on the master node to keep task execution results. * We also register this directory for automatic cleanup on portal delete. */ jobDirectoryName = JobDirectoryName(workerJob->jobId); CreateDirectory(jobDirectoryName); ResourceOwnerEnlargeJobDirectories(CurrentResourceOwner); ResourceOwnerRememberJobDirectory(CurrentResourceOwner, workerJob->jobId); /* pick distributed executor to use */ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) { /* skip distributed query execution for EXPLAIN commands */ } else if (executorType == MULTI_EXECUTOR_REAL_TIME) { MultiRealTimeExecute(workerJob); } else if (executorType == MULTI_EXECUTOR_TASK_TRACKER) { MultiTaskTrackerExecute(workerJob); } /* then create the result relation */ ProcessUtility((Node *) masterCreateStmt, "(temp table creation)", PROCESS_UTILITY_QUERY, NULL, None_Receiver, NULL); /* make the temporary table visible */ CommandCounterIncrement(); if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY)) { CopyQueryResults(masterCopyStmtList); } /* * Update the QueryDesc's snapshot so it sees the table. That's not * particularly pretty, but we don't have much of a choice. One might * think we could unregister the snapshot, push a new active one, * update it, register it, and be happy. That only works if it's only * registered once though... */ queryDesc->snapshot->curcid = GetCurrentCommandId(false); /* * Set the OID of the RTE used in the master select statement to point * to the now created (and filled) temporary table. The target * relation's oid is only known now. */ masterRangeTableEntry = (RangeTblEntry *) linitial(masterSelectPlan->rtable); masterRangeTableEntry->relid = RelnameGetRelid(masterRangeTableEntry->eref->aliasname); /* * Replace to-be-run query with the master select query. As the * planned statement is now replaced we can't call GetMultiPlan() in * the later hooks, so we set a flag marking this as a distributed * statement running on the master. That e.g. allows us to drop the * temp table later. * * We copy the original statement's queryId, to allow * pg_stat_statements and similar extension to associate the * statement with the toplevel statement. */ masterSelectPlan->queryId = queryDesc->plannedstmt->queryId; queryDesc->plannedstmt = masterSelectPlan; eflags |= EXEC_FLAG_CITUS_MASTER_SELECT; } } /* if the execution is not done for router executor, drop into standard executor */ if (queryDesc->estate == NULL || !(queryDesc->estate->es_top_eflags & EXEC_FLAG_CITUS_ROUTER_EXECUTOR)) { standard_ExecutorStart(queryDesc, eflags); } }
Datum partition_insert_trigger(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; char *date_time; char child_table[sizeof(TABLE)+sizeof("2012_09_10")] = TABLE; int partition_field; Relation child_table_id; Oid child_table_oid; BulkInsertState bistate = GetBulkInsertState(); TupleTableSlot *slot; EState *estate = CreateExecutorState(); ResultRelInfo *resultRelInfo = makeNode(ResultRelInfo); List *recheckIndexes = NIL; /* make sure it's called as a trigger at all */ if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "partition_insert_trigger: not called by trigger manager"); /* Sanity checks */ if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) || !TRIGGER_FIRED_BEFORE(trigdata->tg_event)) elog(ERROR, "partition_insert_trigger: not called on insert before"); #ifdef DEBUG elog(INFO, "Trigger Called for: %s", SPI_getrelname(trigdata->tg_relation)); #endif // Get the field number for the partition partition_field = SPI_fnumber(trigdata->tg_relation->rd_att, PARTITION_COLUMN); // Get the value for the partition_field date_time = SPI_getvalue(trigdata->tg_trigtuple, trigdata->tg_relation->rd_att, partition_field); //make sure date was specified if (!date_time) elog(ERROR, "You cannot insert data without specifying a value for the column %s", PARTITION_COLUMN); #ifdef DEBUG elog(INFO, "Trying to insert date_time=%s", date_time); #endif //add date_time, child_table_2012_01_23 strncpy(child_table + sizeof(TABLE) -1 , date_time, 4); //2012 child_table[sizeof(TABLE) + 3] = SEPARATOR; //2012_ strncpy(child_table + sizeof(TABLE) + 4 , date_time + 5, 2); //2012_01 child_table[sizeof(TABLE) + 6] = SEPARATOR; //2012_01_ strncpy(child_table + sizeof(TABLE) + 7 , date_time + 8, 2); //2012_01_23 #ifdef DEBUG elog(INFO, "New table will be %s", child_table); #endif pfree(date_time); //if you care about triggers on the child tables, call ExecBRInsertTriggers //don't care, continue //get the OID of the table we are looking for to insert child_table_oid = RelnameGetRelid(child_table); //Look for child child_table if (child_table_oid == InvalidOid){ elog(INFO, "partition_insert_trigger: Invalid child table %s, inserting data to main table %s", child_table, TABLE); return PointerGetDatum(trigdata->tg_trigtuple); } //get the descriptor of the table we are looking for child_table_id = RelationIdGetRelation(child_table_oid); //Get the child relation descriptor if (child_table_id == NULL){ elog(ERROR, "partition_insert_trigger: Failed to locate relation for child table %s, inserting data to main table %s", child_table, TABLE); return PointerGetDatum(trigdata->tg_trigtuple); } //set the resultRelInfo resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RelationDesc = child_table_id; //setup the estate, not sure why estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; /* Set up a tuple slot not sure why yet */ slot = MakeSingleTupleTableSlot(trigdata->tg_relation->rd_att); ExecStoreTuple(trigdata->tg_trigtuple, slot, InvalidBuffer, false); //heap_insert(child_table_id, trigdata->tg_trigtuple, GetCurrentCommandId(true), use_wal, bistate); simple_heap_insert(child_table_id, trigdata->tg_trigtuple); if (resultRelInfo->ri_NumIndices > 0) recheckIndexes = ExecInsertIndexTuples(slot, &(trigdata->tg_trigtuple->t_self), estate); // not sure if this would work CatalogUpdateIndexes(child_table_id, trigdata->tg_trigtuple) //free the used memory list_free(recheckIndexes); ExecDropSingleTupleTableSlot(slot); //saw this somewhere :P RelationClose(child_table_id); //Must be called to free the relation memory page FreeBulkInsertState(bistate); // ? not sure if needed ? //If return next line will add to the regular table //return PointerGetDatum(trigdata->tg_trigtuple); //Return Null data, still have to figure out to return a proper X rows affected return PointerGetDatum(NULL); }