/*
|
* 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"
|
|
#include "pg_fix.h"
|
|
Datum create_vlabel(PG_FUNCTION_ARGS);
|
Datum create_elabel(PG_FUNCTION_ARGS);
|
|
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));
|
}
|
|
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),
|
CStringGetDatum(vtx_label_name));
|
}
|
}
|
|
if (!label_exists(edge_name_str, graph_oid))
|
{
|
DirectFunctionCall2(create_elabel,
|
CStringGetDatum(graph_name),
|
CStringGetDatum(edge_label_name));
|
}
|
|
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
|
start_node_index = 1; // first created node, from the first complete graph
|
end_node_index = arguments->args[1].value*2; // last created node, second graph
|
|
// 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();
|
}
|