blob: ea8e1bd5bb17b311e9c5c087ae5306882d989d74 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "postgres.h"
#include "access/genam.h"
#include "commands/graph_commands.h"
#include "utils/load/age_load.h"
int64 get_nextval_internal(graph_cache_data* graph_cache,
label_cache_data* label_cache);
/*
* Auxiliary function to get the next internal value in the graph,
* so a new object (node or edge) graph id can be composed.
*/
int64 get_nextval_internal(graph_cache_data* graph_cache,
label_cache_data* label_cache)
{
Oid obj_seq_id;
char* label_seq_name_str;
label_seq_name_str = NameStr(label_cache->seq_name);
obj_seq_id = get_relname_relid(label_seq_name_str,
graph_cache->namespace);
return nextval_internal(obj_seq_id, true);
}
PG_FUNCTION_INFO_V1(create_complete_graph);
/*
* SELECT * FROM ag_catalog.create_complete_graph('graph_name',no_of_nodes, 'edge_label', 'node_label'=NULL);
*/
Datum create_complete_graph(PG_FUNCTION_ARGS)
{
Oid graph_oid;
Name graph_name;
int64 no_vertices;
int64 i,j,vid = 1, eid, start_vid, end_vid;
Name vtx_label_name = NULL;
Name edge_label_name;
int32 vtx_label_id;
int32 edge_label_id;
agtype *props = NULL;
graphid object_graph_id;
graphid start_vertex_graph_id;
graphid end_vertex_graph_id;
Oid vtx_seq_id;
Oid edge_seq_id;
char* graph_name_str;
char* vtx_name_str;
char* edge_name_str;
graph_cache_data *graph_cache;
label_cache_data *vertex_cache;
label_cache_data *edge_cache;
Oid nsp_id;
Name vtx_seq_name;
char *vtx_seq_name_str;
Name edge_seq_name;
char *edge_seq_name_str;
int64 lid;
if (PG_ARGISNULL(0))
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("graph name can not be NULL")));
}
if (PG_ARGISNULL(1))
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("number of nodes can not be NULL")));
}
if (PG_ARGISNULL(2))
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("edge label can not be NULL")));
}
graph_name = PG_GETARG_NAME(0);
no_vertices = (int64) PG_GETARG_INT64(1);
edge_label_name = PG_GETARG_NAME(2);
graph_name_str = NameStr(*graph_name);
vtx_name_str = AG_DEFAULT_LABEL_VERTEX;
edge_name_str = NameStr(*edge_label_name);
if (!PG_ARGISNULL(3))
{
vtx_label_name = PG_GETARG_NAME(3);
vtx_name_str = NameStr(*vtx_label_name);
/* Check if vertex and edge label are same */
if (strcmp(vtx_name_str, edge_name_str) == 0)
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("vertex and edge label can not be same")));
}
}
if (!graph_exists(graph_name_str))
{
DirectFunctionCall1(create_graph, CStringGetDatum(graph_name->data));
}
graph_oid = get_graph_oid(graph_name_str);
if (!PG_ARGISNULL(3))
{
/* Check if label with the input name already exists */
if (!label_exists(vtx_name_str, graph_oid))
{
DirectFunctionCall2(create_vlabel,
CStringGetDatum(graph_name->data),
CStringGetDatum(vtx_label_name->data));
}
}
if (!label_exists(edge_name_str, graph_oid))
{
DirectFunctionCall2(create_elabel,
CStringGetDatum(graph_name->data),
CStringGetDatum(edge_label_name->data));
}
vtx_label_id = get_label_id(vtx_name_str, graph_oid);
edge_label_id = get_label_id(edge_name_str, graph_oid);
graph_cache = search_graph_name_cache(graph_name_str);
vertex_cache = search_label_name_graph_cache(vtx_name_str, graph_oid);
edge_cache = search_label_name_graph_cache(edge_name_str, graph_oid);
nsp_id = graph_cache->namespace;
vtx_seq_name = &(vertex_cache->seq_name);
vtx_seq_name_str = NameStr(*vtx_seq_name);
edge_seq_name = &(edge_cache->seq_name);
edge_seq_name_str = NameStr(*edge_seq_name);
vtx_seq_id = get_relname_relid(vtx_seq_name_str, nsp_id);
edge_seq_id = get_relname_relid(edge_seq_name_str, nsp_id);
props = create_empty_agtype();
/* Creating vertices*/
for (i=(int64)1; i<=no_vertices; i++)
{
vid = nextval_internal(vtx_seq_id, true);
object_graph_id = make_graphid(vtx_label_id, vid);
insert_vertex_simple(graph_oid, vtx_name_str, object_graph_id, props);
}
lid = vid;
/* Creating edges*/
for (i = 1; i<=no_vertices-1; i++)
{
start_vid = lid-no_vertices+i;
for(j=i+1; j<=no_vertices; j++)
{
end_vid = lid-no_vertices+j;
eid = nextval_internal(edge_seq_id, true);
object_graph_id = make_graphid(edge_label_id, eid);
start_vertex_graph_id = make_graphid(vtx_label_id, start_vid);
end_vertex_graph_id = make_graphid(vtx_label_id, end_vid);
insert_edge_simple(graph_oid, edge_name_str, object_graph_id,
start_vertex_graph_id, end_vertex_graph_id,
props);
}
}
PG_RETURN_VOID();
}
PG_FUNCTION_INFO_V1(age_create_barbell_graph);
/*
* The barbell graph is two complete graphs connected by a bridge path
* Syntax:
* ag_catalog.age_create_barbell_graph(graph_name Name,
* m int,
* n int,
* vertex_label_name Name DEFAULT = NULL,
* vertex_properties agtype DEFAULT = NULL,
* edge_label_name Name DEFAULT = NULL,
* edge_properties agtype DEFAULT = NULL)
* Input:
*
* graph_name - Name of the graph to be created.
* m - number of vertices in one complete graph.
* n - number of vertices in the bridge path.
* vertex_label_name - Name of the label to assign each vertex to.
* vertex_properties - Property values to assign each vertex. Default is NULL
* edge_label_name - Name of the label to assign each edge to.
* edge_properties - Property values to assign each edge. Default is NULL
*
* https://en.wikipedia.org/wiki/Barbell_graph
*/
Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
{
FunctionCallInfo arguments;
Oid graph_oid;
Name graph_name;
char* graph_name_str;
int64 start_node_index, end_node_index, nextval;
Name node_label_name = NULL;
int32 node_label_id;
char* node_label_str;
Name edge_label_name;
int32 edge_label_id;
char* edge_label_str;
graphid object_graph_id;
graphid start_node_graph_id;
graphid end_node_graph_id;
graph_cache_data* graph_cache;
label_cache_data* edge_cache;
agtype* properties = NULL;
arguments = fcinfo;
/* Checking for possible NULL arguments */
/* Name graph_name */
if (PG_ARGISNULL(0))
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Graph name cannot be NULL")));
}
graph_name = PG_GETARG_NAME(0);
graph_name_str = NameStr(*graph_name);
/* int graph size (number of nodes in each complete graph) */
if (PG_ARGISNULL(1) && PG_GETARG_INT32(1) < 3)
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Graph size cannot be NULL or lower than 3")));
}
/*
* int64 bridge_size: currently only stays at zero.
* to do: implement bridge with variable number of nodes.
*/
if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0 )
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Bridge size cannot be NULL or lower than 0")));
}
/* node label: if null, gets default label, which is "_ag_label_vertex" */
if (PG_ARGISNULL(3))
{
namestrcpy(node_label_name, AG_DEFAULT_LABEL_VERTEX);
}
else
{
node_label_name = PG_GETARG_NAME(3);
}
node_label_str = NameStr(*node_label_name);
/* Name edge_label */
if (PG_ARGISNULL(5))
{
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("edge label can not be NULL")));
}
edge_label_name = PG_GETARG_NAME(5);
edge_label_str = NameStr(*edge_label_name);
/* create two separate complete graphs */
DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
arguments->args[1].value,
arguments->args[5].value,
arguments->args[3].value);
DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
arguments->args[1].value,
arguments->args[5].value,
arguments->args[3].value);
graph_oid = get_graph_oid(graph_name_str);
node_label_id = get_label_id(node_label_str, graph_oid);
edge_label_id = get_label_id(edge_label_str, graph_oid);
/*
* Fetching caches to get next values for graph id's, and access nodes
* to be connected with edges.
*/
graph_cache = search_graph_name_cache(graph_name_str);
edge_cache = search_label_name_graph_cache(edge_label_str,graph_oid);
/* connect a node from each graph */
/* first created node, from the first complete graph */
start_node_index = 1;
/* last created node, second graph */
end_node_index = arguments->args[1].value*2;
/* next index to be assigned to a node or edge */
nextval = get_nextval_internal(graph_cache, edge_cache);
/* build the graph id's of the edge to be created */
object_graph_id = make_graphid(edge_label_id, nextval);
start_node_graph_id = make_graphid(node_label_id, start_node_index);
end_node_graph_id = make_graphid(node_label_id, end_node_index);
properties = create_empty_agtype();
/* connect two nodes */
insert_edge_simple(graph_oid, edge_label_str,
object_graph_id, start_node_graph_id,
end_node_graph_id, properties);
PG_RETURN_VOID();
}