/*
|
* For PostgreSQL Database Management System:
|
* (formerly known as Postgres, then as Postgres95)
|
*
|
* Portions Copyright (c) 1996-2010, The PostgreSQL Global Development Group
|
*
|
* Portions Copyright (c) 1994, The Regents of the University of California
|
*
|
* Permission to use, copy, modify, and distribute this software and its documentation for any purpose,
|
* without fee, and without a written agreement is hereby granted, provided that the above copyright notice
|
* and this paragraph and the following two paragraphs appear in all copies.
|
*
|
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT,
|
* INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
|
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
|
* OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
*
|
* THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA
|
* HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
*/
|
|
#include "postgres.h"
|
|
#include "access/htup_details.h"
|
#include "access/sysattr.h"
|
#include "access/xact.h"
|
#include "access/heapam.h"
|
#include "access/multixact.h"
|
#include "access/xact.h"
|
#include "nodes/extensible.h"
|
#include "nodes/makefuncs.h"
|
#include "nodes/nodes.h"
|
#include "nodes/nodeFuncs.h"
|
#include "nodes/plannodes.h"
|
#include "parser/parsetree.h"
|
#include "parser/parse_relation.h"
|
#include "storage/procarray.h"
|
#include "utils/rel.h"
|
|
#include "catalog/ag_label.h"
|
#include "commands/label_commands.h"
|
#include "executor/cypher_executor.h"
|
#include "executor/cypher_utils.h"
|
#include "utils/agtype.h"
|
#include "utils/ag_cache.h"
|
#include "utils/agtype.h"
|
#include "utils/graphid.h"
|
|
/*
|
* Given the graph name and the label name, create a ResultRelInfo for the table
|
* those to variables represent. Open the Indices too.
|
*/
|
ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name,
|
char *label_name)
|
{
|
RangeVar *rv;
|
Relation label_relation;
|
ResultRelInfo *resultRelInfo;
|
|
ParseState *pstate = make_parsestate(NULL);
|
|
resultRelInfo = palloc(sizeof(ResultRelInfo));
|
|
if (strlen(label_name) == 0)
|
{
|
rv = makeRangeVar(graph_name, AG_DEFAULT_LABEL_VERTEX, -1);
|
}
|
else
|
{
|
rv = makeRangeVar(graph_name, label_name, -1);
|
}
|
|
label_relation = parserOpenTable(pstate, rv, RowExclusiveLock);
|
|
// initialize the resultRelInfo
|
InitResultRelInfo(resultRelInfo, label_relation,
|
list_length(estate->es_range_table), NULL,
|
estate->es_instrument);
|
|
// open the parse state
|
ExecOpenIndices(resultRelInfo, false);
|
|
free_parsestate(pstate);
|
|
return resultRelInfo;
|
}
|
|
// close the result_rel_info and close all the indices
|
void destroy_entity_result_rel_info(ResultRelInfo *result_rel_info)
|
{
|
// close the indices
|
ExecCloseIndices(result_rel_info);
|
|
// close the rel
|
table_close(result_rel_info->ri_RelationDesc, RowExclusiveLock);
|
}
|
|
TupleTableSlot *populate_vertex_tts(
|
TupleTableSlot *elemTupleSlot, agtype_value *id, agtype_value *properties)
|
{
|
bool properties_isnull;
|
|
if (id == NULL)
|
{
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("vertex id field cannot be NULL")));
|
}
|
|
properties_isnull = properties == NULL;
|
|
elemTupleSlot->tts_values[vertex_tuple_id] = GRAPHID_GET_DATUM(id->val.int_value);
|
elemTupleSlot->tts_isnull[vertex_tuple_id] = false;
|
|
elemTupleSlot->tts_values[vertex_tuple_properties] =
|
AGTYPE_P_GET_DATUM(agtype_value_to_agtype(properties));
|
elemTupleSlot->tts_isnull[vertex_tuple_properties] = properties_isnull;
|
|
return elemTupleSlot;
|
}
|
|
TupleTableSlot *populate_edge_tts(
|
TupleTableSlot *elemTupleSlot, agtype_value *id, agtype_value *startid,
|
agtype_value *endid, agtype_value *properties)
|
{
|
bool properties_isnull;
|
|
if (id == NULL)
|
{
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("edge id field cannot be NULL")));
|
}
|
if (startid == NULL)
|
{
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("edge start_id field cannot be NULL")));
|
}
|
|
if (endid == NULL)
|
{
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("edge end_id field cannot be NULL")));
|
}
|
|
properties_isnull = properties == NULL;
|
|
elemTupleSlot->tts_values[edge_tuple_id] =
|
GRAPHID_GET_DATUM(id->val.int_value);
|
elemTupleSlot->tts_isnull[edge_tuple_id] = false;
|
|
elemTupleSlot->tts_values[edge_tuple_start_id] =
|
GRAPHID_GET_DATUM(startid->val.int_value);
|
elemTupleSlot->tts_isnull[edge_tuple_start_id] = false;
|
|
elemTupleSlot->tts_values[edge_tuple_end_id] =
|
GRAPHID_GET_DATUM(endid->val.int_value);
|
elemTupleSlot->tts_isnull[edge_tuple_end_id] = false;
|
|
elemTupleSlot->tts_values[edge_tuple_properties] =
|
AGTYPE_P_GET_DATUM(agtype_value_to_agtype(properties));
|
elemTupleSlot->tts_isnull[edge_tuple_properties] = properties_isnull;
|
|
return elemTupleSlot;
|
}
|
|
|
/*
|
* Find out if the entity still exists. This is for 'implicit' deletion
|
* of an entity.
|
*/
|
bool entity_exists(EState *estate, Oid graph_oid, graphid id)
|
{
|
label_cache_data *label;
|
ScanKeyData scan_keys[1];
|
TableScanDesc scan_desc;
|
HeapTuple tuple;
|
Relation rel;
|
bool result = true;
|
|
/*
|
* Extract the label id from the graph id and get the table name
|
* the entity is part of.
|
*/
|
label = search_label_graph_oid_cache(graph_oid, GET_LABEL_ID(id));
|
|
// Setup the scan key to be the graphid
|
ScanKeyInit(&scan_keys[0], 1, BTEqualStrategyNumber,
|
F_GRAPHIDEQ, GRAPHID_GET_DATUM(id));
|
|
rel = table_open(label->relation, RowExclusiveLock);
|
scan_desc = table_beginscan(rel, estate->es_snapshot, 1, scan_keys);
|
|
tuple = heap_getnext(scan_desc, ForwardScanDirection);
|
|
/*
|
* If a single tuple was returned, the tuple is still valid, otherwise'
|
* set to false.
|
*/
|
if (!HeapTupleIsValid(tuple))
|
{
|
result = false;
|
}
|
|
table_endscan(scan_desc);
|
table_close(rel, RowExclusiveLock);
|
|
return result;
|
}
|
|
/*
|
* Insert the edge/vertex tuple into the table and indices. Check that the
|
* table's constraints have not been violated.
|
*
|
* This function defaults to, and flags for update, the currentCommandId. If
|
* you need to pass a specific cid and avoid using the currentCommandId, use
|
* insert_entity_tuple_cid instead.
|
*/
|
HeapTuple insert_entity_tuple(ResultRelInfo *resultRelInfo,
|
TupleTableSlot *elemTupleSlot,
|
EState *estate)
|
{
|
return insert_entity_tuple_cid(resultRelInfo, elemTupleSlot, estate,
|
GetCurrentCommandId(true));
|
}
|
|
/*
|
* Insert the edge/vertex tuple into the table and indices. Check that the
|
* table's constraints have not been violated.
|
*
|
* This function uses the passed cid for updates.
|
*/
|
HeapTuple insert_entity_tuple_cid(ResultRelInfo *resultRelInfo,
|
TupleTableSlot *elemTupleSlot,
|
EState *estate, CommandId cid)
|
{
|
HeapTuple tuple = NULL;
|
|
ExecStoreVirtualTuple(elemTupleSlot);
|
tuple = ExecFetchSlotHeapTuple(elemTupleSlot, true, NULL);
|
|
/* Check the constraints of the tuple */
|
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
|
if (resultRelInfo->ri_RelationDesc->rd_att->constr != NULL)
|
{
|
ExecConstraints(resultRelInfo, elemTupleSlot, estate);
|
}
|
|
// Insert the tuple normally
|
table_tuple_insert(resultRelInfo->ri_RelationDesc, elemTupleSlot,
|
GetCurrentCommandId(true), 0, NULL);
|
|
// Insert index entries for the tuple
|
if (resultRelInfo->ri_NumIndices > 0)
|
{
|
ExecInsertIndexTuples(resultRelInfo, elemTupleSlot, estate, false,
|
false, NULL, NIL);
|
}
|
|
return tuple;
|
}
|