| /* |
| * 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 "nodes/makefuncs.h" |
| #include "parser/parse_relation.h" |
| |
| #include "catalog/ag_label.h" |
| #include "commands/label_commands.h" |
| #include "executor/cypher_utils.h" |
| #include "utils/ag_cache.h" |
| |
| /* |
| * Given the graph name and the label name, create a ResultRelInfo for the table |
| * those two variables represent. Open the Indices too. |
| */ |
| ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name, |
| char *label_name) |
| { |
| RangeVar *rv = NULL; |
| Relation label_relation = NULL; |
| ResultRelInfo *resultRelInfo = NULL; |
| ParseState *pstate = NULL; |
| RangeTblEntry *rte = NULL; |
| int pii = 0; |
| |
| /* create a new parse state for this operation */ |
| 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); |
| |
| /* |
| * Get the rte to determine the correct perminfoindex value. Some rtes |
| * may have it set up, some created here (executor) may not. |
| * |
| * Note: The RTEPermissionInfo structure was added in PostgreSQL version 16. |
| * |
| * Note: We use the list_length because exec_rt_fetch starts at 1, not 0. |
| * Doing this gives us the last rte in the es_range_table list, which |
| * is the rte in question. |
| * |
| * If the rte is created here and doesn't have a perminfoindex, we |
| * need to pass on a 0. Otherwise, later on GetResultRTEPermissionInfo |
| * will attempt to get the rte's RTEPermissionInfo data, which doesn't |
| * exist. |
| * |
| * TODO: Ideally, we should consider creating the RTEPermissionInfo data, |
| * but as this is just a read of the label relation, it is likely |
| * unnecessary. |
| */ |
| rte = exec_rt_fetch(list_length(estate->es_range_table), estate); |
| pii = (rte->perminfoindex == 0) ? 0 : list_length(estate->es_range_table); |
| |
| /* initialize the resultRelInfo */ |
| InitResultRelInfo(resultRelInfo, label_relation, pii, NULL, |
| estate->es_instrument); |
| |
| /* open the indices */ |
| 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, false); |
| } |
| |
| return tuple; |
| } |