├── .circleci └── config.yml ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── demo ├── README.md ├── __init__.py ├── client │ ├── __init__.py │ ├── client.py │ └── prettytable │ │ └── __init__.py ├── imdb │ ├── disposableredis │ │ └── __init__.py │ ├── imdb_demo.py │ ├── imdb_queries.py │ ├── imdb_utils.py │ ├── requirements.txt │ └── resources │ │ ├── actors.csv │ │ └── movies.csv ├── social │ ├── disposableredis │ │ └── __init__.py │ ├── requirements.txt │ ├── resources │ │ ├── countries.csv │ │ ├── friends.csv │ │ ├── person.csv │ │ └── visits.csv │ ├── social_demo.py │ ├── social_queries.py │ └── social_utils.py └── utils.py ├── docs ├── CNAME ├── commands.md ├── design.md ├── docker.md ├── images │ ├── favicon.png │ └── logo.svg └── index.md ├── mkdocs.yml ├── ramp.yml ├── src ├── Makefile ├── arithmetic │ ├── agg_ctx.h │ ├── agg_funcs.c │ ├── agg_funcs.h │ ├── aggregate.c │ ├── aggregate.h │ ├── arithmetic_expression.c │ ├── arithmetic_expression.h │ ├── repository.c │ └── repository.h ├── dep │ └── rax │ │ ├── COPYING │ │ ├── Makefile │ │ ├── rax.c │ │ ├── rax.h │ │ ├── rax_malloc.h │ │ ├── rax_oom_malloc.h │ │ ├── rax_type.c │ │ ├── rax_type.h │ │ ├── rc4rand.c │ │ └── rc4rand.h ├── execution_plan │ ├── execution_plan.c │ ├── execution_plan.h │ └── ops │ │ ├── op.h │ │ ├── op_aggregate.c │ │ ├── op_aggregate.h │ │ ├── op_all_node_scan.c │ │ ├── op_all_node_scan.h │ │ ├── op_create.c │ │ ├── op_create.h │ │ ├── op_delete.c │ │ ├── op_delete.h │ │ ├── op_expand_all.c │ │ ├── op_expand_all.h │ │ ├── op_expand_into.c │ │ ├── op_expand_into.h │ │ ├── op_filter.c │ │ ├── op_filter.h │ │ ├── op_index_scan.c │ │ ├── op_index_scan.h │ │ ├── op_node_by_label_scan.c │ │ ├── op_node_by_label_scan.h │ │ ├── op_produce_results.c │ │ ├── op_produce_results.h │ │ ├── op_update.c │ │ ├── op_update.h │ │ └── ops.h ├── filter_tree │ ├── filter_tree.c │ └── filter_tree.h ├── graph │ ├── edge.c │ ├── edge.h │ ├── graph.c │ ├── graph.h │ ├── graph_entity.c │ ├── graph_entity.h │ ├── node.c │ └── node.h ├── grouping │ ├── group.c │ ├── group.h │ ├── group_cache.c │ └── group_cache.h ├── hexastore │ ├── hexastore.c │ ├── hexastore.h │ ├── triplet.c │ └── triplet.h ├── index │ ├── index.c │ ├── index.h │ ├── index_type.c │ └── index_type.h ├── module.c ├── parser │ ├── Makefile │ ├── ast.c │ ├── ast.h │ ├── ast_arithmetic_expression.c │ ├── ast_arithmetic_expression.h │ ├── ast_common.c │ ├── ast_common.h │ ├── clauses │ │ ├── clauses.h │ │ ├── create.c │ │ ├── create.h │ │ ├── delete.c │ │ ├── delete.h │ │ ├── index.c │ │ ├── index.h │ │ ├── limit.c │ │ ├── limit.h │ │ ├── match.c │ │ ├── match.h │ │ ├── merge.c │ │ ├── merge.h │ │ ├── order.c │ │ ├── order.h │ │ ├── return.c │ │ ├── return.h │ │ ├── set.c │ │ ├── set.h │ │ ├── where.c │ │ └── where.h │ ├── grammar.c │ ├── grammar.h │ ├── grammar.y │ ├── lemon │ ├── lemon.c │ ├── lempar.c │ ├── lex.yy.c │ ├── lexer.l │ ├── parse.h │ ├── parser_common.h │ └── token.h ├── pytest │ ├── Makefile │ ├── rmtest │ │ ├── __init__.py │ │ └── disposableredis │ │ │ └── __init__.py │ └── test.py ├── query_executor.c ├── query_executor.h ├── redismodule.h ├── resultset │ ├── record.c │ ├── record.h │ ├── resultset.c │ ├── resultset.h │ └── resultset_header.h ├── rmutil │ ├── Makefile │ ├── heap.c │ ├── heap.h │ ├── priority_queue.c │ ├── priority_queue.h │ ├── sds.c │ ├── sds.h │ ├── sdsalloc.h │ ├── strings.c │ ├── strings.h │ ├── test_heap │ ├── test_heap.c │ ├── test_priority_queue │ ├── test_priority_queue.c │ ├── test_util.h │ ├── test_vector │ ├── test_vector.c │ ├── util.c │ ├── util.h │ ├── vector.c │ └── vector.h ├── stores │ ├── store.c │ └── store.h ├── util │ ├── heap.c │ ├── heap.h │ ├── prng.c │ ├── prng.h │ ├── qsort.h │ ├── sha1.c │ ├── sha1.h │ ├── skiplist.c │ ├── skiplist.h │ ├── snowflake.c │ ├── snowflake.h │ └── stats.h ├── value.c ├── value.h ├── value_cmp.c └── value_cmp.h └── tests ├── Makefile ├── __init__.py ├── flow ├── __init__.py ├── base.py ├── disposableredis │ └── __init__.py ├── requirments.txt ├── test_imdb.py └── test_social.py └── unit ├── .gitignore ├── Makefile ├── test_agg_sum.c ├── test_aggregate_functions.c ├── test_arithmetic_expression.c ├── test_ast.c ├── test_edge.c ├── test_filter_tree.c ├── test_graph.c ├── test_hexastore.c ├── test_node.c ├── test_resultset_record.c ├── test_skiplist_generic.c ├── test_skiplist_graph.c ├── test_triplet.c └── test_value.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Folders 32 | CMakeFiles/ 33 | 34 | # Files 35 | CMakeCache.txt 36 | DartConfiguration.tcl 37 | 38 | # Debug files 39 | *.dSYM/ 40 | *.su 41 | 42 | # Additional suffixes 43 | *.pyc 44 | *.cmake 45 | *.make 46 | *.xml 47 | *.iml 48 | *.d 49 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: gcc 3 | dist: xenial 4 | 5 | env: 6 | - MODULE_NAME=redisgraph REDIS_MODULE_PATH=$TRAVIS_BUILD_DIR/src/redisgraph.so REDIS_PATH=$TRAVIS_BUILD_DIR/redis/src/redis-server 7 | 8 | before_script: 9 | - pip install --user redis ramp-packer rmtest redisgraph 10 | - git clone -b 4.0.1 --depth 1 https://github.com/antirez/redis.git 11 | - cd redis 12 | - make -j 4 13 | - cd .. 14 | 15 | install: 16 | - make clean 17 | - make -j 4 18 | 19 | script: 20 | - make test 21 | - make package 22 | 23 | deploy: 24 | # Module latest artifact 25 | - provider: s3 26 | access_key_id: $AWS_ACCESS_KEY 27 | secret_access_key: $AWS_SECRET 28 | skip_cleanup: true 29 | acl: public_read 30 | bucket: redismodules 31 | upload-dir: redisgraph 32 | on: 33 | branch: master 34 | local_dir: $TRAVIS_BUILD_DIR/build 35 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM redislabsmodules/rmbuilder:latest as builder 2 | 3 | # Set up a build environment 4 | RUN set -ex;\ 5 | pip install redisgraph; 6 | 7 | # Build the source 8 | ADD ./ /redisgraph 9 | WORKDIR /redisgraph 10 | RUN set -ex;\ 11 | make clean; \ 12 | make all -j 4; \ 13 | make test; 14 | 15 | # Package the runner 16 | FROM redis:latest 17 | ENV LIBDIR /usr/lib/redis/modules 18 | WORKDIR /data 19 | RUN set -ex;\ 20 | mkdir -p "$LIBDIR"; 21 | COPY --from=builder /redisgraph/src/redisgraph.so "$LIBDIR" 22 | 23 | CMD ["redis-server", "--loadmodule", "/usr/lib/redis/modules/redisgraph.so"] 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C ./src all 3 | 4 | clean: 5 | $(MAKE) -C ./src $@ 6 | 7 | package: all 8 | $(MAKE) -C ./src package 9 | .PHONY: package 10 | 11 | docker: 12 | docker build . -t redislabs/redisgraph 13 | 14 | docker_push: docker 15 | docker push redislabs/redisgraph:latest 16 | 17 | builddocs: 18 | mkdocs build 19 | 20 | localdocs: builddocs 21 | mkdocs serve 22 | 23 | deploydocs: builddocs 24 | mkdocs gh-deploy 25 | 26 | test: 27 | $(MAKE) -C ./src test 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Future development of RedisGraph is at 2 | [github.com/RedisGraph/RedisGraph](https://github.com/RedisGraph/RedisGraph). Please submit issues at [issues](https://github.com/RedisGraph/RedisGraph/issues). 3 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Demos 2 | 3 | 1. Set the following environment variables: 4 | 5 | - REDIS_MODULE_PATH - Relative path to compiled redisgraph.so 6 | 7 | ```sh 8 | export REDIS_MODULE_PATH= 9 | ``` 10 | 11 | - REDIS_PATH - Absolute path to redis-server 12 | 13 | ```sh 14 | export REDIS_PATH= 15 | ``` 16 | 17 | 2. Install requirements: 18 | 19 | From either /demo/imdb or /demo/social, Run: 20 | 21 | ```sh 22 | pip install -r requirements.txt 23 | ``` 24 | 25 | 3. Run demo: 26 | 27 | ```sh 28 | python imdb_demo.py 29 | ``` 30 | 31 | or 32 | 33 | ```sh 34 | python social_demo.py 35 | ``` 36 | -------------------------------------------------------------------------------- /demo/__init__.py: -------------------------------------------------------------------------------- 1 | class QueryInfo(object): 2 | """ 3 | This class contains the needed data about a query 4 | """ 5 | 6 | def __init__(self, query=None, description=None, max_run_time_ms=None, expected_result=None): 7 | """ 8 | QueryInfo constructor 9 | 10 | :param query: The query itself (string) 11 | :param description: The information about what the query does (string) 12 | :param max_run_time_ms: The max run time of the query in milliseconds (float) 13 | :param expected_result: The expected result of the query (list of lists, where the first list 14 | is the columns names, and the rest is the result) 15 | """ 16 | 17 | self.query = query 18 | self.description = description 19 | self.expected_result = expected_result 20 | self.max_run_time_ms = max_run_time_ms 21 | -------------------------------------------------------------------------------- /demo/client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/demo/client/__init__.py -------------------------------------------------------------------------------- /demo/client/client.py: -------------------------------------------------------------------------------- 1 | import redis 2 | from prettytable import PrettyTable 3 | 4 | class Node(object): 5 | """ 6 | """ 7 | def __init__(self, node_id, *args, **kwargs): 8 | """ 9 | """ 10 | self.id = node_id 11 | self.label = kwargs["label"] 12 | self.properties = {} 13 | 14 | args = list(args) 15 | for i in range(0, len(args), 2): 16 | self.properties[args[i]] = args[i+1] 17 | 18 | class Edge(object): 19 | """ 20 | """ 21 | def __init__(self, relation, *args): 22 | """ 23 | """ 24 | self.relation = relation 25 | self.properties = {} 26 | 27 | args = list(args) 28 | for i in range(0, len(args), 2): 29 | self.properties[args[i]] = args[i+1] 30 | 31 | class RedisGraph(object): 32 | """ 33 | """ 34 | def __init__(self, graph, redis_con): 35 | """ 36 | """ 37 | self.graph = graph 38 | self.redis_con = redis_con 39 | 40 | def create_node(self, *args, **kwargs): 41 | """ 42 | Creates a new node, specify node properties 43 | within args where args[i] i%2=0 is 44 | the prop name, and i%2=1 is prop value 45 | """ 46 | label = kwargs["label"] 47 | if label: 48 | node_id = self.redis_con.execute_command( 49 | "GRAPH.CREATENODE", 50 | self.graph, 51 | label, 52 | *args) 53 | else: 54 | node_id = self.redis_con.execute_command( 55 | "GRAPH.CREATENODE", 56 | self.graph, 57 | *args) 58 | 59 | return Node(node_id, label=label, *args) 60 | 61 | def connect_nodes(self, src_node, relation, dest_node, *args): 62 | """ 63 | Connects source node to dest node with relation 64 | specify relation properties within 65 | args where args[i] i%2=0 is 66 | the prop name, and i%2=1 is prop value 67 | """ 68 | resp = self.redis_con.execute_command( 69 | "GRAPH.ADDEDGE", 70 | self.graph, 71 | src_node.id, 72 | dest_node.id, 73 | relation, 74 | *args) 75 | 76 | if resp == "OK": 77 | return Edge(relation, *args) 78 | else: 79 | return None 80 | 81 | def query(self, q): 82 | """ 83 | """ 84 | resultset = self.redis_con.execute_command("GRAPH.QUERY", self.graph, q) 85 | columns = resultset[0].split(",") 86 | resultset = resultset[1:] 87 | tbl = PrettyTable(columns) 88 | 89 | for idx, result in enumerate(resultset): 90 | if idx < len(resultset)-1: 91 | tbl.add_row(result.split(",")) 92 | 93 | print tbl 94 | # Last record holds internal execution time. 95 | print resultset[len(resultset)-1] 96 | return tbl 97 | 98 | def execution_plan(self, query): 99 | return self.redis_con.execute_command("GRAPH.EXPLAIN", self.graph, query) 100 | -------------------------------------------------------------------------------- /demo/imdb/disposableredis/__init__.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import socket 3 | import tempfile 4 | import redis 5 | import time 6 | import os 7 | import itertools 8 | 9 | def get_random_port(): 10 | sock = socket.socket() 11 | sock.listen(0) 12 | _, port = sock.getsockname() 13 | sock.close() 14 | 15 | return port 16 | 17 | 18 | class DisposableRedis(object): 19 | def __init__(self, port=None, path='redis-server', **extra_args): 20 | """ 21 | :param port: port number to start the redis server on. Specify none to automatically generate 22 | :type port: int|None 23 | :param extra_args: any extra arguments kwargs will be passed to redis server as --key val 24 | """ 25 | 26 | self._port = port 27 | 28 | # this will hold the actual port the redis is listening on. It's equal to `_port` unless `_port` is None 29 | # in that case `port` is randomly generated 30 | self.port = None 31 | self.extra_args = list(itertools.chain( 32 | *(('--%s'%k, v) for k, v in extra_args.items()) 33 | )) 34 | self.path = path 35 | 36 | 37 | def __enter__(self): 38 | if self._port is None: 39 | self.port = get_random_port() 40 | else: 41 | self.port = self._port 42 | args = [self.path, 43 | '--port', str(self.port), 44 | '--dir', tempfile.gettempdir(), 45 | '--save', ''] + self.extra_args 46 | 47 | self.process = subprocess.Popen( 48 | args, 49 | #cwd=os.getcwd(), 50 | stdin=subprocess.PIPE, 51 | stdout=open(os.devnull, 'w') 52 | ) 53 | 54 | while True: 55 | try: 56 | self.client().ping() 57 | 58 | break 59 | except redis.ConnectionError: 60 | self.process.poll() 61 | if self.process.returncode is not None: 62 | raise RuntimeError("Process has exited") 63 | time.sleep(0.1) 64 | 65 | return self.client() 66 | 67 | def __exit__(self, exc_type, exc_val, exc_tb): 68 | self.process.terminate() 69 | 70 | def client(self): 71 | """ 72 | :rtype: redis.StrictRedis 73 | """ 74 | 75 | return redis.StrictRedis(port=self.port) 76 | 77 | -------------------------------------------------------------------------------- /demo/imdb/imdb_demo.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import redis 4 | from redisgraph import Graph 5 | 6 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 7 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../') 8 | from imdb_queries import queries_info 9 | import imdb_utils 10 | from utils import execute_query, _redis 11 | 12 | 13 | redis_con = None 14 | redis_graph = None 15 | 16 | 17 | def run_queries(): 18 | print("Querying...\n") 19 | 20 | for query_info in queries_info: 21 | execute_query(redis_graph, 22 | query_info.description, 23 | query_info.query) 24 | 25 | 26 | def debug(): 27 | print("debug") 28 | global redis_con 29 | global redis_graph 30 | redis_con = redis.Redis(host='localhost', port=6379) 31 | redis_graph = Graph(imdb_utils.graph_name, redis_con) 32 | 33 | print("populate_graph") 34 | imdb_utils.populate_graph(redis_con, redis_graph) 35 | 36 | print("run_queries") 37 | run_queries() 38 | 39 | 40 | def main(argv): 41 | global redis_con 42 | global redis_graph 43 | if "--debug" in argv: 44 | debug() 45 | else: 46 | with _redis() as redis_con: 47 | redis_graph = Graph(imdb_utils.graph_name, redis_con) 48 | imdb_utils.populate_graph(redis_con, redis_graph) 49 | run_queries() 50 | 51 | 52 | if __name__ == '__main__': 53 | main(sys.argv[1:]) 54 | -------------------------------------------------------------------------------- /demo/imdb/imdb_utils.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | from datetime import date 4 | from redisgraph import Node, Edge 5 | 6 | 7 | graph_name = "imdb" 8 | 9 | 10 | def populate_graph(redis_con, redis_graph): 11 | # check if graph already exists 12 | if redis_con.exists(graph_name): 13 | return 14 | 15 | # Load movies entities 16 | print("Loading movies") 17 | movies = {} 18 | 19 | with open(os.path.dirname(os.path.abspath(__file__)) + '/resources/movies.csv', 'r') as f: 20 | reader = csv.reader(f, delimiter=',') 21 | for row in reader: 22 | title = row[0] 23 | genre = row[1] 24 | votes = int(row[2]) 25 | rating = float(row[3]) 26 | year = int(row[4]) 27 | 28 | node = Node(label="movie", properties={'title': title, 29 | 'genre': genre, 30 | 'votes': votes, 31 | 'rating': rating, 32 | 'year': year}) 33 | movies[title] = node 34 | redis_graph.add_node(node) 35 | 36 | print("Loaded %d movies" % len(movies)) 37 | 38 | # Load actors entities 39 | print("Loading actors") 40 | actors = {} 41 | today = date.today() 42 | 43 | with open(os.path.dirname(os.path.abspath(__file__)) + '/resources/actors.csv', 'r') as f: 44 | reader = csv.reader(f, delimiter=',') 45 | for row in reader: 46 | name = row[0] 47 | yearOfBirth = int(row[1]) 48 | movie = row[2] 49 | age = today.year - yearOfBirth 50 | 51 | if name not in actors: 52 | node = Node(label="actor", properties={'name': name, 'age': age}) 53 | actors[name] = node 54 | redis_graph.add_node(node) 55 | 56 | if movie in movies: 57 | edge = Edge(actors[name], "act", movies[movie]) 58 | redis_graph.add_edge(edge) 59 | 60 | print("Loaded %d actors" % len(actors)) 61 | redis_graph.commit() 62 | -------------------------------------------------------------------------------- /demo/imdb/requirements.txt: -------------------------------------------------------------------------------- 1 | redis==2.10.5 2 | redisgraph -------------------------------------------------------------------------------- /demo/social/disposableredis/__init__.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import socket 3 | import tempfile 4 | import redis 5 | import time 6 | import os 7 | import itertools 8 | 9 | def get_random_port(): 10 | sock = socket.socket() 11 | sock.listen(0) 12 | _, port = sock.getsockname() 13 | sock.close() 14 | 15 | return port 16 | 17 | 18 | class DisposableRedis(object): 19 | def __init__(self, port=None, path='redis-server', **extra_args): 20 | """ 21 | :param port: port number to start the redis server on. Specify none to automatically generate 22 | :type port: int|None 23 | :param extra_args: any extra arguments kwargs will be passed to redis server as --key val 24 | """ 25 | 26 | self._port = port 27 | 28 | # this will hold the actual port the redis is listening on. It's equal to `_port` unless `_port` is None 29 | # in that case `port` is randomly generated 30 | self.port = None 31 | self.extra_args = list(itertools.chain( 32 | *(('--%s'%k, v) for k, v in extra_args.items()) 33 | )) 34 | self.path = path 35 | 36 | 37 | def __enter__(self): 38 | if self._port is None: 39 | self.port = get_random_port() 40 | else: 41 | self.port = self._port 42 | args = [self.path, 43 | '--port', str(self.port), 44 | '--dir', tempfile.gettempdir(), 45 | '--save', ''] + self.extra_args 46 | 47 | self.process = subprocess.Popen( 48 | args, 49 | #cwd=os.getcwd(), 50 | stdin=subprocess.PIPE, 51 | stdout=open(os.devnull, 'w') 52 | ) 53 | 54 | while True: 55 | try: 56 | self.client().ping() 57 | 58 | break 59 | except redis.ConnectionError: 60 | self.process.poll() 61 | if self.process.returncode is not None: 62 | raise RuntimeError("Process has exited") 63 | time.sleep(0.1) 64 | 65 | return self.client() 66 | 67 | def __exit__(self, exc_type, exc_val, exc_tb): 68 | self.process.terminate() 69 | 70 | def client(self): 71 | """ 72 | :rtype: redis.StrictRedis 73 | """ 74 | 75 | return redis.StrictRedis(port=self.port) 76 | 77 | -------------------------------------------------------------------------------- /demo/social/requirements.txt: -------------------------------------------------------------------------------- 1 | redis==2.10.5 2 | redisgraph -------------------------------------------------------------------------------- /demo/social/resources/countries.csv: -------------------------------------------------------------------------------- 1 | USA, 2 | Prague, 3 | Japan, 4 | Greece, 5 | Canada, 6 | China, 7 | Amsterdam, 8 | Andora, 9 | Kazakhstan, 10 | Russia, 11 | Germany, 12 | Italy, 13 | Thailand -------------------------------------------------------------------------------- /demo/social/resources/friends.csv: -------------------------------------------------------------------------------- 1 | Roi Lipman,Alon Fital 2 | Roi Lipman,Ailon Velger 3 | Roi Lipman,Ori Laslo 4 | Roi Lipman,Boaz Arad 5 | Roi Lipman,Omri Traub 6 | Roi Lipman,Tal Doron 7 | Alon Fital,Lucy Yanfital 8 | Alon Fital,Gal Derriere 9 | Alon Fital,Mor Yesharim 10 | Ailon Velger,Jane Chernomorin 11 | Ailon Velger,Noam Nativ 12 | Ori Laslo,Shelly Laslo Rooz 13 | Boaz Arad,Valerie Abigail Arad -------------------------------------------------------------------------------- /demo/social/resources/person.csv: -------------------------------------------------------------------------------- 1 | Roi Lipman,32,male,married 2 | Alon Fital,32,male,married 3 | Ailon Velger,32,male,married 4 | Ori Laslo,32,male,married 5 | Boaz Arad,31,male,married 6 | Omri Traub,33,male,single 7 | Tal Doron,32,male,single 8 | Lucy Yanfital,30,female,married 9 | Jane Chernomorin,31,female,married 10 | Shelly Laslo Rooz,31,female,married 11 | Valerie Abigail Arad,31,female,married 12 | Gal Derriere,26,male,single 13 | Mor Yesharim,31,female,married 14 | Noam Nativ,34,male,single -------------------------------------------------------------------------------- /demo/social/resources/visits.csv: -------------------------------------------------------------------------------- 1 | Roi Lipman,USA,business 2 | Roi Lipman,Prague,both 3 | Roi Lipman,Japan,pleasure 4 | Alon Fital,Prague,both 5 | Alon Fital,USA,both 6 | Alon Fital,Greece,pleasure 7 | Ori Laslo,Canada,pleasure 8 | Ori Laslo,USA,business 9 | Ori Laslo,China,business 10 | Boaz Arad,Amsterdam,both 11 | Boaz Arad,USA,both 12 | Omri Traub,USA,pleasure 13 | Omri Traub,Greece,pleasure 14 | Omri Traub,Andora,pleasure 15 | Tal Doron,USA,both 16 | Tal Doron,Japan,business 17 | Tal Doron,Andora,pleasure 18 | Lucy Yanfital,Prague,pleasure 19 | Lucy Yanfital,USA,both 20 | Lucy Yanfital,Kazakhstan,pleasure 21 | Jane Chernomorin,USA,both 22 | Jane Chernomorin,Greece,pleasure 23 | Jane Chernomorin,Amsterdam,pleasure 24 | Shelly Laslo Rooz,USA,pleasure 25 | Shelly Laslo Rooz,Canada,pleasure 26 | Shelly Laslo Rooz,China,pleasure 27 | Valerie Abigail Arad,Amsterdam,pleasure 28 | Valerie Abigail Arad,Russia,pleasure 29 | Gal Derriere,Amsterdam,business 30 | Mor Yesharim,Germany,business 31 | Mor Yesharim,Greece,pleasure 32 | Mor Yesharim,Italy,pleasure 33 | Noam Nativ,Amsterdam,pleasure 34 | Noam Nativ,Germany,pleasure 35 | Noam Nativ,Thailand,pleasure -------------------------------------------------------------------------------- /demo/social/social_demo.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import redis 4 | from redisgraph import Graph 5 | 6 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 7 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../') 8 | from social_queries import queries_info 9 | import social_utils 10 | from utils import execute_query, _redis 11 | 12 | 13 | redis_con = None 14 | redis_graph = None 15 | 16 | 17 | def run_queries(): 18 | print("Querying...\n") 19 | 20 | for query_info in queries_info: 21 | execute_query(redis_graph, 22 | query_info.description, 23 | query_info.query) 24 | 25 | 26 | def debug(): 27 | print("debug") 28 | global redis_con 29 | global redis_graph 30 | redis_con = redis.Redis(host='localhost', port=6379) 31 | redis_graph = Graph(social_utils.graph_name, redis_con) 32 | 33 | print("populate_graph") 34 | social_utils.populate_graph(redis_con, redis_graph) 35 | 36 | print("run_queries") 37 | run_queries() 38 | 39 | 40 | def main(argv): 41 | global redis_con 42 | global redis_graph 43 | if "--debug" in argv: 44 | debug() 45 | else: 46 | with _redis() as redis_con: 47 | redis_graph = Graph(social_utils.graph_name, redis_con) 48 | social_utils.populate_graph(redis_con, redis_graph) 49 | run_queries() 50 | 51 | 52 | if __name__ == '__main__': 53 | main(sys.argv[1:]) 54 | -------------------------------------------------------------------------------- /demo/social/social_utils.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | from redisgraph import Node, Edge 4 | 5 | 6 | graph_name = "social" 7 | 8 | 9 | def populate_graph(redis_con, redis_graph): 10 | if redis_con.exists(graph_name): 11 | return 12 | 13 | # dictionary person name to its node entity 14 | persons = {} 15 | # dictionary country name to its node entity 16 | countries = {} 17 | 18 | # Create country entities 19 | with open(os.path.dirname(os.path.abspath(__file__)) + '/resources/countries.csv', 'r') as f: 20 | reader = csv.reader(f, delimiter=',') 21 | for row in reader: 22 | name = row[0] 23 | node = Node(label="country", properties={"name": name}) 24 | countries[name] = node 25 | redis_graph.add_node(node) 26 | 27 | # Create person entities 28 | with open(os.path.dirname(os.path.abspath(__file__)) + '/resources/person.csv', 'r') as f: 29 | reader = csv.reader(f, delimiter=',') 30 | for row in reader: 31 | name = row[0] 32 | age = int(row[1]) 33 | gender = row[2] 34 | status = row[3] 35 | node = Node(label="person", properties={"name": name, 36 | "age": age, 37 | "gender": gender, 38 | "status": status}) 39 | 40 | persons[name] = node 41 | redis_graph.add_node(node) 42 | 43 | # Connect people to places they've visited. 44 | with open(os.path.dirname(os.path.abspath(__file__)) + '/resources/visits.csv', 'r') as f: 45 | reader = csv.reader(f, delimiter=',') 46 | for row in reader: 47 | person = row[0] 48 | country = row[1] 49 | purpose = row[2] 50 | edge = Edge(persons[person], 51 | "visited", 52 | countries[country], 53 | properties={'purpose': purpose}) 54 | redis_graph.add_edge(edge) 55 | 56 | # Connect friends 57 | with open(os.path.dirname(os.path.abspath(__file__)) + '/resources/friends.csv', 'r') as f: 58 | reader = csv.reader(f, delimiter=',') 59 | for row in reader: 60 | person = persons[row[0]] 61 | friend = persons[row[1]] 62 | edge = Edge(person, "friend", friend) 63 | redis_graph.add_edge(edge) 64 | 65 | redis_graph.commit() 66 | -------------------------------------------------------------------------------- /demo/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from disposableredis import DisposableRedis 3 | 4 | 5 | REDIS_MODULE_PATH_ENVVAR = 'REDIS_MODULE_PATH' 6 | REDIS_PATH_ENVVAR = 'REDIS_PATH' 7 | REDIS_PORT_ENVVAR = 'REDIS_PORT' 8 | 9 | 10 | def execute_query(redis_graph, query_desc, query): 11 | print(query_desc) 12 | print("query: {query}".format(query=query)) 13 | print("execution plan:\n{plan}".format(plan=redis_graph.execution_plan(query))) 14 | query_res = redis_graph.query(query) 15 | query_res.pretty_print() 16 | print("\n") 17 | 18 | 19 | def _redis(): 20 | module_path = os.getenv(REDIS_MODULE_PATH_ENVVAR) 21 | redis_path = os.getenv(REDIS_PATH_ENVVAR) 22 | fixed_port = os.getenv(REDIS_PORT_ENVVAR) 23 | 24 | if module_path is None: 25 | print("Undeclared environment variable {}".format(REDIS_MODULE_PATH_ENVVAR)) 26 | print("run: export {}=../../src/redisgraph.so".format(REDIS_MODULE_PATH_ENVVAR)) 27 | return None 28 | 29 | if redis_path is None: 30 | print("Undeclared environment variable {}".format(REDIS_PATH_ENVVAR)) 31 | print("run: export {}=".format(REDIS_PATH_ENVVAR)) 32 | return None 33 | 34 | _module_path = os.path.abspath(os.path.join(os.getcwd(), module_path)) 35 | 36 | port = None 37 | if fixed_port is not None: 38 | port = fixed_port 39 | 40 | print("port=%s, path=%s, loadmodule=%s" % (port, redis_path, _module_path)) 41 | dr = DisposableRedis(port=port, path=redis_path, loadmodule=_module_path) 42 | return dr 43 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | redisgraph.io -------------------------------------------------------------------------------- /docs/docker.md: -------------------------------------------------------------------------------- 1 | # Docker 2 | 3 | 4 | ## Using Docker 5 | 6 | ### Build Docker image 7 | 8 | RedisGraph's `Dockerfile` uses a recent verion of Docker in order to take 9 | advantage of features such as [build stages](https://docs.docker.com/develop/develop-images/multistage-build/). 10 | To build RedisGraph Docker images, you will need a version of Docker equal to 11 | or greater than 17.05. To update the Docker on your system, see the 12 | [Docker update documentation](https://www.docker.com/community-edition#/download) 13 | for your OS. 14 | 15 | The RedisGraph Docker build has been updated to run the tests after compiling 16 | the module, so running the tests with Docker is simply a matter of executing 17 | the following: 18 | 19 | ``` 20 | $ make docker 21 | ``` 22 | 23 | As the output scrolls by, towards the end you will see the tests get run, and 24 | if all goes as expected, you will see them all pass. 25 | 26 | ### Running within a Docker container 27 | 28 | To run RedisGraph within docker simply execute: 29 | ``` 30 | $ docker run -p 6379:6379 redislabs/redisgraph 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/docs/images/favicon.png -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Redis Graph - a graph database module for Redis 2 | site_url: http://redisgraph.io 3 | site_favicon: 'images/favicon.png' 4 | repo_url: https://github.com/RedisLabsModules/redis-graph 5 | repo_name: RedisLabsModules/redis-graph 6 | 7 | theme: material 8 | 9 | extra: 10 | palette: 11 | primary: 'indigo' 12 | logo: 'images/logo.svg' 13 | social: 14 | - type: 'github' 15 | link: 'https://github.com/redislabs' 16 | - type: 'twitter' 17 | link: 'https://twitter.com/redislabs' 18 | 19 | pages: 20 | - 'Quickstart': index.md 21 | - 'Commands': commands.md 22 | - 'Design': design.md 23 | - 'Docker': docker.md 24 | 25 | markdown_extensions: 26 | - admonition 27 | - codehilite(guess_lang=false) 28 | - toc(permalink=true) -------------------------------------------------------------------------------- /ramp.yml: -------------------------------------------------------------------------------- 1 | display_name: redisgraph 2 | author: RedisLabs 3 | email: roi@redislabs.com 4 | description: A graph database on top of Redis which supports Open-Cypher query language. 5 | homepage: http://redisgraph.io 6 | license: AGPLv3 7 | command_line_args: "" 8 | min_redis_version: "4.0" 9 | min_redis_pack_version: "5.0" 10 | capabilities: 11 | - types 12 | - no_multi_key -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # find the OS 2 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 3 | 4 | CC=gcc 5 | export CC 6 | 7 | # if DEBUG env var is set, we compile with "debug" cflags 8 | DEBUGFLAGS = -g -ggdb -O3 9 | ifeq ($(DEBUG), 1) 10 | DEBUGFLAGS = -g -ggdb3 -O0 11 | endif 12 | 13 | # Default CFLAGS 14 | CFLAGS = -Wall -Wno-unused-function -Wno-unused-variable -Wno-unused-result -fPIC \ 15 | -D_GNU_SOURCE -std=gnu99 -I"$(shell pwd)" -DREDIS_MODULE_TARGET \ 16 | -DREDISMODULE_EXPERIMENTAL_API 17 | CFLAGS += $(DEBUGFLAGS) 18 | 19 | # Compile flags for linux / osx 20 | ifeq ($(uname_S),Linux) 21 | SHOBJ_LDFLAGS ?= -shared -Bsymbolic -Bsymbolic-functions -ldl -lpthread 22 | else 23 | CFLAGS += -mmacosx-version-min=10.6 24 | SHOBJ_LDFLAGS ?= -macosx_version_min 10.6 -exported_symbol _RedisModule_OnLoad -bundle -undefined dynamic_lookup -ldl -lpthread 25 | endif 26 | export CFLAGS 27 | 28 | # Sources 29 | SOURCEDIR=$(shell pwd -P) 30 | CC_SOURCES = $(wildcard $(SOURCEDIR)/*.c) 31 | CC_SOURCES += $(wildcard $(SOURCEDIR)/arithmetic/*.c) 32 | CC_SOURCES += $(wildcard $(SOURCEDIR)/execution_plan/*.c) 33 | CC_SOURCES += $(wildcard $(SOURCEDIR)/execution_plan/ops/*.c) 34 | CC_SOURCES += $(wildcard $(SOURCEDIR)/filter_tree/*.c) 35 | CC_SOURCES += $(wildcard $(SOURCEDIR)/graph/*.c) 36 | CC_SOURCES += $(wildcard $(SOURCEDIR)/grouping/*.c) 37 | CC_SOURCES += $(wildcard $(SOURCEDIR)/hexastore/*.c) 38 | CC_SOURCES += $(wildcard $(SOURCEDIR)/parser/clauses/*.c) 39 | CC_SOURCES += $(SOURCEDIR)/parser/ast.c 40 | CC_SOURCES += $(SOURCEDIR)/parser/ast_common.c 41 | CC_SOURCES += $(SOURCEDIR)/parser/ast_arithmetic_expression.c 42 | CC_SOURCES += $(SOURCEDIR)/parser/lex.yy.c 43 | CC_SOURCES += $(SOURCEDIR)/parser/grammar.c 44 | CC_SOURCES += $(wildcard $(SOURCEDIR)/resultset/*.c) 45 | CC_SOURCES += $(wildcard $(SOURCEDIR)/stores/*.c) 46 | CC_SOURCES += $(wildcard $(SOURCEDIR)/util/*.c) 47 | CC_SOURCES += $(wildcard $(SOURCEDIR)/dep/rax/*.c) 48 | CC_SOURCES += $(wildcard $(SOURCEDIR)/index/*.c) 49 | 50 | # Convert all sources to .o files 51 | CC_OBJECTS = $(patsubst %.c, %.o, $(CC_SOURCES) ) 52 | export CC_OBJECTS 53 | 54 | # .d files for each c file. These make sure that changing a header file 55 | # will also change the dependent .c files of it 56 | CC_DEPS = $(patsubst %.c, %.d, $(CC_SOURCES) ) 57 | 58 | # Library dependencies 59 | LIBRMUTIL=rmutil/librmutil.a 60 | 61 | # Compilation deps for the module 62 | LIBS=$(LIBRMUTIL) 63 | MODULE=$(CC_OBJECTS) $(LIBS) 64 | 65 | %.c: %.y 66 | 67 | # Compile C file while generating a .d file for it 68 | %.o: %.c 69 | %.o: %.c 70 | $(CC) $(CFLAGS) -c $< -o $@ -MMD -MF $(@:.o=.d) 71 | 72 | all: redisgraph.so 73 | 74 | # Include all dependency files for C files 75 | -include $(CC_DEPS) 76 | 77 | # Library compile rules 78 | $(LIBRMUTIL): 79 | $(MAKE) -C rmutil 80 | .PHONY: $(LIBRMUTIL) 81 | 82 | # Compile query parse. 83 | # This is not included in the usual make target! 84 | parser: 85 | $(MAKE) -C $@ 86 | .PHONY: parser 87 | 88 | # Build the module... 89 | redisgraph.so: $(MODULE) 90 | $(LD) -o $@ $(MODULE) $(SHOBJ_LDFLAGS) -lc -lm 91 | 92 | clean: 93 | find . -name '*.[oad]' -type f -delete 94 | $(MAKE) -C ../tests clean 95 | 96 | package: redisgraph.so 97 | mkdir -p ../build 98 | ramp pack -m "`pwd`/../ramp_manifest.yml" -v -o "../build/redisgraph.{os}-{architecture}.latest.zip" "`pwd`/redisgraph.so" 99 | 100 | test: $(MODULE) redisgraph.so 101 | # unit tests 102 | $(MAKE) -C ../tests test 103 | -------------------------------------------------------------------------------- /src/arithmetic/agg_ctx.h: -------------------------------------------------------------------------------- 1 | #ifndef __AGG_CTX_H__ 2 | #define __AGG_CTX_H__ 3 | 4 | #include "../value.h" 5 | 6 | typedef char AggError; 7 | 8 | struct AggCtx { 9 | void *fctx; 10 | AggError *err; 11 | SIValue result; 12 | int (*Step)(struct AggCtx *ctx, SIValue *argv, int argc); 13 | int (*ReduceNext)(struct AggCtx *ctx); 14 | }; 15 | typedef struct AggCtx AggCtx; 16 | 17 | #endif -------------------------------------------------------------------------------- /src/arithmetic/agg_funcs.h: -------------------------------------------------------------------------------- 1 | #ifndef __AGG_FUNCTIONS_H__ 2 | #define __AGG_FUNCTIONS_H__ 3 | 4 | #include "agg_ctx.h" 5 | 6 | typedef AggCtx* (*AggFuncInit)(void); 7 | 8 | AggCtx* Agg_SumFunc(); 9 | AggCtx* Agg_AvgFunc(); 10 | AggCtx* Agg_MaxFunc(); 11 | AggCtx* Agg_MinFunc(); 12 | AggCtx* Agg_CountFunc(); 13 | AggCtx* Agg_PercContFunc(); 14 | AggCtx* Agg_PercDiscFunc(); 15 | AggCtx* Agg_stDev(); 16 | 17 | void Agg_RegisterFuncs(); 18 | 19 | #endif -------------------------------------------------------------------------------- /src/arithmetic/aggregate.c: -------------------------------------------------------------------------------- 1 | #include "aggregate.h" 2 | 3 | AggCtx *Agg_Reduce(void *ctx, StepFunc f, ReduceFunc reduce) { 4 | AggCtx *ac = Agg_NewCtx(ctx); 5 | ac->Step = f; 6 | ac->ReduceNext = reduce; 7 | return ac; 8 | } 9 | 10 | AggCtx *Agg_NewCtx(void *fctx) { 11 | AggCtx *ac = malloc(sizeof(AggCtx)); 12 | ac->err = NULL; 13 | ac->fctx = fctx; 14 | ac->result = SI_NullVal(); 15 | ac->Step = NULL; 16 | ac->ReduceNext = NULL; 17 | return ac; 18 | } 19 | 20 | void AggCtx_Free(AggCtx *ctx) { 21 | if (ctx->err) { 22 | free(ctx->err); 23 | } 24 | SIValue_Free(&ctx->result); 25 | free(ctx); 26 | } 27 | 28 | int Agg_SetError(AggCtx *ctx, AggError *err) { 29 | ctx->err = err; 30 | return AGG_ERR; 31 | } 32 | 33 | int Agg_Step(AggCtx *ctx, SIValue *argv, int argc) { 34 | return ctx->Step(ctx, argv, argc); 35 | } 36 | 37 | int Agg_Finalize(AggCtx *ctx) { 38 | return ctx->ReduceNext(ctx); 39 | } 40 | 41 | inline void *Agg_FuncCtx(AggCtx *ctx) { return ctx->fctx; } 42 | 43 | inline void Agg_SetResult(struct AggCtx *ctx, SIValue v) { 44 | ctx->result = v; 45 | } -------------------------------------------------------------------------------- /src/arithmetic/aggregate.h: -------------------------------------------------------------------------------- 1 | #ifndef __SI_AGREGATE_H__ 2 | #define __SI_AGREGATE_H__ 3 | 4 | #include 5 | 6 | #include "agg_ctx.h" 7 | #include "../value.h" 8 | 9 | #define AGG_OK 1 10 | #define AGG_SKIP 2 11 | #define AGG_EOF 0 12 | #define AGG_ERR -1 13 | 14 | #define AGG_STATE_INIT 0 15 | #define AGG_STATE_DONE 1 16 | #define AGG_STATE_ERR 2 17 | #define AGG_STATE_EOF 3 18 | 19 | typedef int (*StepFunc)(AggCtx *ctx, SIValue *argv, int argc); 20 | typedef int (*ReduceFunc)(AggCtx *ctx); 21 | 22 | AggCtx *Agg_Reduce(void *ctx, StepFunc f, ReduceFunc reduce); 23 | AggCtx *Agg_NewCtx(void *fctx); 24 | void AggCtx_Free(AggCtx *ctx); 25 | int Agg_SetError(AggCtx *ctx, AggError *err); 26 | void *Agg_FuncCtx(AggCtx *ctx); 27 | void Agg_SetResult(AggCtx *ctx, SIValue v); 28 | 29 | int Agg_Step(AggCtx *ctx, SIValue *argv, int argc); 30 | int Agg_Finalize(AggCtx *ctx); 31 | 32 | #endif -------------------------------------------------------------------------------- /src/arithmetic/repository.c: -------------------------------------------------------------------------------- 1 | #include "repository.h" 2 | #include "../rmutil/vector.h" 3 | 4 | typedef struct { 5 | const char *name; 6 | AggFuncInit func; 7 | } __aggFuncEntry; 8 | 9 | static Vector *__aggRegisteredFuncs = NULL; 10 | 11 | static void __agg_initRegistry() { 12 | if (__aggRegisteredFuncs == NULL) { 13 | __aggRegisteredFuncs = NewVector(__aggFuncEntry *, 8); 14 | } 15 | } 16 | 17 | int Agg_RegisterFunc(const char* name, AggFuncInit f) { 18 | __agg_initRegistry(); 19 | __aggFuncEntry *e = malloc(sizeof(__aggFuncEntry)); 20 | e->name = strdup(name); 21 | e->func = f; 22 | return Vector_Push(__aggRegisteredFuncs, e); 23 | } 24 | 25 | void Agg_GetFunc(const char* name, AggCtx** ctx) { 26 | if (!__aggRegisteredFuncs) { 27 | *ctx = NULL; 28 | return; 29 | } 30 | 31 | for (int i = 0; i < Vector_Size(__aggRegisteredFuncs); i++) { 32 | __aggFuncEntry *e = NULL; 33 | Vector_Get(__aggRegisteredFuncs, i, &e); 34 | if (e != NULL && !strcasecmp(name, e->name)) { 35 | *ctx = e->func(); 36 | return; 37 | } 38 | } 39 | *ctx = NULL; 40 | } -------------------------------------------------------------------------------- /src/arithmetic/repository.h: -------------------------------------------------------------------------------- 1 | #ifndef __AGG_FUNC_REPO_H__ 2 | #define __AGG_FUNC_REPO_H__ 3 | 4 | #include "agg_ctx.h" 5 | #include "agg_funcs.h" 6 | 7 | void Agg_GetFunc(const char* name, AggCtx** ctx); 8 | int Agg_RegisterFunc(const char* name, AggFuncInit f); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/dep/rax/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Salvatore Sanfilippo 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /src/dep/rax/Makefile: -------------------------------------------------------------------------------- 1 | DEBUG?= -g -ggdb 2 | CFLAGS?= -O2 -Wall -W -std=c99 3 | LDFLAGS= -lm 4 | 5 | # Uncomment the following two lines for coverage testing 6 | # 7 | # CFLAGS+=-fprofile-arcs -ftest-coverage 8 | # LDFLAGS+=-lgcov 9 | 10 | all: rax-test rax-oom-test 11 | 12 | rax.o: rax.h 13 | rax-test.o: rax.h 14 | rax-oom-test.o: rax.h 15 | 16 | rax-test: rax-test.o rax.o rc4rand.o 17 | $(CC) -o $@ $^ $(LDFLAGS) $(DEBUG) 18 | 19 | rax-oom-test: rax-oom-test.o rax.o 20 | $(CC) -o $@ $^ $(LDFLAGS) $(DEBUG) 21 | 22 | .c.o: 23 | $(CC) -c $(CFLAGS) $(DEBUG) $< 24 | 25 | clean: 26 | rm -f rax-test rax-oom-test *.gcda *.gcov *.gcno *.o 27 | -------------------------------------------------------------------------------- /src/dep/rax/rax_malloc.h: -------------------------------------------------------------------------------- 1 | /* Rax -- A radix tree implementation. 2 | * 3 | * Copyright (c) 2017, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /* Allocator selection. 32 | * 33 | * This file is used in order to change the Rax allocator at compile time. 34 | * Just define the following defines to what you want to use. Also add 35 | * the include of your alternate allocator if needed (not needed in order 36 | * to use the default libc allocator). */ 37 | 38 | #ifndef RAX_ALLOC_H 39 | #define RAX_ALLOC_H 40 | #define rax_malloc malloc 41 | #define rax_realloc realloc 42 | #define rax_free free 43 | #endif 44 | -------------------------------------------------------------------------------- /src/dep/rax/rax_oom_malloc.h: -------------------------------------------------------------------------------- 1 | /* Rax -- A radix tree implementation. 2 | * 3 | * Copyright (c) 2017, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /* Allocator selection. 32 | * 33 | * This file is used in order to change the Rax allocator at compile time. 34 | * Just define the following defines to what you want to use. Also add 35 | * the include of your alternate allocator if needed (not needed in order 36 | * to use the default libc allocator). */ 37 | 38 | #ifndef RAX_ALLOC_H 39 | #define RAX_ALLOC_H 40 | 41 | #include 42 | #include 43 | 44 | void *oom_malloc(char *file, int line, size_t size) { 45 | if ((rand() % 20) == 0) { 46 | printf("oom_malloc(): returning NULL at %s:%d\n", file, line); 47 | return NULL; 48 | } 49 | return malloc(size); 50 | } 51 | 52 | void *oom_realloc(char *file, int line, void *ptr, size_t size) { 53 | if ((rand() % 20) == 0) { 54 | printf("oom_remalloc(): returning NULL at %s:%d\n", file, line); 55 | return NULL; 56 | } 57 | return realloc(ptr,size); 58 | } 59 | 60 | #define rax_malloc(sz) oom_malloc(__FILE__,__LINE__,(sz)) 61 | #define rax_realloc(ptr,sz) oom_realloc(__FILE__,__LINE__,(ptr),(sz)) 62 | #define rax_free free 63 | #endif 64 | -------------------------------------------------------------------------------- /src/dep/rax/rax_type.c: -------------------------------------------------------------------------------- 1 | #include "./rax.h" 2 | #include "rax_type.h" 3 | 4 | /* declaration of the type for redis registration. */ 5 | RedisModuleType *RaxRedisModuleType; 6 | 7 | void *RaxType_RdbLoad(RedisModuleIO *rdb, int encver) { 8 | if (encver != 0) { 9 | return NULL; 10 | } 11 | 12 | // Determin how many elements are in the trie 13 | uint64_t elements = RedisModule_LoadUnsigned(rdb); 14 | rax *r = raxNew(); 15 | 16 | while (elements--) { 17 | size_t len; 18 | char *key = RedisModule_LoadStringBuffer(rdb, &len); 19 | // TODO: Load value from RDB. 20 | raxInsert(r, (unsigned char *)key, len, NULL, NULL); 21 | } 22 | return r; 23 | } 24 | 25 | void RaxType_RdbSave(RedisModuleIO *rdb, void *value) { 26 | rax *r = (rax *)value; 27 | int count = raxSize(r); 28 | 29 | RedisModule_SaveUnsigned(rdb, count); 30 | 31 | // Scan entire trie. 32 | raxIterator it; 33 | raxStart(&it, r); 34 | raxSeek(&it, "^", NULL, 0); 35 | 36 | while(raxNext(&it)) { 37 | RedisModule_SaveStringBuffer(rdb, (const char *)it.key, it.key_len); 38 | } 39 | raxStop(&it); 40 | } 41 | 42 | void RaxType_AofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { 43 | // TODO: implement. 44 | } 45 | 46 | void RaxType_Free(void *value) { 47 | rax *r = value; 48 | raxFree(r); 49 | } 50 | 51 | int RaxType_Register(RedisModuleCtx *ctx) { 52 | RedisModuleTypeMethods tm = {.version = REDISMODULE_TYPE_METHOD_VERSION, 53 | .rdb_load = RaxType_RdbLoad, 54 | .rdb_save = RaxType_RdbSave, 55 | .aof_rewrite = RaxType_AofRewrite, 56 | .free = RaxType_Free}; 57 | 58 | RaxRedisModuleType = RedisModule_CreateDataType(ctx, "raxtype01", RAX_TYPE_ENCODING_VERSION, &tm); 59 | if (RaxRedisModuleType == NULL) { 60 | return REDISMODULE_ERR; 61 | } 62 | 63 | return REDISMODULE_OK; 64 | } -------------------------------------------------------------------------------- /src/dep/rax/rax_type.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAX_TYPE_H__ 2 | #define __RAX_TYPE_H__ 3 | 4 | #include "../../redismodule.h" 5 | 6 | extern RedisModuleType *RaxRedisModuleType; 7 | 8 | #define RAX_TYPE_ENCODING_VERSION 1 9 | 10 | /* Commands related to the redis rax registration */ 11 | int RaxType_Register(RedisModuleCtx *ctx); 12 | void* RaxType_RdbLoad(RedisModuleIO *rdb, int encver); 13 | void RaxType_RdbSave(RedisModuleIO *rdb, void *value); 14 | void RaxType_AofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value); 15 | void RaxType_Free(void *value); 16 | 17 | #endif -------------------------------------------------------------------------------- /src/dep/rax/rc4rand.c: -------------------------------------------------------------------------------- 1 | /* A simple RC4 based RNG that sucks less than the certain libc rand() 2 | * implementations, notably the incredibly bad MacOS() implementation. 3 | * 4 | * Copyright (c) 2018, Salvatore Sanfilippo 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | 31 | /* Initialize the sbox with an initial seed. */ 32 | static unsigned char rc4_sbox[256] = {244, 223, 67, 241, 181, 90, 95, 76, 248, 141, 130, 109, 92, 230, 238, 131, 111, 26, 75, 196, 186, 185, 51, 4, 40, 156, 11, 23, 15, 101, 144, 128, 173, 121, 123, 124, 218, 89, 187, 29, 178, 7, 0, 1, 217, 113, 235, 33, 182, 158, 94, 221, 100, 53, 140, 172, 139, 24, 162, 143, 229, 202, 36, 37, 39, 35, 45, 231, 87, 103, 198, 14, 255, 83, 93, 136, 165, 64, 3, 16, 49, 219, 155, 183, 233, 73, 215, 10, 159, 247, 212, 99, 106, 74, 154, 72, 22, 19, 65, 52, 46, 195, 134, 197, 69, 209, 34, 91, 137, 115, 102, 135, 63, 211, 47, 56, 127, 161, 27, 9, 118, 220, 191, 54, 21, 216, 18, 253, 81, 148, 42, 213, 114, 12, 190, 245, 129, 8, 41, 201, 168, 177, 204, 32, 242, 82, 13, 117, 2, 152, 60, 66, 166, 193, 164, 79, 142, 133, 126, 30, 85, 252, 184, 20, 17, 205, 236, 174, 226, 138, 38, 62, 222, 157, 77, 145, 250, 6, 249, 78, 192, 132, 170, 189, 176, 86, 153, 97, 125, 50, 254, 44, 61, 240, 203, 225, 246, 88, 234, 150, 179, 149, 122, 25, 104, 214, 105, 199, 228, 200, 163, 5, 112, 84, 55, 206, 251, 31, 98, 167, 108, 116, 175, 194, 171, 188, 58, 210, 237, 96, 147, 208, 110, 80, 169, 71, 180, 243, 239, 59, 28, 227, 160, 146, 48, 107, 68, 151, 224, 119, 43, 57, 207, 120, 232, 70}; 33 | 34 | void rc4srand(uint64_t seed) { 35 | /* To seed the RNG in a reproducible way we use a simple 36 | * hashing function, together with the swapping idea of the 37 | * original RC4 key scheduling algorithm. */ 38 | for (int j = 0; j < 255; j++) rc4_sbox[j] = j; 39 | 40 | seed ^= 5381; 41 | for (int j = 0; j < 256; j++) { 42 | seed = ((seed << 5) + seed) + j + 1; 43 | int i = seed & 0xff; 44 | 45 | /* Swap sbox[i] and sbox[j]. */ 46 | unsigned char t = rc4_sbox[j]; 47 | rc4_sbox[j] = rc4_sbox[i]; 48 | rc4_sbox[i] = t; 49 | } 50 | } 51 | 52 | static void rc4(unsigned char *buf, unsigned long len) { 53 | static unsigned int i = 0, j = 0; 54 | unsigned int si, sj, x; 55 | 56 | for (x = 0; x < len; x++) { 57 | i = (i+1) & 0xff; 58 | si = rc4_sbox[i]; 59 | j = (j + si) & 0xff; 60 | sj = rc4_sbox[j]; 61 | rc4_sbox[i] = sj; 62 | rc4_sbox[j] = si; 63 | *buf++ = rc4_sbox[(si+sj)&0xff]; 64 | } 65 | } 66 | 67 | uint32_t rc4rand(void) { 68 | uint32_t r; 69 | rc4((unsigned char*)&r,sizeof(r)); 70 | return r; 71 | } 72 | 73 | uint64_t rc4rand64(void) { 74 | uint64_t r; 75 | rc4((unsigned char*)&r,sizeof(r)); 76 | return r; 77 | } 78 | -------------------------------------------------------------------------------- /src/dep/rax/rc4rand.h: -------------------------------------------------------------------------------- 1 | #ifndef RC4RAND_H 2 | #define RC4RAND_H 3 | 4 | #include 5 | void rc4srand(uint64_t seed); 6 | uint32_t rc4rand(void); 7 | uint64_t rc4rand64(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/execution_plan/execution_plan.h: -------------------------------------------------------------------------------- 1 | #ifndef __EXECUTION_PLAN_H__ 2 | #define __EXECUTION_PLAN_H__ 3 | 4 | #include "./ops/op.h" 5 | #include "../parser/ast.h" 6 | #include "../resultset/resultset.h" 7 | #include "../filter_tree/filter_tree.h" 8 | 9 | /* StreamState 10 | * Different states in which stream can be at. */ 11 | typedef enum { 12 | StreamUnInitialized, 13 | StreamConsuming, 14 | StreamDepleted, 15 | } StreamState; 16 | 17 | struct OpNode; 18 | 19 | struct OpNode { 20 | OpBase *operation; /* Node operation. */ 21 | struct OpNode **children; /* Child operations. */ 22 | int childCount; /* Number of children. */ 23 | struct OpNode **parents; /* Parent operations. */ 24 | int parentCount; /* Number of parents. */ 25 | StreamState state; /* Stream state. */ 26 | }; 27 | 28 | typedef struct OpNode OpNode; 29 | 30 | OpNode* NewOpNode(OpBase *op); 31 | void OpNode_Free(OpNode* op); 32 | 33 | 34 | typedef struct { 35 | OpNode *root; 36 | Graph *graph; 37 | FT_FilterNode *filter_tree; 38 | const char *graph_name; 39 | ResultSet *result_set; 40 | } ExecutionPlan; 41 | 42 | /* Creates a new execution plan from AST */ 43 | ExecutionPlan *NewExecutionPlan(RedisModuleCtx *ctx, const char *graph, AST_Query *ast); 44 | 45 | /* Prints execution plan */ 46 | char* ExecutionPlanPrint(const ExecutionPlan *plan); 47 | 48 | /* Executes plan */ 49 | ResultSet* ExecutionPlan_Execute(ExecutionPlan *plan); 50 | 51 | /* Free execution plan */ 52 | void ExecutionPlanFree(ExecutionPlan *plan); 53 | 54 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_H__ 2 | #define __OP_H__ 3 | 4 | #include "../../redismodule.h" 5 | #include "../../graph/graph.h" 6 | #include "../../graph/node.h" 7 | #include "../../graph/edge.h" 8 | #include "../../stores/store.h" 9 | #include "../../rmutil/vector.h" 10 | 11 | #define OP_REQUIRE_NEW_DATA(opRes) (opRes & (OP_DEPLETED | OP_REFRESH)) > 0 12 | 13 | typedef enum { 14 | OPType_AGGREGATE, 15 | OPType_ALL_NODE_SCAN, 16 | OPType_EXPAND_ALL, 17 | OPType_EXPAND_INTO, 18 | OPType_FILTER, 19 | OPType_NODE_BY_LABEL_SCAN, 20 | OpType_INDEX_SCAN, 21 | OPType_PRODUCE_RESULTS, 22 | OPType_CREATE, 23 | OPType_UPDATE, 24 | OPType_DELETE 25 | } OPType; 26 | 27 | typedef enum { 28 | OP_DEPLETED = 1, 29 | OP_REFRESH = 2, 30 | OP_OK = 4, 31 | OP_ERR = 8, 32 | } OpResult; 33 | 34 | struct OpBase; 35 | 36 | typedef OpResult (*fpConsume)(struct OpBase*, Graph* graph); 37 | typedef OpResult (*fpReset)(struct OpBase*); 38 | typedef void (*fpFree)(struct OpBase*); 39 | 40 | struct OpBase { 41 | OPType type; 42 | fpConsume consume; 43 | fpReset reset; 44 | fpFree free; 45 | char *name; 46 | Vector *modifies; // List of aliases, this op modifies. 47 | }; 48 | typedef struct OpBase OpBase; 49 | 50 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_aggregate.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_AGGREGATE_H__ 2 | #define __OP_AGGREGATE_H__ 3 | 4 | #include "op.h" 5 | #include "../../parser/ast.h" 6 | #include "../../redismodule.h" 7 | #include "../../graph/graph.h" 8 | #include "../../arithmetic/arithmetic_expression.h" 9 | 10 | /* Aggregate 11 | * aggregates graph according to 12 | * return clause */ 13 | typedef struct { 14 | OpBase op; 15 | RedisModuleCtx *ctx; 16 | int refreshAfterPass; 17 | AST_Query *ast; 18 | int none_aggregated_expression_count; /* Number of return terms which are not aggregated. */ 19 | AR_ExpNode **none_aggregated_expressions; 20 | SIValue *group_keys; /* Array of values composing an aggregated group. */ 21 | int init; 22 | } Aggregate; 23 | 24 | OpBase* NewAggregateOp(RedisModuleCtx *ctx, AST_Query *ast); 25 | Aggregate* NewAggregate(RedisModuleCtx *ctx, AST_Query *ast); 26 | OpResult AggregateConsume(OpBase *opBase, Graph* graph); 27 | OpResult AggregateReset(OpBase *opBase); 28 | void AggregateFree(OpBase *opBase); 29 | 30 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_all_node_scan.c: -------------------------------------------------------------------------------- 1 | #include "op_all_node_scan.h" 2 | 3 | OpBase* NewAllNodeScanOp(RedisModuleCtx *ctx, Graph *g, Node **n, const char *graph_name) { 4 | return (OpBase*)NewAllNodeScan(ctx, g, n, graph_name); 5 | } 6 | 7 | AllNodeScan* NewAllNodeScan(RedisModuleCtx *ctx, Graph *g, Node **n, const char *graph_name) { 8 | // Get graph store 9 | LabelStore *store = LabelStore_Get(ctx, STORE_NODE, graph_name, NULL); 10 | if(store == NULL) { 11 | return NULL; 12 | } 13 | 14 | AllNodeScan *allNodeScan = malloc(sizeof(AllNodeScan)); 15 | allNodeScan->ctx = ctx; 16 | allNodeScan->node = n; 17 | allNodeScan->_node = *n; 18 | allNodeScan->store = store; 19 | allNodeScan->graph = graph_name; 20 | LabelStore_Scan(store, &allNodeScan->iter); 21 | 22 | // Set our Op operations 23 | allNodeScan->op.name = "All Node Scan"; 24 | allNodeScan->op.type = OPType_ALL_NODE_SCAN; 25 | allNodeScan->op.consume = AllNodeScanConsume; 26 | allNodeScan->op.reset = AllNodeScanReset; 27 | allNodeScan->op.free = AllNodeScanFree; 28 | allNodeScan->op.modifies = NewVector(char*, 1); 29 | 30 | Vector_Push(allNodeScan->op.modifies, Graph_GetNodeAlias(g, *n)); 31 | 32 | return allNodeScan; 33 | } 34 | 35 | OpResult AllNodeScanConsume(OpBase *opBase, Graph* graph) { 36 | AllNodeScan *op = (AllNodeScan*)opBase; 37 | 38 | if(raxEOF(&op->iter)) { 39 | return OP_DEPLETED; 40 | } 41 | 42 | char *id; 43 | uint16_t idLen; 44 | Node *node; 45 | int res = LabelStoreIterator_Next(&op->iter, &id, &idLen, (void**)&node); 46 | 47 | if(res == 0) { 48 | return OP_DEPLETED; 49 | } 50 | 51 | /* Update node */ 52 | *op->node = node; 53 | 54 | return OP_OK; 55 | } 56 | 57 | OpResult AllNodeScanReset(OpBase *op) { 58 | AllNodeScan *allNodeScan = (AllNodeScan*)op; 59 | 60 | *allNodeScan->node = allNodeScan->_node; 61 | LabelStoreIterator_Free(&allNodeScan->iter); 62 | 63 | LabelStore_Scan(allNodeScan->store, &allNodeScan->iter); 64 | return OP_OK; 65 | } 66 | 67 | void AllNodeScanFree(OpBase *ctx) { 68 | AllNodeScan *allNodeScan = (AllNodeScan *)ctx; 69 | LabelStoreIterator_Free(&allNodeScan->iter); 70 | free(allNodeScan); 71 | } -------------------------------------------------------------------------------- /src/execution_plan/ops/op_all_node_scan.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_ALL_NODE_SCAN_H__ 2 | #define __OP_ALL_NODE_SCAN_H__ 3 | 4 | #include "op.h" 5 | #include "../../redismodule.h" 6 | #include "../../graph/graph.h" 7 | #include "../../graph/node.h" 8 | #include "../../stores/store.h" 9 | 10 | /* AllNodesScan 11 | * Scans entire graph 12 | * Sets node id to current element */ 13 | 14 | typedef struct { 15 | OpBase op; 16 | Node **node; /* node being scanned */ 17 | Node *_node; 18 | LabelStore *store; /* store being scanned */ 19 | RedisModuleCtx *ctx; /* redis module API context */ 20 | const char *graph; /* queried graph id */ 21 | LabelStoreIterator iter; /* graph iterator */ 22 | } AllNodeScan; 23 | 24 | OpBase* NewAllNodeScanOp(RedisModuleCtx *ctx, Graph *g, Node **n, const char *graph_name); 25 | AllNodeScan* NewAllNodeScan(RedisModuleCtx *ctx, Graph *g, Node **n, const char *graph_name); 26 | OpResult AllNodeScanConsume(OpBase *opBase, Graph* graph); 27 | OpResult AllNodeScanReset(OpBase *op); 28 | void AllNodeScanFree(OpBase *ctx); 29 | 30 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_create.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_CREATE_H 2 | #define __OP_CREATE_H 3 | 4 | #include "op.h" 5 | #include "../../graph/node.h" 6 | #include "../../graph/edge.h" 7 | #include "../../resultset/resultset.h" 8 | /* Creats new entities according to the CREATE clause. */ 9 | 10 | typedef struct { 11 | Edge *original_edge; 12 | Edge **original_edge_ref; 13 | char *src_node_alias; 14 | char *dest_node_alias; 15 | } EdgeCreateCtx; 16 | 17 | typedef struct { 18 | Node *original_node; 19 | Node **original_node_ref; 20 | } NodeCreateCtx; 21 | 22 | typedef struct { 23 | OpBase op; 24 | RedisModuleCtx *ctx; 25 | int request_refresh; 26 | Graph *graph; 27 | const char *graph_name; 28 | 29 | NodeCreateCtx *nodes_to_create; 30 | size_t node_count; 31 | 32 | EdgeCreateCtx *edges_to_create; 33 | size_t edge_count; 34 | 35 | Vector *created_nodes; 36 | Vector *created_edges; 37 | ResultSet *result_set; 38 | } OpCreate; 39 | 40 | OpBase* NewCreateOp(RedisModuleCtx *ctx, Graph *graph, const char *graph_name, int request_refresh, ResultSet *result_set); 41 | OpCreate* NewCreate(RedisModuleCtx *ctx, Graph *graph, const char *graph_name, int request_refresh, ResultSet *result_set); 42 | 43 | OpResult OpCreateConsume(OpBase *opBase, Graph* graph); 44 | OpResult OpCreateReset(OpBase *ctx); 45 | void OpCreateFree(OpBase *ctx); 46 | 47 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_delete.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_DELETE_H 2 | #define __OP_DELETE_H 3 | 4 | #include "op.h" 5 | #include "../../graph/node.h" 6 | #include "../../graph/edge.h" 7 | #include "../../resultset/resultset.h" 8 | #include "../../dep/rax/rax.h" 9 | /* Delets entities specified within the DELETE clause. */ 10 | 11 | typedef struct { 12 | OpBase op; 13 | RedisModuleCtx *ctx; 14 | int request_refresh; 15 | const char *graph_name; 16 | 17 | Node ***nodes_to_delete; 18 | size_t node_count; 19 | 20 | Edge ***edges_to_delete; 21 | size_t edge_count; 22 | 23 | rax *deleted_nodes; 24 | rax *deleted_edges; 25 | ResultSet *result_set; 26 | } OpDelete; 27 | 28 | OpBase* NewDeleteOp(RedisModuleCtx *ctx, AST_DeleteNode *ast_delete_node, Graph *graph, const char *graph_name, ResultSet *result_set); 29 | OpDelete* _NewDeleteOp(RedisModuleCtx *ctx, AST_DeleteNode *ast_delete_node, Graph *graph, const char *graph_name, ResultSet *result_set); 30 | 31 | OpResult OpDeleteConsume(OpBase *opBase, Graph* graph); 32 | OpResult OpDeleteReset(OpBase *ctx); 33 | void OpDeleteFree(OpBase *ctx); 34 | 35 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_expand_all.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_EXPAND_ALL_H 2 | #define __OP_EXPAND_ALL_H 3 | 4 | #include "op.h" 5 | #include "../../rmutil/sds.h" 6 | #include "../../hexastore/triplet.h" 7 | 8 | 9 | /* ExpandAllStates 10 | * Different states in which ExpandAll can be at. */ 11 | typedef enum { 12 | ExpandAllUninitialized, /* ExpandAll wasn't initialized it. */ 13 | ExpandAllResetted, /* ExpandAll was just restarted. */ 14 | ExpandAllConsuming, /* ExpandAll consuming data. */ 15 | } ExpandAllStates; 16 | 17 | /* ExpandAll 18 | * Expands entire graph, 19 | * Each node within the graph will be set */ 20 | typedef struct { 21 | OpBase op; 22 | Node **src_node; /* Source node to expand. */ 23 | Node *_src_node; /* Original source node to expand. */ 24 | Node **dest_node; /* Destination node to expand. */ 25 | Node *_dest_node; /* Original destination node to expand. */ 26 | Edge **relation; /* Edge to expand. */ 27 | Edge *_relation; /* Original edge to expand. */ 28 | RedisModuleCtx *ctx; /* Redis context. */ 29 | HexaStore *hexastore; /* Graph store. */ 30 | Triplet *triplet; 31 | Triplet modifies; /* Which entities does this operation modifies. */ 32 | sds str_triplet; /* String representation of current triplet. */ 33 | TripletIterator iter; /* Graph iterator. */ 34 | ExpandAllStates state; /* Operation current state. */ 35 | } ExpandAll; 36 | 37 | /* Creates a new ExpandAll operation */ 38 | OpBase* NewExpandAllOp(RedisModuleCtx *ctx, Graph *g, const char *graph_name, 39 | Node **src_node, Edge **relation, Node **dest_node); 40 | 41 | ExpandAll* NewExpandAll(RedisModuleCtx *ctx, Graph *g, const char *graph_name, 42 | Node **src_node, Edge **relation, Node **dest_node); 43 | 44 | /* ExpandAllConsume next operation 45 | * each call will update the graph 46 | * returns OP_DEPLETED when no additional updates are available */ 47 | OpResult ExpandAllConsume(OpBase *opBase, Graph* graph); 48 | 49 | /* Restart iterator */ 50 | OpResult ExpandAllReset(OpBase *ctx); 51 | 52 | /* Frees ExpAndAll*/ 53 | void ExpandAllFree(OpBase *ctx); 54 | 55 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_expand_into.c: -------------------------------------------------------------------------------- 1 | #include "op_expand_into.h" 2 | 3 | void NewExpandIntoOp(RedisModuleCtx *ctx, Graph *g, const char *graph_name, Node **src_node, 4 | Edge **relation, Node **dest_node, OpBase **op) { 5 | *op = (OpBase *)NewExpandInto(ctx, g, graph_name, src_node, relation, dest_node); 6 | } 7 | 8 | ExpandInto* NewExpandInto(RedisModuleCtx *ctx, Graph *g, const char *graph_name, Node **src_node, 9 | Edge **relation, Node **dest_node) { 10 | ExpandInto *expandInto = calloc(1, sizeof(ExpandInto)); 11 | 12 | expandInto->src_node = src_node; 13 | expandInto->dest_node = dest_node; 14 | expandInto->relation = relation; 15 | expandInto->_relation = *relation; 16 | expandInto->refreshAfterPass = 0; 17 | expandInto->ctx = ctx; 18 | expandInto->str_triplet = sdsempty(); 19 | expandInto->hexastore = GetHexaStore(ctx, graph_name); 20 | HexaStore_Search(expandInto->hexastore, "", &expandInto->iter); 21 | 22 | // Set our Op operations 23 | expandInto->op.name = "Expand Into"; 24 | expandInto->op.type = OPType_EXPAND_INTO; 25 | expandInto->op.consume = ExpandIntoConsume; 26 | expandInto->op.reset = ExpandIntoReset; 27 | expandInto->op.free = ExpandIntoFree; 28 | expandInto->op.modifies = NewVector(char*, 1); 29 | 30 | Vector_Push(expandInto->op.modifies, Graph_GetEdgeAlias(g, *relation)); 31 | 32 | return expandInto; 33 | } 34 | 35 | OpResult ExpandIntoConsume(OpBase *opBase, Graph* graph) { 36 | ExpandInto *op = (ExpandInto*)opBase; 37 | 38 | /* Both src and dest nodes must have an ID. */ 39 | if( (*(op->src_node))->id == INVALID_ENTITY_ID || 40 | (*(op->dest_node))->id == INVALID_ENTITY_ID) return OP_REFRESH; 41 | 42 | /* Request data refresh. */ 43 | if(op->refreshAfterPass) { 44 | op->refreshAfterPass = 0; 45 | *op->relation = op->_relation; 46 | return OP_REFRESH; 47 | } 48 | 49 | Triplet t = {.subject = *(op->src_node), 50 | .predicate = *(op->relation), 51 | .object = *(op->dest_node)}; 52 | 53 | t.kind = SO; 54 | 55 | /* Overrides current value with triplet string representation, 56 | * If string buffer is large enough, there will be no allocation. */ 57 | TripletToString(&t, &op->str_triplet); 58 | 59 | /* Search hexastore, reuse iterator. */ 60 | HexaStore_Search_Iterator(op->hexastore, op->str_triplet, &op->iter); 61 | 62 | /* No connection between src and dest. */ 63 | Triplet *triplet = NULL; 64 | if(!TripletIterator_Next(&op->iter, &triplet)) return OP_REFRESH; 65 | 66 | /* Update graph. */ 67 | *op->relation = triplet->predicate; 68 | 69 | /* Connection found, 70 | * Require a data refresh next time we're called */ 71 | op->refreshAfterPass = 1; 72 | return OP_OK; 73 | } 74 | 75 | /* Simply returns OP_OK */ 76 | OpResult ExpandIntoReset(OpBase *ctx) { 77 | ExpandInto *op = (ExpandInto*)ctx; 78 | /* Restore original relation. */ 79 | *op->relation = op->_relation; 80 | return OP_OK; 81 | } 82 | 83 | /* Frees ExpandInto*/ 84 | void ExpandIntoFree(OpBase *ctx) { 85 | ExpandInto *op = (ExpandInto*)ctx; 86 | free(op); 87 | } 88 | -------------------------------------------------------------------------------- /src/execution_plan/ops/op_expand_into.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_EXPAND_INTO_H 2 | #define __OP_EXPAND_INTO_H 3 | 4 | #include "op.h" 5 | #include "../../rmutil/sds.h" 6 | #include "../../hexastore/hexastore.h" 7 | 8 | /* ExpandInto checks to see if 9 | * there's a connection between source and destination 10 | * nodes, it does not changes node's ID. */ 11 | typedef struct { 12 | OpBase op; 13 | Node **src_node; 14 | Node **dest_node; 15 | Edge **relation; /* Type of relation we're looking for to exists between nodes. */ 16 | Edge *_relation; 17 | sds str_triplet; 18 | int refreshAfterPass; 19 | RedisModuleCtx *ctx; 20 | HexaStore *hexastore; 21 | TripletIterator iter; /* Graph iterator. */ 22 | } ExpandInto; 23 | 24 | /* Creates a new ExpandInto operation */ 25 | void NewExpandIntoOp(RedisModuleCtx *ctx, Graph *g, const char *graph_name, Node **src_node, 26 | Edge **relation, Node **dest_node, OpBase **op); 27 | 28 | ExpandInto* NewExpandInto(RedisModuleCtx *ctx, Graph *g, const char *graph_name, Node **src_node, 29 | Edge **relation, Node **dest_node); 30 | 31 | /* ExpandIntoConsume next operation 32 | * Returns OP_OK if there's a connection between source and dest nodes 33 | * OP_DEPLETED is returned when there's no more data to work with. */ 34 | OpResult ExpandIntoConsume(OpBase *opBase, Graph* graph); 35 | 36 | /* Simply returns OP_OK */ 37 | OpResult ExpandIntoReset(OpBase *ctx); 38 | 39 | /* Frees ExpandInto*/ 40 | void ExpandIntoFree(OpBase *ctx); 41 | 42 | #endif // __OP_EXPAND_INTO_H -------------------------------------------------------------------------------- /src/execution_plan/ops/op_filter.c: -------------------------------------------------------------------------------- 1 | #include "op_filter.h" 2 | 3 | OpBase* NewFilterOp(FT_FilterNode *filterTree) { 4 | return (OpBase*)NewFilter(filterTree); 5 | } 6 | 7 | Filter* NewFilter(FT_FilterNode *filterTree) { 8 | Filter *filter = malloc(sizeof(Filter)); 9 | filter->filterTree = filterTree; 10 | filter->state = FilterUninitialized; 11 | 12 | // Set our Op operations 13 | filter->op.name = "Filter"; 14 | filter->op.type = OPType_FILTER; 15 | filter->op.consume = FilterConsume; 16 | filter->op.reset = FilterReset; 17 | filter->op.free = FilterFree; 18 | filter->op.modifies = NULL; 19 | 20 | return filter; 21 | } 22 | 23 | /* FilterConsume next operation 24 | * returns OP_OK when graph passes filter tree. */ 25 | OpResult FilterConsume(OpBase *opBase, Graph* graph) { 26 | Filter *filter = (Filter*)opBase; 27 | 28 | if(filter->state == FilterUninitialized || filter->state == FilterRequestRefresh) { 29 | return OP_REFRESH; 30 | } 31 | 32 | /* Pass graph through filter tree */ 33 | int pass = applyFilters(graph, filter->filterTree); 34 | 35 | filter->state = FilterRequestRefresh; 36 | 37 | /* Incase graph fails to pass filter, request new data. */ 38 | if(pass != FILTER_PASS) return OP_REFRESH; 39 | 40 | return OP_OK; 41 | } 42 | 43 | /* Restart iterator */ 44 | OpResult FilterReset(OpBase *ctx) { 45 | Filter *filter = (Filter*)ctx; 46 | filter->state = FilterResetted; 47 | return OP_OK; 48 | } 49 | 50 | /* Frees Filter*/ 51 | void FilterFree(OpBase *ctx) { 52 | Filter *filter = (Filter*)ctx; 53 | FilterTree_Free(filter->filterTree); 54 | free(filter); 55 | } -------------------------------------------------------------------------------- /src/execution_plan/ops/op_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef _FILTER_H_ 2 | #define _FILTER_H_ 3 | 4 | #include "op.h" 5 | #include "../../filter_tree/filter_tree.h" 6 | 7 | /* FilterState 8 | * Different states in which ExpandAll can be at. */ 9 | typedef enum { 10 | FilterUninitialized, /* Filter wasn't initialized it. */ 11 | FilterRequestRefresh, /* */ 12 | FilterResetted, /* Filter was just restarted. */ 13 | } FilterState; 14 | 15 | /* Filter 16 | * filters graph according to where cluase */ 17 | typedef struct { 18 | OpBase op; 19 | FT_FilterNode *filterTree; 20 | FilterState state; 21 | } Filter; 22 | 23 | /* Creates a new Filter operation */ 24 | OpBase* NewFilterOp(FT_FilterNode *filterTree); 25 | Filter* NewFilter(FT_FilterNode *filterTree); 26 | 27 | /* FilterConsume next operation 28 | * returns OP_DEPLETED when */ 29 | OpResult FilterConsume(OpBase *opBase, Graph* graph); 30 | 31 | /* Restart iterator */ 32 | OpResult FilterReset(OpBase *ctx); 33 | 34 | /* Frees Filter*/ 35 | void FilterFree(OpBase *ctx); 36 | 37 | #endif //_FILTER_H_ -------------------------------------------------------------------------------- /src/execution_plan/ops/op_index_scan.c: -------------------------------------------------------------------------------- 1 | #include "op_index_scan.h" 2 | 3 | OpBase *NewIndexScanOp(Graph *g, Node **node, IndexIterator *iter) { 4 | return (OpBase*)NewIndexScan(g, node, iter); 5 | } 6 | 7 | IndexScan* NewIndexScan(Graph *g, Node **node, IndexIterator *iter) { 8 | 9 | IndexScan *indexScan = malloc(sizeof(IndexScan)); 10 | indexScan->node = node; 11 | indexScan->_node = *node; 12 | indexScan->iter = iter; 13 | 14 | // Set our Op operations 15 | indexScan->op.name = "Index Scan"; 16 | indexScan->op.type = OpType_INDEX_SCAN; 17 | indexScan->op.consume = IndexScanConsume; 18 | indexScan->op.reset = IndexScanReset; 19 | indexScan->op.free = IndexScanFree; 20 | indexScan->op.modifies = NewVector(char*, 1); 21 | 22 | Vector_Push(indexScan->op.modifies, Graph_GetNodeAlias(g, *node)); 23 | 24 | return indexScan; 25 | } 26 | 27 | OpResult IndexScanConsume(OpBase *opBase, Graph* graph) { 28 | IndexScan *op = (IndexScan*)opBase; 29 | 30 | /* Update node */ 31 | *op->node = IndexIterator_Next(op->iter); 32 | 33 | if(*op->node == NULL) { 34 | return OP_DEPLETED; 35 | } 36 | 37 | return OP_OK; 38 | } 39 | 40 | OpResult IndexScanReset(OpBase *ctx) { 41 | IndexScan *indexScan = (IndexScan*)ctx; 42 | 43 | /* Restore original node. */ 44 | *indexScan->node = indexScan->_node; 45 | IndexIterator_Reset(indexScan->iter); 46 | 47 | return OP_OK; 48 | } 49 | 50 | void IndexScanFree(OpBase *op) { 51 | IndexScan *indexScan = (IndexScan *)op; 52 | IndexIterator_Free(indexScan->iter); 53 | free(indexScan); 54 | } -------------------------------------------------------------------------------- /src/execution_plan/ops/op_index_scan.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_INDEX_SCAN_H 2 | #define __OP_INDEX_SCAN_H 3 | 4 | #include "op.h" 5 | #include "../../graph/graph.h" 6 | #include "../../graph/node.h" 7 | #include "../../index/index.h" 8 | 9 | 10 | typedef struct { 11 | OpBase op; 12 | Node **node; /* node being scanned */ 13 | Node *_node; 14 | IndexIterator *iter; 15 | } IndexScan; 16 | 17 | /* Creates a new IndexScan operation */ 18 | OpBase *NewIndexScanOp(Graph *g, Node **node, IndexIterator *iter); 19 | IndexScan* NewIndexScan(Graph *g, Node **node, IndexIterator *iter); 20 | 21 | /* IndexScan next operation 22 | * called each time a new ID is required */ 23 | OpResult IndexScanConsume(OpBase *opBase, Graph* graph); 24 | 25 | /* Restart iterator */ 26 | OpResult IndexScanReset(OpBase *ctx); 27 | 28 | /* Frees IndexScan */ 29 | void IndexScanFree(OpBase *ctx); 30 | 31 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_node_by_label_scan.c: -------------------------------------------------------------------------------- 1 | #include "op_node_by_label_scan.h" 2 | 3 | OpBase *NewNodeByLabelScanOp(RedisModuleCtx *ctx, Graph *g, Node **node, 4 | const char *graph_name, char *label) { 5 | return (OpBase*)NewNodeByLabelScan(ctx, g, node, graph_name, label); 6 | } 7 | 8 | NodeByLabelScan* NewNodeByLabelScan(RedisModuleCtx *ctx, Graph *g, Node **node, 9 | const char *graph_name, char *label) { 10 | // Get graph store 11 | LabelStore *store = LabelStore_Get(ctx, STORE_NODE, graph_name, label); 12 | if(store == NULL) { 13 | return NULL; 14 | } 15 | 16 | NodeByLabelScan *nodeByLabelScan = malloc(sizeof(NodeByLabelScan)); 17 | nodeByLabelScan->ctx = ctx; 18 | nodeByLabelScan->node = node; 19 | nodeByLabelScan->_node = *node; 20 | nodeByLabelScan->graph = graph_name; 21 | nodeByLabelScan->store = store; 22 | LabelStore_Scan(store, &nodeByLabelScan->iter); 23 | 24 | 25 | // Set our Op operations 26 | nodeByLabelScan->op.name = "Node By Label Scan"; 27 | nodeByLabelScan->op.type = OPType_NODE_BY_LABEL_SCAN; 28 | nodeByLabelScan->op.consume = NodeByLabelScanConsume; 29 | nodeByLabelScan->op.reset = NodeByLabelScanReset; 30 | nodeByLabelScan->op.free = NodeByLabelScanFree; 31 | nodeByLabelScan->op.modifies = NewVector(char*, 1); 32 | 33 | Vector_Push(nodeByLabelScan->op.modifies, Graph_GetNodeAlias(g, *node)); 34 | 35 | return nodeByLabelScan; 36 | } 37 | 38 | OpResult NodeByLabelScanConsume(OpBase *opBase, Graph* graph) { 39 | NodeByLabelScan *op = (NodeByLabelScan*)opBase; 40 | 41 | if(raxEOF(&op->iter)) return OP_DEPLETED; 42 | 43 | char *id; 44 | uint16_t idLen; 45 | 46 | /* Update node */ 47 | Node **n = op->node; 48 | int res = LabelStoreIterator_Next(&op->iter, &id, &idLen, (void**)op->node); 49 | 50 | if(res == 0) { 51 | return OP_DEPLETED; 52 | } 53 | 54 | return OP_OK; 55 | } 56 | 57 | OpResult NodeByLabelScanReset(OpBase *ctx) { 58 | NodeByLabelScan *nodeByLabelScan = (NodeByLabelScan*)ctx; 59 | 60 | /* Restore original node. */ 61 | *nodeByLabelScan->node = nodeByLabelScan->_node; 62 | 63 | LabelStoreIterator_Free(&nodeByLabelScan->iter); 64 | 65 | LabelStore_Scan(nodeByLabelScan->store, &nodeByLabelScan->iter); 66 | return OP_OK; 67 | } 68 | 69 | void NodeByLabelScanFree(OpBase *op) { 70 | NodeByLabelScan *nodeByLabelScan = (NodeByLabelScan*)op; 71 | LabelStoreIterator_Free(&nodeByLabelScan->iter); 72 | free(nodeByLabelScan); 73 | } -------------------------------------------------------------------------------- /src/execution_plan/ops/op_node_by_label_scan.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_NODE_BY_LABEL_SCAN_H 2 | #define __OP_NODE_BY_LABEL_SCAN_H 3 | 4 | #include "op.h" 5 | #include "../../redismodule.h" 6 | #include "../../graph/graph.h" 7 | #include "../../graph/node.h" 8 | #include "../../stores/store.h" 9 | /* NodeByLabelScan 10 | * Scans entire label-store 11 | * Sets node id to current element within 12 | * the label store */ 13 | 14 | typedef struct { 15 | OpBase op; 16 | Node **node; /* node being scanned */ 17 | Node *_node; 18 | LabelStore *store; 19 | RedisModuleCtx *ctx; 20 | const char *graph; /* queried graph id */ 21 | LabelStoreIterator iter; 22 | } NodeByLabelScan; 23 | 24 | /* Creates a new NodeByLabelScan operation */ 25 | OpBase *NewNodeByLabelScanOp(RedisModuleCtx *ctx, Graph *g, Node **node, 26 | const char *graph_name, char *label); 27 | 28 | NodeByLabelScan* NewNodeByLabelScan(RedisModuleCtx *ctx, Graph *g, Node **node, 29 | const char *graph_name, char *label); 30 | 31 | /* NodeByLabelScan next operation 32 | * called each time a new ID is required */ 33 | OpResult NodeByLabelScanConsume(OpBase *opBase, Graph* graph); 34 | 35 | /* Restart iterator */ 36 | OpResult NodeByLabelScanReset(OpBase *ctx); 37 | 38 | /* Frees NodeByLabelScan */ 39 | void NodeByLabelScanFree(OpBase *ctx); 40 | 41 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_produce_results.c: -------------------------------------------------------------------------------- 1 | #include "op_produce_results.h" 2 | #include "../../resultset/record.h" 3 | #include "../../arithmetic/arithmetic_expression.h" 4 | #include "../../query_executor.h" 5 | 6 | OpBase* NewProduceResultsOp(RedisModuleCtx *ctx, AST_Query *ast, ResultSet *result_set) { 7 | // *op = (OpBase *)NewProduceResults(ctx, ast, result_set); 8 | return (OpBase*)NewProduceResults(ctx, ast, result_set); 9 | } 10 | 11 | ProduceResults* NewProduceResults(RedisModuleCtx *ctx, AST_Query *ast, ResultSet *result_set) { 12 | ProduceResults *produceResults = malloc(sizeof(ProduceResults)); 13 | produceResults->ctx = ctx; 14 | produceResults->ast = ast; 15 | produceResults->result_set = result_set; 16 | produceResults->refreshAfterPass = 0; 17 | produceResults->return_elements = NULL; 18 | produceResults->init = 0; 19 | 20 | // Set our Op operations 21 | produceResults->op.name = "Produce Results"; 22 | produceResults->op.type = OPType_PRODUCE_RESULTS; 23 | produceResults->op.consume = ProduceResultsConsume; 24 | produceResults->op.reset = ProduceResultsReset; 25 | produceResults->op.free = ProduceResultsFree; 26 | produceResults->op.modifies = NULL; 27 | return produceResults; 28 | } 29 | 30 | /* Construct arithmetic expressions from return clause. */ 31 | void _BuildArithmeticExpressions(ProduceResults* op, AST_ReturnNode *return_node, Graph* graph) { 32 | op->return_elements = NewVector(AR_ExpNode*, Vector_Size(return_node->returnElements)); 33 | 34 | for(int i = 0; i < Vector_Size(return_node->returnElements); i++) { 35 | AST_ReturnElementNode *ret_node; 36 | Vector_Get(return_node->returnElements, i, &ret_node); 37 | 38 | AR_ExpNode *ae = AR_EXP_BuildFromAST(ret_node->exp, graph); 39 | Vector_Push(op->return_elements, ae); 40 | } 41 | } 42 | 43 | Record *_ProduceResultsetRecord(ProduceResults* op) { 44 | Record *r = NewRecord(Vector_Size(op->return_elements)); 45 | for(int i = 0; i < Vector_Size(op->return_elements); i++) { 46 | AR_ExpNode *ae; 47 | Vector_Get(op->return_elements, i, &ae); 48 | r->values[i] = AR_EXP_Evaluate(ae); 49 | } 50 | return r; 51 | } 52 | 53 | /* ProduceResults next operation 54 | * called each time a new result record is required */ 55 | OpResult ProduceResultsConsume(OpBase *opBase, Graph* graph) { 56 | ProduceResults *op = (ProduceResults*)opBase; 57 | 58 | if(!op->init) { 59 | _BuildArithmeticExpressions(op, op->ast->returnNode, graph); 60 | op->init = 1; 61 | return OP_REFRESH; 62 | } 63 | 64 | if(op->refreshAfterPass == 1) { 65 | op->refreshAfterPass = 0; 66 | return OP_REFRESH; 67 | } 68 | 69 | /* Append to final result set. */ 70 | Record *r = _ProduceResultsetRecord(op); 71 | if(ResultSet_AddRecord(op->result_set, r) == RESULTSET_FULL) { 72 | return OP_ERR; 73 | } 74 | 75 | /* Request data refresh next time consume is called. */ 76 | op->refreshAfterPass = 1; 77 | return OP_OK; 78 | } 79 | 80 | /* Restart */ 81 | OpResult ProduceResultsReset(OpBase *op) { 82 | return OP_OK; 83 | } 84 | 85 | /* Frees ProduceResults */ 86 | void ProduceResultsFree(OpBase *op) { 87 | if(op != NULL) { 88 | free(op); 89 | } 90 | } -------------------------------------------------------------------------------- /src/execution_plan/ops/op_produce_results.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_PRODUCE_RESULTS_H 2 | #define __OP_PRODUCE_RESULTS_H 3 | 4 | #include "op.h" 5 | #include "../../parser/ast.h" 6 | #include "../../redismodule.h" 7 | #include "../../graph/graph.h" 8 | #include "../../resultset/resultset.h" 9 | 10 | /* ProduceResults 11 | * generates result set */ 12 | 13 | typedef struct { 14 | OpBase op; 15 | RedisModuleCtx *ctx; 16 | int refreshAfterPass; 17 | AST_Query *ast; 18 | Vector *return_elements; /* Vector of arithmetic expressions. */ 19 | ResultSet *result_set; 20 | int init; 21 | } ProduceResults; 22 | 23 | 24 | /* Creates a new NodeByLabelScan operation */ 25 | // void NewProduceResultsOp(RedisModuleCtx *ctx, AST_Query *ast, ResultSet *resultset, OpBase **op); 26 | OpBase* NewProduceResultsOp(RedisModuleCtx *ctx, AST_Query *ast, ResultSet *result_set); 27 | ProduceResults* NewProduceResults(RedisModuleCtx *ctx, AST_Query *ast, ResultSet *resultset); 28 | 29 | /* ProduceResults next operation 30 | * called each time a new result record is required */ 31 | OpResult ProduceResultsConsume(OpBase *op, Graph* graph); 32 | 33 | /* Restart iterator */ 34 | OpResult ProduceResultsReset(OpBase *ctx); 35 | 36 | /* Frees ProduceResults */ 37 | void ProduceResultsFree(OpBase *ctx); 38 | 39 | #endif -------------------------------------------------------------------------------- /src/execution_plan/ops/op_update.h: -------------------------------------------------------------------------------- 1 | #ifndef __OP_UPDATE_H 2 | #define __OP_UPDATE_H 3 | 4 | #include "op.h" 5 | #include "../../graph/node.h" 6 | #include "../../graph/edge.h" 7 | #include "../../resultset/resultset.h" 8 | #include "../../arithmetic/arithmetic_expression.h" 9 | 10 | typedef struct { 11 | GraphEntity **entity; /* Entity to update. */ 12 | char *property; /* Property to update. */ 13 | AR_ExpNode *exp; /* Expression to evaluate. */ 14 | } EntityUpdateEvalCtx; 15 | 16 | typedef struct { 17 | EntityProperty *dest_entity_prop; /* Entity's property to update. */ 18 | SIValue new_value; /* Constant value to set. */ 19 | } EntityUpdateCtx; 20 | 21 | typedef struct { 22 | OpBase op; 23 | int request_refresh; 24 | ResultSet *result_set; 25 | EntityUpdateEvalCtx *update_expressions; /* List of entities to update and their arithmetic expressions. */ 26 | size_t update_expressions_count; 27 | EntityUpdateCtx *entities_to_update; /* List of entities to update and their actual new value. */ 28 | size_t entities_to_update_cap; 29 | size_t entities_to_update_count; 30 | } OpUpdate; 31 | 32 | OpBase* NewUpdateOp(AST_SetNode *ast, Graph *graph, ResultSet *result_set); 33 | OpResult OpUpdateConsume(OpBase *opBase, Graph* graph); 34 | OpResult OpUpdateReset(OpBase *ctx); 35 | void OpUpdateFree(OpBase *ctx); 36 | 37 | #endif /* __OP_UPDATE_H */ -------------------------------------------------------------------------------- /src/execution_plan/ops/ops.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPS_H_ 2 | #define __OPS_H_ 3 | 4 | /* Include all available execution plan operations. */ 5 | #include "op_aggregate.h" 6 | #include "op_all_node_scan.h" 7 | #include "op_create.h" 8 | #include "op_delete.h" 9 | #include "op_expand_all.h" 10 | #include "op_expand_into.h" 11 | #include "op_filter.h" 12 | #include "op_node_by_label_scan.h" 13 | #include "op_index_scan.h" 14 | #include "op_produce_results.h" 15 | #include "op_update.h" 16 | 17 | #endif -------------------------------------------------------------------------------- /src/filter_tree/filter_tree.h: -------------------------------------------------------------------------------- 1 | #ifndef _FILTER_TREE_H 2 | #define _FILTER_TREE_H 3 | 4 | #include "../value_cmp.h" 5 | #include "../parser/ast.h" 6 | #include "../graph/graph.h" 7 | #include "../redismodule.h" 8 | 9 | #define FILTER_FAIL 0 10 | #define FILTER_PASS 1 11 | /* Nodes within the filter tree are one of two types 12 | * Either a predicate node or a condition node. */ 13 | typedef enum { 14 | FT_N_PRED, 15 | FT_N_COND, 16 | } FT_FilterNodeType; 17 | 18 | typedef enum { 19 | FT_N_CONSTANT, 20 | FT_N_VARYING, 21 | } FT_CompareValueType; 22 | 23 | struct FT_FilterNode; 24 | 25 | typedef struct { 26 | struct { /* Left side of predicate. */ 27 | char* alias; /* Element in question alias. */ 28 | char* property; /* Element's property to check. */ 29 | } Lop; 30 | int op; /* Operation (<, <=, =, =>, >, !). */ 31 | union { /* Right side of predicate. */ 32 | SIValue constVal; /* Value to compare against. */ 33 | struct { 34 | char* alias; 35 | char* property; 36 | } Rop; 37 | }; 38 | FT_CompareValueType t; /* Compared value type, constant/node. */ 39 | CmpFunc cf; /* Compare function, determines relation between val and element property. */ 40 | } FT_PredicateNode; 41 | 42 | typedef struct { 43 | struct FT_FilterNode *left; 44 | struct FT_FilterNode *right; 45 | int op; /* OR, AND */ 46 | } FT_ConditionNode; 47 | 48 | /* All nodes within the filter tree are of type FT_FilterNode. */ 49 | struct FT_FilterNode { 50 | union { 51 | FT_PredicateNode pred; 52 | FT_ConditionNode cond; 53 | }; 54 | FT_FilterNodeType t; /* Determines actual type of this node. */ 55 | }; 56 | 57 | typedef struct FT_FilterNode FT_FilterNode; 58 | 59 | /* Given AST's WHERE subtree constructs a filter tree 60 | * This is done to speed up the filtering process. */ 61 | FT_FilterNode* BuildFiltersTree(const AST_FilterNode *root); 62 | 63 | FT_FilterNode* CreateVaryingFilterNode(const char *LAlias, const char *LProperty, const char *RAlias, const char *RProperty, int op); 64 | FT_FilterNode* CreateConstFilterNode(const char *alias, const char *property, int op, SIValue val); 65 | FT_FilterNode* CreateCondFilterNode(int op); 66 | 67 | FT_FilterNode *AppendLeftChild(FT_FilterNode *root, FT_FilterNode *child); 68 | FT_FilterNode *AppendRightChild(FT_FilterNode *root, FT_FilterNode *child); 69 | 70 | /* Runs val through the filter tree. */ 71 | int applyFilters(const Graph* g, const FT_FilterNode* root); 72 | 73 | /* Checks to see if aliased node is within the filter tree. */ 74 | int FilterTree_ContainsNode(const FT_FilterNode *root, const Vector *aliases); 75 | 76 | /* Builds Vector of constant filters associated with specific alias */ 77 | Vector* FilterTree_CollectAliasConsts(const FT_FilterNode *root, const char *alias); 78 | 79 | /* Clones given tree */ 80 | void FilterTree_Clone(const FT_FilterNode *root, FT_FilterNode **clone); 81 | 82 | /* Prints tree. */ 83 | void FilterTree_Print(const FT_FilterNode *root); 84 | 85 | void FilterTree_RemoveAllNodesExcept(FT_FilterNode **root, Vector *aliases); 86 | void FilterTree_RemovePredNodes(FT_FilterNode **root, const Vector *aliases); 87 | void FilterTree_Squash(FT_FilterNode **root); 88 | FT_FilterNode* FilterTree_MinFilterTree(FT_FilterNode *root, Vector *aliases); 89 | 90 | void FilterTree_Free(FT_FilterNode *root); 91 | 92 | #endif // _FILTER_TREE_H -------------------------------------------------------------------------------- /src/graph/edge.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "edge.h" 6 | #include "graph_entity.h" 7 | 8 | Edge* NewEdge(long int id, Node *src, Node *dest, const char *relationship) { 9 | assert(src && dest); 10 | 11 | Edge* edge = (Edge*)calloc(1, sizeof(Edge)); 12 | edge->id = id; 13 | edge->src = src; 14 | edge->dest = dest; 15 | edge->prop_count = 0; 16 | 17 | if(relationship != NULL) { 18 | edge->relationship = strdup(relationship); 19 | } 20 | 21 | return edge; 22 | } 23 | 24 | void Edge_Add_Properties(Edge *edge, int prop_count, char **keys, SIValue *values) { 25 | GraphEntity_Add_Properties((GraphEntity*)edge, prop_count, keys, values); 26 | } 27 | 28 | SIValue* Edge_Get_Property(const Edge *edge, const char* key) { 29 | return GraphEntity_Get_Property((GraphEntity*)edge, key); 30 | } 31 | 32 | void FreeEdge(Edge* edge) { 33 | FreeGraphEntity((GraphEntity*)edge); 34 | 35 | if(edge->relationship != NULL) { 36 | free(edge->relationship); 37 | } 38 | 39 | free(edge); 40 | } -------------------------------------------------------------------------------- /src/graph/edge.h: -------------------------------------------------------------------------------- 1 | #ifndef EDGE_H_ 2 | #define EDGE_H_ 3 | 4 | #include "graph_entity.h" 5 | #include "node.h" 6 | #include "../value.h" 7 | 8 | struct Edge { 9 | struct { 10 | long int id; 11 | int prop_count; 12 | EntityProperty *properties; 13 | }; 14 | char* relationship; 15 | Node* src; 16 | Node* dest; 17 | }; 18 | 19 | typedef struct Edge Edge; 20 | 21 | /* Creates a new edge, connecting src to dest node. */ 22 | Edge* NewEdge(long int id, Node *src, Node *dest, const char *relationship); 23 | 24 | /* Adds a properties to node 25 | * propCount - number of new properties to add 26 | * keys - array of properties keys 27 | * values - array of properties values */ 28 | void Edge_Add_Properties(Edge *edge, int propCount, char **keys, SIValue *values); 29 | 30 | /* Retrieves edge's property 31 | * NOTE: If the key does not exist, we return the special 32 | * constant value Edge_PROPERTY_NOTFOUND. */ 33 | SIValue* Edge_Get_Property(const Edge *edge, const char *key); 34 | 35 | // Frees alocated space by given edge 36 | void FreeEdge(Edge *edge); 37 | 38 | #endif -------------------------------------------------------------------------------- /src/graph/graph.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPH_H_ 2 | #define GRAPH_H_ 3 | 4 | #include "node.h" 5 | #include "edge.h" 6 | #include "../rmutil/vector.h" 7 | #include "../hexastore/hexastore.h" 8 | 9 | #define DEFAULT_GRAPH_CAP 32 /* Number of edges/nodes within the graph. */ 10 | 11 | typedef struct { 12 | Node **nodes; 13 | Edge **edges; 14 | char **node_aliases; 15 | char **edge_aliases; 16 | size_t node_count; 17 | size_t edge_count; 18 | size_t node_cap; 19 | size_t edge_cap; 20 | } Graph; 21 | 22 | Graph* NewGraph(); 23 | Graph* NewGraph_WithCapacity(size_t node_cap, size_t edge_cap); 24 | 25 | /* Checks if graph contains given node 26 | * Returns 1 if so, 0 otherwise */ 27 | int Graph_ContainsNode(const Graph *graph, const Node *node); 28 | 29 | int Graph_ContainsEdge(const Graph *graph, const Edge *edge); 30 | 31 | /* Retrieves node from graph */ 32 | Node* Graph_GetNodeById(const Graph *g, long int id); 33 | 34 | /* Retrieves node from graph */ 35 | Edge* Graph_GetEdgeById(const Graph *g, long int id); 36 | 37 | /* Search the graph for a node with given alias */ 38 | Node* Graph_GetNodeByAlias(const Graph *g, const char *alias); 39 | 40 | /* Search the graph for an edge with given alias */ 41 | Edge* Graph_GetEdgeByAlias(const Graph *g, const char *alias); 42 | 43 | /* Search for either node/edge with given alias. */ 44 | GraphEntity* Graph_GetEntityByAlias(const Graph *g, const char *alias); 45 | 46 | /* Adds a new node to the graph */ 47 | int Graph_AddNode(Graph* g, Node *n, char *alias); 48 | 49 | /* Adds a new edge to the graph */ 50 | void Graph_ConnectNodes(Graph *g, Node *src, Node *dest, Edge *e, char *edge_alias); 51 | 52 | /* Finds a node with given input degree */ 53 | Vector* Graph_GetNDegreeNodes(Graph *g, int degree); 54 | 55 | /* Looks up node's alias within the graph */ 56 | char* Graph_GetNodeAlias(const Graph *g, const Node *n); 57 | 58 | /* Looks up edge's alias within the graph */ 59 | char* Graph_GetEdgeAlias(const Graph *g, const Edge *e); 60 | 61 | GraphEntity** Graph_GetEntityRef(const Graph *g, const char *alias); 62 | Node** Graph_GetNodeRef(const Graph *g, const Node *n); 63 | Edge** Graph_GetEdgeRef(const Graph *g, const Edge *e); 64 | 65 | /* Frees entire graph */ 66 | void Graph_Free(Graph* g); 67 | 68 | #endif -------------------------------------------------------------------------------- /src/graph/graph_entity.c: -------------------------------------------------------------------------------- 1 | #include "graph.h" 2 | #include "graph_entity.h" 3 | 4 | SIValue *PROPERTY_NOTFOUND = &(SIValue){.intval = 0, .type = T_NULL}; 5 | 6 | /* Expecting e to be either *Node or *Edge */ 7 | void GraphEntity_Add_Properties(GraphEntity *e, int prop_count, char **keys, SIValue *values) { 8 | if(e->properties == NULL) { 9 | e->properties = malloc(sizeof(EntityProperty) * prop_count); 10 | } else { 11 | e->properties = realloc(e->properties, sizeof(EntityProperty) * (e->prop_count + prop_count)); 12 | } 13 | 14 | for(int i = 0; i < prop_count; i++) { 15 | e->properties[e->prop_count + i].name = keys[i]; 16 | e->properties[e->prop_count + i].value = values[i]; 17 | } 18 | 19 | e->prop_count += prop_count; 20 | } 21 | 22 | SIValue* GraphEntity_Get_Property(const GraphEntity *e, const char* key) { 23 | for(int i = 0; i < e->prop_count; i++) { 24 | if(strcmp(key, e->properties[i].name) == 0) { 25 | return &e->properties[i].value; 26 | } 27 | } 28 | return PROPERTY_NOTFOUND; 29 | } 30 | 31 | void GraphEntity_Update_Property(GraphEntity *e, const char *key, SIValue *value) { 32 | int found = -1; 33 | for(int i = 0; i < e->prop_count; i++) { 34 | if(!strcmp(key, e->properties[i].name)) { 35 | found = i; 36 | break; 37 | } 38 | } 39 | if (found >= 0) { 40 | // TODO Not calling SIValue_Free here will cause a memory leak if the value is a heap-allocated string 41 | // owned exclusively by this property, but if the string is shared or not a heap allocation, this call 42 | // will cause crashes. Revisit once the logic surrounding SIValue allocations is cemented. 43 | 44 | // SIValue_Free(&e->properties[found].value); 45 | e->properties[found].value = SI_Clone(*value); 46 | } else { 47 | char *new_key = strdup(key); 48 | GraphEntity_Add_Properties(e, 1, &new_key, value); 49 | } 50 | } 51 | 52 | void FreeGraphEntity(GraphEntity *e) { 53 | if(e->properties == NULL) { 54 | for(int i = 0; i < e->prop_count; i++) { 55 | free(e->properties[i].name); 56 | } 57 | free(e->properties); 58 | } 59 | } -------------------------------------------------------------------------------- /src/graph/graph_entity.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPH_ENTITY_H_ 2 | #define GRAPH_ENTITY_H_ 3 | 4 | #include "../value.h" 5 | #define INVALID_ENTITY_ID -1l 6 | 7 | 8 | SIValue *PROPERTY_NOTFOUND; 9 | 10 | typedef struct { 11 | char *name; 12 | SIValue value; 13 | } EntityProperty; 14 | 15 | typedef struct { 16 | long int id; /* unique id (might be empty) */ 17 | int prop_count; 18 | EntityProperty *properties; 19 | } GraphEntity; 20 | 21 | /* Adds a properties to entity 22 | * prop_count - number of new properties to add 23 | * keys - array of properties keys 24 | * values - array of properties values */ 25 | void GraphEntity_Add_Properties(GraphEntity *e, int prop_count, char **keys, SIValue *values); 26 | 27 | /* Retrieves entity's property 28 | * NOTE: If the key does not exist, we return the special 29 | * constant value PROPERTY_NOTFOUND. */ 30 | SIValue* GraphEntity_Get_Property(const GraphEntity *e, const char* key); 31 | 32 | void GraphEntity_Update_Property(GraphEntity *e, const char *key, SIValue *value); 33 | 34 | /* Release all memory allocated by entity */ 35 | void FreeGraphEntity(GraphEntity *e); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/graph/node.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "node.h" 4 | #include "edge.h" 5 | #include "assert.h" 6 | #include "graph_entity.h" 7 | 8 | Node* NewNode(long int id, const char *label) { 9 | Node* node = (Node*)calloc(1, sizeof(Node)); 10 | 11 | node->id = id; 12 | node->prop_count = 0; 13 | node->outgoing_edges = NewVector(Edge*, 0); 14 | node->incoming_edges = NewVector(Edge*, 0); 15 | 16 | if(label != NULL) { 17 | node->label = strdup(label); 18 | } 19 | 20 | return node; 21 | } 22 | 23 | int Node_Compare(const Node *a, const Node *b) { 24 | return a->id == b->id; 25 | } 26 | 27 | void Node_ConnectNode(Node* src, Node* dest, struct Edge* e) { 28 | // assert(src && dest && e->src == src && e->dest == dest); 29 | Vector_Push(src->outgoing_edges, e); 30 | Vector_Push(dest->incoming_edges, e); 31 | } 32 | 33 | int Node_IncomeDegree(const Node *n) { 34 | return Vector_Size(n->incoming_edges); 35 | } 36 | 37 | void Node_Add_Properties(Node *node, int prop_count, char **keys, SIValue *values) { 38 | GraphEntity_Add_Properties((GraphEntity*)node, prop_count, keys, values); 39 | } 40 | 41 | SIValue* Node_Get_Property(const Node *node, const char* key) { 42 | return GraphEntity_Get_Property((GraphEntity*)node, key); 43 | } 44 | 45 | void FreeNode(Node* node) { 46 | if(!node) return; 47 | 48 | FreeGraphEntity((GraphEntity*)node); 49 | 50 | if(node->label != NULL) { 51 | free(node->label); 52 | } 53 | 54 | Vector_Free(node->outgoing_edges); 55 | Vector_Free(node->incoming_edges); 56 | 57 | free(node); 58 | node = NULL; 59 | } -------------------------------------------------------------------------------- /src/graph/node.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_H_ 2 | #define NODE_H_ 3 | 4 | #include "graph_entity.h" 5 | #include "../value.h" 6 | #include "../rmutil/vector.h" 7 | 8 | /* Forward declaration of edge */ 9 | struct Edge; 10 | typedef struct { 11 | // GraphEntity entity; 12 | struct { 13 | long int id; 14 | int prop_count; 15 | EntityProperty *properties; 16 | }; 17 | char *label; /* label attached to node */ 18 | Vector* outgoing_edges; /* list of incoming edges (ME)<-(SRC) */ 19 | Vector* incoming_edges; /* list on outgoing edges (ME)->(DEST) */ 20 | } Node; 21 | 22 | /* Creates a new node. */ 23 | Node* NewNode(long int id, const char *label); 24 | 25 | /* Checks if nodes are "equal" */ 26 | int Node_Compare(const Node *a, const Node *b); 27 | 28 | /* Returns number of edges pointing into node */ 29 | int Node_IncomeDegree(const Node *n); 30 | 31 | /* Connects source node to destination node by edge */ 32 | void Node_ConnectNode(Node* src, Node* dest, struct Edge* e); 33 | 34 | /* Adds properties to node 35 | * prop_count - number of new properties to add 36 | * keys - array of properties keys 37 | * values - array of properties values */ 38 | void Node_Add_Properties(Node *node, int prop_count, char **keys, SIValue *values); 39 | 40 | /* Retrieves node's property 41 | * NOTE: If the key does not exist, we return the special 42 | * constant value PROPERTY_NOTFOUND. */ 43 | SIValue* Node_Get_Property(const Node *node, const char *key); 44 | 45 | /* Frees allocated space by given node. */ 46 | void FreeNode(Node* node); 47 | 48 | #endif -------------------------------------------------------------------------------- /src/grouping/group.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "group.h" 3 | #include "../redismodule.h" 4 | 5 | // Creates a new group 6 | // arguments specify group's key. 7 | Group* NewGroup(int key_count, SIValue* keys, Vector* funcs) { 8 | Group* g = malloc(sizeof(Group)); 9 | g->keys = keys; 10 | g->key_count = key_count; 11 | g->aggregationFunctions = funcs; 12 | return g; 13 | } 14 | 15 | void FreeGroup(Group* group) { 16 | if(group == NULL) return; 17 | 18 | /* TODO: Free keys. */ 19 | free(group); 20 | } -------------------------------------------------------------------------------- /src/grouping/group.h: -------------------------------------------------------------------------------- 1 | #ifndef GROUP_H_ 2 | #define GROUP_H_ 3 | 4 | #include "../rmutil/vector.h" 5 | #include "../arithmetic/agg_ctx.h" 6 | 7 | typedef struct { 8 | int key_count; 9 | SIValue* keys; 10 | Vector* aggregationFunctions; /* Vector of AR_ExpNode*, where the root is an aggregation function. */ 11 | } Group; 12 | 13 | /* Creates a new group */ 14 | Group* NewGroup(int key_count, SIValue* keys, Vector* funcs); 15 | 16 | void FreeGroup(Group* group); 17 | 18 | #endif -------------------------------------------------------------------------------- /src/grouping/group_cache.c: -------------------------------------------------------------------------------- 1 | #include "group_cache.h" 2 | #include "../rmutil/vector.h" 3 | 4 | void InitGroupCache() { 5 | __groupCache = raxNew(); 6 | } 7 | 8 | void CacheGroupAdd(char *key, Group *group) { 9 | raxInsert(__groupCache, (unsigned char *)key, strlen(key), group, NULL); 10 | } 11 | 12 | // Retrives a group, 13 | // Sets group to NULL if key is missing. 14 | void CacheGroupGet(char *key, Group **group) { 15 | *group = raxFind(__groupCache, (unsigned char *)key, strlen(key)); 16 | if (*group == raxNotFound) { 17 | *group = NULL; 18 | } 19 | } 20 | 21 | void FreeGroupCache() { 22 | raxFreeWithCallback(__groupCache, (void (*)(void *))FreeGroup); 23 | } 24 | 25 | // Returns an iterator to scan entire group cache 26 | void CacheGroupIter(CacheGroupIterator *it) { 27 | raxStart(it, __groupCache); 28 | raxSeek(it, "^", NULL, 0); 29 | } 30 | 31 | // Advance iterator and returns key & value in current position. 32 | int CacheGroupIterNext(CacheGroupIterator *it, char **key, Group **group) { 33 | *key = NULL; 34 | *group = NULL; 35 | 36 | int res = raxNext(it); 37 | if(res) { 38 | *key = (char*)it->key; 39 | *group = it->data; 40 | } 41 | 42 | return res; 43 | } 44 | -------------------------------------------------------------------------------- /src/grouping/group_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef GROUP_CACHE_H_ 2 | #define GROUP_CACHE_H_ 3 | 4 | #include "group.h" 5 | #include "../dep/rax/rax.h" 6 | #include "../rmutil/vector.h" 7 | 8 | typedef rax CacheGroup; 9 | typedef raxIterator CacheGroupIterator; 10 | 11 | static CacheGroup *__groupCache = NULL; 12 | 13 | void InitGroupCache(); 14 | 15 | void CacheGroupAdd(char *key, Group *group); 16 | 17 | // Retrives a group, 18 | // Sets group to NULL if key is missing. 19 | void CacheGroupGet(char *key, Group **group); 20 | 21 | void FreeGroupCache(); 22 | 23 | // Initialize an iterator to scan groups. 24 | void CacheGroupIter(CacheGroupIterator *it); 25 | 26 | // Advance iterator and returns key & value in current position. 27 | int CacheGroupIterNext(CacheGroupIterator *iter, char **key, Group **group); 28 | 29 | #endif -------------------------------------------------------------------------------- /src/hexastore/hexastore.h: -------------------------------------------------------------------------------- 1 | #ifndef __HEXASTORE_H__ 2 | #define __HEXASTORE_H__ 3 | 4 | #include "../rmutil/sds.h" 5 | #include "../redismodule.h" 6 | #include "../dep/rax/rax.h" 7 | #include "triplet.h" 8 | 9 | // TODO: find a suiteable place to store hexastores. 10 | typedef rax HexaStore; 11 | 12 | HexaStore *_NewHexaStore(); 13 | 14 | HexaStore *GetHexaStore(RedisModuleCtx *ctx, const char *id); 15 | 16 | /* Create all 6 triplets from given triplet. */ 17 | void HexaStore_InsertAllPerm(HexaStore* hexaStore, Triplet *t); 18 | 19 | void HexaStore_RemoveAllPerm(HexaStore *hexaStore, const Triplet *t); 20 | 21 | void HexaStore_Search(HexaStore* hexaStore, const char *prefix, TripletIterator *it); 22 | 23 | void HexaStore_Search_Iterator(HexaStore* hexastore, sds prefix, TripletIterator *it); 24 | 25 | #endif -------------------------------------------------------------------------------- /src/hexastore/triplet.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIPLET_H 2 | #define TRIPLET_H 3 | 4 | #include "../graph/node.h" 5 | #include "../graph/edge.h" 6 | #include "../redismodule.h" 7 | #include "../dep/rax/rax.h" 8 | #include "../rmutil/sds.h" 9 | 10 | #define TRIPLET_ELEMENT_DELIMITER ":" 11 | #define TRIPLET_PREDICATE_DELIMITER "@" 12 | 13 | typedef enum {UNKNOW, P, O, OP, S, SP, SO, SOP} TripletKind; 14 | typedef struct { 15 | Node* subject; 16 | Edge* predicate; 17 | Node* object; 18 | TripletKind kind; 19 | } Triplet; 20 | 21 | typedef raxIterator TripletIterator; 22 | 23 | /* Creates a new triplet */ 24 | Triplet* NewTriplet(Node *s, Edge *p, Node *o); 25 | 26 | /* Gets triplet's kind. */ 27 | TripletKind TripletGetKind(const Triplet *t); 28 | 29 | /* Given an edge (A) -[edge]-> (B), creates a new triplet. */ 30 | void TripletFromEdge(Edge *e, Triplet *t); 31 | 32 | /* Breaks down triplet into its components */ 33 | void TripletComponents(const Triplet *t, char **subject, char **predicate, char **object); 34 | 35 | /* Returns a string representation of triplet. */ 36 | // char* TripletToString(const Triplet *triplet); 37 | void TripletToString(const Triplet *triplet, sds *str); 38 | 39 | /* Frees allocated space by given triplet. */ 40 | void FreeTriplet(Triplet *triplet); 41 | 42 | // -------------Triplet cursor------------- 43 | 44 | // Returns the next triplet from the cursor. 45 | int TripletIterator_Next(TripletIterator* it, Triplet** triplet); 46 | 47 | void TripletIterator_Free(TripletIterator *it); 48 | 49 | #endif -------------------------------------------------------------------------------- /src/index/index.h: -------------------------------------------------------------------------------- 1 | #ifndef __INDEX_H__ 2 | #define __INDEX_H__ 3 | 4 | #include 5 | #include "../redismodule.h" 6 | #include "../graph/graph_entity.h" 7 | #include "../stores/store.h" 8 | #include "../util/skiplist.h" 9 | #include "../parser/ast.h" 10 | #include "../graph/node.h" 11 | #include "../dep/rax/rax.h" 12 | #include "../filter_tree/filter_tree.h" 13 | #include "../parser/grammar.h" // required for the definition of filter operations (LT, GT, etc) 14 | 15 | #define INDEX_PREFIX "redis_graph_INDEX" 16 | 17 | typedef skiplistIterator IndexIterator; 18 | 19 | typedef struct { 20 | IndexTarget target; 21 | skiplist *string_sl; 22 | skiplist *numeric_sl; 23 | } Index; 24 | 25 | void Index_Create(RedisModuleCtx *ctx, const char *graphName, AST_IndexNode *indexOp); 26 | /* Select an Index and range based on filters associated with Node */ 27 | IndexIterator* Index_IntersectFilters(RedisModuleCtx *ctx, const char *graphName, Vector *filters, const char *label); 28 | 29 | char* Index_OpPrint(AST_IndexNode *indexNode); 30 | 31 | void* IndexIterator_Next(IndexIterator *iter); 32 | void IndexIterator_Reset(IndexIterator *iter); 33 | void IndexIterator_Free(IndexIterator *iter); 34 | 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/index/index_type.h: -------------------------------------------------------------------------------- 1 | #ifndef __INDEX_TYPE_H__ 2 | #define __INDEX_TYPE_H__ 3 | 4 | #include "index.h" 5 | #include "../redismodule.h" 6 | 7 | extern RedisModuleType *IndexRedisModuleType; 8 | 9 | #define INDEX_TYPE_ENCODING_VERSION 1 10 | 11 | int IndexType_Register(RedisModuleCtx *ctx); 12 | void* IndexType_RdbLoad(RedisModuleIO *rdb, int encver); 13 | void IndexType_RdbSave(RedisModuleIO *rdb, void *value); 14 | void IndexType_AofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value); 15 | void IndexType_Free(void *value); 16 | 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /src/parser/Makefile: -------------------------------------------------------------------------------- 1 | %.c: %.y 2 | parser: 3 | lex -i lexer.l;\ 4 | ./lemon -s grammar.y 5 | 6 | .PHONY: parser 7 | 8 | lemon: 9 | gcc -o lemon lemon.c 10 | 11 | all: parser -------------------------------------------------------------------------------- /src/parser/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_H 2 | #define AST_H 3 | 4 | #include "./clauses/clauses.h" 5 | #include "../rmutil/vector.h" 6 | #include "./ast_common.h" 7 | #include "../value.h" 8 | 9 | typedef enum { 10 | AST_VALID, 11 | AST_INVALID 12 | } AST_Validation; 13 | 14 | typedef struct { 15 | AST_MatchNode *matchNode; 16 | AST_CreateNode *createNode; 17 | AST_MergeNode *mergeNode; 18 | AST_SetNode *setNode; 19 | AST_DeleteNode *deleteNode; 20 | AST_WhereNode *whereNode; 21 | AST_ReturnNode *returnNode; 22 | AST_OrderNode *orderNode; 23 | AST_LimitNode *limitNode; 24 | AST_IndexNode *indexNode; 25 | } AST_Query; 26 | 27 | AST_Query* New_AST_Query(AST_MatchNode *matchNode, AST_WhereNode *whereNode, 28 | AST_CreateNode *createNode, AST_MergeNode *mergeNode, 29 | AST_SetNode *setNode, AST_DeleteNode *deleteNode, 30 | AST_ReturnNode *returnNode, AST_OrderNode *orderNode, 31 | AST_LimitNode *limitNode, AST_IndexNode *indexNode); 32 | 33 | /* AST clause validations. */ 34 | AST_Validation _Validate_CREATE_Clause(const AST_Query* ast, char **reason); 35 | AST_Validation _Validate_DELETE_Clause(const AST_Query* ast, char **reason); 36 | AST_Validation _Validate_MATCH_Clause(const AST_Query* ast, char **reason); 37 | AST_Validation _Validate_RETURN_Clause(const AST_Query* ast, char **reason); 38 | AST_Validation _Validate_SET_Clause(const AST_Query* ast, char **reason); 39 | AST_Validation _Validate_WHERE_Clause(const AST_Query* ast, char **reason); 40 | AST_Validation Validate_AST(const AST_Query* ast, char **reason); 41 | 42 | void Free_AST_Query(AST_Query *queryExpressionNode); 43 | 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/parser/ast_arithmetic_expression.c: -------------------------------------------------------------------------------- 1 | #include "ast_arithmetic_expression.h" 2 | 3 | AST_ArithmeticExpressionNode* New_AST_AR_EXP_VariableOperandNode(char* alias, char *property) { 4 | AST_ArithmeticExpressionNode *node = malloc(sizeof(AST_ArithmeticExpressionNode)); 5 | node->type = AST_AR_EXP_OPERAND; 6 | node->operand.type = AST_AR_EXP_VARIADIC; 7 | node->operand.variadic.alias = strdup(alias); 8 | if(property) { 9 | // This is a collapsed entity. 10 | node->operand.variadic.property = strdup(property); 11 | } else { 12 | node->operand.variadic.property = NULL; 13 | } 14 | return node; 15 | } 16 | 17 | AST_ArithmeticExpressionNode* New_AST_AR_EXP_ConstOperandNode(SIValue constant) { 18 | AST_ArithmeticExpressionNode *node = malloc(sizeof(AST_ArithmeticExpressionNode)); 19 | node->type = AST_AR_EXP_OPERAND; 20 | node->operand.type = AST_AR_EXP_CONSTANT; 21 | node->operand.constant = constant; 22 | return node; 23 | } 24 | 25 | AST_ArithmeticExpressionNode* New_AST_AR_EXP_OpNode(char *func, Vector *args) { 26 | AST_ArithmeticExpressionNode *node = malloc(sizeof(AST_ArithmeticExpressionNode)); 27 | node->type = AST_AR_EXP_OP; 28 | node->op.function = strdup(func); 29 | node->op.args = args; 30 | return node; 31 | } 32 | 33 | void Free_AST_ArithmeticExpressionNode(AST_ArithmeticExpressionNode *arExpNode) { 34 | /* Free arithmetic expression operation. */ 35 | if(arExpNode->type == AST_AR_EXP_OP) { 36 | /* Free each argument. */ 37 | for(int i = 0; i < Vector_Size(arExpNode->op.args); i++) { 38 | AST_ArithmeticExpressionNode *child; 39 | Vector_Get(arExpNode->op.args, i, &child); 40 | Free_AST_ArithmeticExpressionNode(child); 41 | } 42 | Vector_Free(arExpNode->op.args); 43 | } else { 44 | /* Node is an arithmetic expression operand. */ 45 | if(arExpNode->operand.type == AST_AR_EXP_VARIADIC) { 46 | free(arExpNode->operand.variadic.alias); 47 | free(arExpNode->operand.variadic.property); 48 | } 49 | } 50 | /* Finaly we can free the node. */ 51 | free(arExpNode); 52 | } 53 | -------------------------------------------------------------------------------- /src/parser/ast_arithmetic_expression.h: -------------------------------------------------------------------------------- 1 | #ifndef _AST_ARITHMETIC_EXPRESSION_H 2 | #define _AST_ARITHMETIC_EXPRESSION_H 3 | 4 | #include "../rmutil/vector.h" 5 | #include "../value.h" 6 | 7 | /* ArExpNodeType lists the type of nodes within 8 | * an arithmetic expression tree. */ 9 | typedef enum { 10 | AST_AR_EXP_OP, 11 | AST_AR_EXP_OPERAND, 12 | } AST_ArithmeticExpression_NodeType; 13 | 14 | typedef enum { 15 | AST_AR_EXP_CONSTANT, 16 | AST_AR_EXP_VARIADIC, 17 | } AST_ArithmeticExpression_OperandNodeType; 18 | 19 | typedef struct { 20 | char *function; /* Name of operation. */ 21 | Vector *args; /* Vector of AST_ArithmeticExpressionNode pointers. */ 22 | } AST_ArithmeticExpressionOP; 23 | 24 | /* OperandNode represents either a constant numeric value, 25 | * or a graph entity property. */ 26 | typedef struct { 27 | union { 28 | SIValue constant; 29 | struct { 30 | char *alias; 31 | char *property; 32 | } variadic; 33 | }; 34 | AST_ArithmeticExpression_OperandNodeType type; 35 | } AST_ArithmeticExpressionOperand; 36 | 37 | typedef struct { 38 | union { 39 | AST_ArithmeticExpressionOperand operand; 40 | AST_ArithmeticExpressionOP op; 41 | }; 42 | AST_ArithmeticExpression_NodeType type; 43 | } AST_ArithmeticExpressionNode; 44 | 45 | AST_ArithmeticExpressionNode* New_AST_AR_EXP_VariableOperandNode(char* alias, char *property); 46 | AST_ArithmeticExpressionNode* New_AST_AR_EXP_ConstOperandNode(SIValue constant); 47 | AST_ArithmeticExpressionNode* New_AST_AR_EXP_OpNode(char *func, Vector *args); 48 | void Free_AST_ArithmeticExpressionNode(AST_ArithmeticExpressionNode *arExpNode); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/parser/ast_common.c: -------------------------------------------------------------------------------- 1 | #include "./ast_common.h" 2 | #include "../value.h" 3 | 4 | AST_Variable* New_AST_Variable(const char* alias, const char* property) { 5 | AST_Variable *v = (AST_Variable*)calloc(1, sizeof(AST_Variable)); 6 | 7 | if(alias != NULL) { 8 | v->alias = strdup(alias); 9 | } 10 | if(property != NULL) { 11 | v->property = strdup(property); 12 | } 13 | 14 | return v; 15 | } 16 | 17 | AST_LinkEntity* New_AST_LinkEntity(char *alias, char *label, Vector *properties, AST_LinkDirection dir) { 18 | AST_LinkEntity* le = (AST_LinkEntity*)calloc(1, sizeof(AST_LinkEntity)); 19 | le->direction = dir; 20 | le->ge.t = N_LINK; 21 | le->ge.properties = properties; 22 | 23 | if(label != NULL) { 24 | le->ge.label = strdup(label); 25 | } 26 | if(alias != NULL) { 27 | le->ge.alias = strdup(alias); 28 | } 29 | 30 | return le; 31 | } 32 | 33 | AST_NodeEntity* New_AST_NodeEntity(char *alias, char *label, Vector *properties) { 34 | AST_NodeEntity* ne = (AST_NodeEntity*)calloc(1, sizeof(AST_NodeEntity)); 35 | ne->t = N_ENTITY; 36 | ne->properties = properties; 37 | 38 | if(alias != NULL) { 39 | ne->alias = strdup(alias); 40 | } 41 | if(label != NULL) { 42 | ne->label = strdup(label); 43 | } 44 | 45 | return ne; 46 | } 47 | 48 | void Free_AST_GraphEntity(AST_GraphEntity *graphEntity) { 49 | if(graphEntity->label != NULL) { 50 | free(graphEntity->label); 51 | } 52 | if(graphEntity->alias != NULL) { 53 | free(graphEntity->alias); 54 | } 55 | if(graphEntity->properties != NULL) { 56 | for(int i = 0; i < Vector_Size(graphEntity->properties); i++) { 57 | SIValue *val; 58 | Vector_Get(graphEntity->properties, i, &val); 59 | SIValue_Free(val); 60 | free(val); 61 | } 62 | Vector_Free(graphEntity->properties); 63 | } 64 | free(graphEntity); 65 | } 66 | 67 | void Free_AST_Variable(AST_Variable *v) { 68 | if(v != NULL) { 69 | if(v->alias != NULL) { 70 | free(v->alias); 71 | } 72 | if(v->property != NULL) { 73 | free(v->property); 74 | } 75 | free(v); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/parser/ast_common.h: -------------------------------------------------------------------------------- 1 | #ifndef _AST_COMMON_H 2 | #define _AST_COMMON_H 3 | 4 | #include "../rmutil/vector.h" 5 | 6 | typedef enum { 7 | N_ENTITY, 8 | N_LINK, 9 | } AST_GraphEntityType; 10 | 11 | typedef enum { 12 | N_LEFT_TO_RIGHT, 13 | N_RIGHT_TO_LEFT, 14 | N_DIR_NOT_SPECIFIED, 15 | } AST_LinkDirection; 16 | 17 | typedef struct { 18 | char *alias; 19 | char *label; 20 | Vector *properties; 21 | AST_GraphEntityType t; 22 | } AST_GraphEntity; 23 | 24 | typedef AST_GraphEntity AST_NodeEntity; 25 | 26 | typedef struct { 27 | char *alias; 28 | char *property; 29 | } AST_Variable; 30 | 31 | typedef struct { 32 | AST_GraphEntity ge; 33 | AST_LinkDirection direction; 34 | } AST_LinkEntity; 35 | 36 | AST_NodeEntity* New_AST_NodeEntity(char *alias, char *label, Vector *properties); 37 | AST_LinkEntity* New_AST_LinkEntity(char *alias, char *relationship, Vector *properties, AST_LinkDirection dir); 38 | AST_Variable* New_AST_Variable(const char *alias, const char *property); 39 | void Free_AST_GraphEntity(AST_GraphEntity *entity); 40 | void Free_AST_Variable(AST_Variable *v); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/parser/clauses/clauses.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSES_H_ 2 | #define _CLAUSES_H_ 3 | 4 | #include "./create.h" 5 | #include "./delete.h" 6 | #include "./limit.h" 7 | #include "./match.h" 8 | #include "./merge.h" 9 | #include "./order.h" 10 | #include "./return.h" 11 | #include "./set.h" 12 | #include "./where.h" 13 | #include "./index.h" 14 | 15 | #endif -------------------------------------------------------------------------------- /src/parser/clauses/create.c: -------------------------------------------------------------------------------- 1 | #include "./create.h" 2 | #include "../ast_common.h" 3 | 4 | AST_CreateNode* New_AST_CreateNode(Vector *elements) { 5 | AST_CreateNode *create_node = (AST_CreateNode*)malloc(sizeof(AST_CreateNode)); 6 | create_node->graphEntities = elements; 7 | return create_node; 8 | } 9 | 10 | void Free_AST_CreateNode(AST_CreateNode *createNode) { 11 | if(!createNode) return; 12 | 13 | for(int i = 0; i < Vector_Size(createNode->graphEntities); i++) { 14 | AST_GraphEntity *ge; 15 | Vector_Get(createNode->graphEntities, i, &ge); 16 | Free_AST_GraphEntity(ge); 17 | } 18 | 19 | Vector_Free(createNode->graphEntities); 20 | free(createNode); 21 | } -------------------------------------------------------------------------------- /src/parser/clauses/create.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_CREATE_H 2 | #define _CLAUSE_CREATE_H 3 | 4 | #include "../../rmutil/vector.h" 5 | 6 | typedef struct { 7 | Vector *graphEntities; /* Vector of Vectors of AST_GraphEntity pointers. */ 8 | } AST_CreateNode; 9 | 10 | AST_CreateNode* New_AST_CreateNode(Vector *elements); 11 | void Free_AST_CreateNode(AST_CreateNode *createNode); 12 | 13 | #endif -------------------------------------------------------------------------------- /src/parser/clauses/delete.c: -------------------------------------------------------------------------------- 1 | #include "delete.h" 2 | 3 | AST_DeleteNode* New_AST_DeleteNode(Vector *elements) { 4 | AST_DeleteNode *deleteNode = (AST_DeleteNode*)malloc(sizeof(AST_DeleteNode)); 5 | deleteNode->graphEntities = elements; 6 | return deleteNode; 7 | } 8 | 9 | void Free_AST_DeleteNode(AST_DeleteNode *deleteNode) { 10 | if(!deleteNode) return; 11 | Vector_Free(deleteNode->graphEntities); 12 | free(deleteNode); 13 | } 14 | -------------------------------------------------------------------------------- /src/parser/clauses/delete.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_DELETE_H 2 | #define _CLAUSE_DELETE_H 3 | 4 | #include "../../rmutil/vector.h" 5 | 6 | typedef struct { 7 | Vector *graphEntities; /* Vector of char pointers. */ 8 | } AST_DeleteNode; 9 | 10 | AST_DeleteNode* New_AST_DeleteNode(Vector *elements); 11 | void Free_AST_DeleteNode(AST_DeleteNode *deleteNode); 12 | 13 | #endif -------------------------------------------------------------------------------- /src/parser/clauses/index.c: -------------------------------------------------------------------------------- 1 | #include "./index.h" 2 | #include "../ast_common.h" 3 | 4 | AST_IndexNode* New_AST_IndexNode(const char *label, const char *property, AST_IndexOpType optype) { 5 | AST_IndexNode *indexOp = malloc(sizeof(AST_IndexNode)); 6 | indexOp->target.label = label; 7 | indexOp->target.property = property; 8 | indexOp->operation = optype; 9 | return indexOp; 10 | } -------------------------------------------------------------------------------- /src/parser/clauses/index.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_INDEX_H 2 | #define _CLAUSE_INDEX_H 3 | 4 | typedef enum { 5 | DROP_INDEX, 6 | CREATE_INDEX 7 | } AST_IndexOpType; 8 | 9 | typedef struct { 10 | const char *label; 11 | const char *property; 12 | } IndexTarget; 13 | 14 | typedef struct { 15 | IndexTarget target; 16 | AST_IndexOpType operation; 17 | } AST_IndexNode; 18 | 19 | AST_IndexNode* New_AST_IndexNode(const char *label, const char *property, AST_IndexOpType optype); 20 | 21 | #endif -------------------------------------------------------------------------------- /src/parser/clauses/limit.c: -------------------------------------------------------------------------------- 1 | #include "./limit.h" 2 | 3 | AST_LimitNode* New_AST_LimitNode(int limit) { 4 | AST_LimitNode* limitNode = (AST_LimitNode*)malloc(sizeof(AST_LimitNode)); 5 | limitNode->limit = limit; 6 | return limitNode; 7 | } 8 | 9 | void Free_AST_LimitNode(AST_LimitNode* limitNode) { 10 | if(limitNode) { 11 | free(limitNode); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/parser/clauses/limit.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_LIMIT_H 2 | #define _CLAUSE_LIMIT_H 3 | 4 | #include "../../rmutil/vector.h" 5 | 6 | typedef struct { 7 | int limit; 8 | } AST_LimitNode; 9 | 10 | AST_LimitNode* New_AST_LimitNode(int limit); 11 | void Free_AST_LimitNode(AST_LimitNode *limitNode); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/parser/clauses/match.c: -------------------------------------------------------------------------------- 1 | #include "./match.h" 2 | #include "../ast_common.h" 3 | 4 | AST_MatchNode* New_AST_MatchNode(Vector *elements) { 5 | AST_MatchNode *matchNode = (AST_MatchNode*)malloc(sizeof(AST_MatchNode)); 6 | matchNode->graphEntities = elements; 7 | return matchNode; 8 | } 9 | 10 | void Free_AST_MatchNode(AST_MatchNode *matchNode) { 11 | for(int i = 0; i < Vector_Size(matchNode->graphEntities); i++) { 12 | AST_GraphEntity *ge; 13 | Vector_Get(matchNode->graphEntities, i, &ge); 14 | Free_AST_GraphEntity(ge); 15 | } 16 | 17 | Vector_Free(matchNode->graphEntities); 18 | free(matchNode); 19 | } 20 | -------------------------------------------------------------------------------- /src/parser/clauses/match.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_MATCH_H 2 | #define _CLAUSE_MATCH_H 3 | 4 | #include "../../rmutil/vector.h" 5 | 6 | typedef struct { 7 | Vector *graphEntities; 8 | } AST_MatchNode; 9 | 10 | AST_MatchNode* New_AST_MatchNode(Vector *elements); 11 | void Free_AST_MatchNode(AST_MatchNode *matchNode); 12 | 13 | #endif -------------------------------------------------------------------------------- /src/parser/clauses/merge.c: -------------------------------------------------------------------------------- 1 | #include "merge.h" 2 | #include "../ast_common.h" 3 | 4 | AST_MergeNode* New_AST_MergeNode() { 5 | AST_MergeNode *mergeNode = malloc(sizeof(AST_MergeNode)); 6 | mergeNode->nodes = NewVector(AST_NodeEntity*, 1); 7 | return mergeNode; 8 | } 9 | 10 | void Free_AST_MergeNode(AST_MergeNode *mergeNode) { 11 | if(!mergeNode) return; 12 | 13 | AST_NodeEntity *node = NULL; 14 | while(Vector_Pop(mergeNode->nodes, node)) { 15 | Free_AST_GraphEntity(node); 16 | } 17 | 18 | Vector_Free(mergeNode->nodes); 19 | free(mergeNode); 20 | } -------------------------------------------------------------------------------- /src/parser/clauses/merge.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_MERGE_H 2 | #define _CLAUSE_MERGE_H 3 | 4 | #include "../../rmutil/vector.h" 5 | 6 | typedef struct { 7 | Vector *nodes; /* Vector of AST_NodeEntity pointers. */ 8 | } AST_MergeNode; 9 | 10 | AST_MergeNode* New_AST_MergeNode(); 11 | void Free_AST_MergeNode(AST_MergeNode *mergeNode); 12 | 13 | #endif -------------------------------------------------------------------------------- /src/parser/clauses/order.c: -------------------------------------------------------------------------------- 1 | #include "./order.h" 2 | 3 | AST_OrderNode* New_AST_OrderNode(Vector *columns, AST_OrderByDirection direction) { 4 | AST_OrderNode *orderNode = (AST_OrderNode*)malloc(sizeof(AST_OrderNode)); 5 | orderNode->columns = columns; 6 | orderNode->direction = direction; 7 | return orderNode; 8 | } 9 | 10 | AST_ColumnNode* New_AST_ColumnNode(const char *alias, const char *property, AST_ColumnNodeType type) { 11 | AST_ColumnNode *node = malloc(sizeof(AST_ColumnNode)); 12 | node->type = type; 13 | node->alias = NULL; 14 | node->property = NULL; 15 | 16 | node->alias = malloc(sizeof(char) * (strlen(alias) + 1)); 17 | strcpy(node->alias, alias); 18 | 19 | if(type == N_VARIABLE) { 20 | node->property = malloc(sizeof(char) * (strlen(property) + 1)); 21 | strcpy(node->property, property); 22 | } 23 | 24 | return node; 25 | } 26 | 27 | AST_ColumnNode* AST_ColumnNodeFromVariable(const AST_Variable *variable) { 28 | return New_AST_ColumnNode(variable->alias, variable->property, N_VARIABLE); 29 | } 30 | 31 | AST_ColumnNode* AST_ColumnNodeFromAlias(const char *alias) { 32 | return New_AST_ColumnNode(alias, NULL, N_ALIAS); 33 | } 34 | 35 | void Free_AST_ColumnNode(AST_ColumnNode* node) { 36 | if(node != NULL) { 37 | if(node->alias != NULL) { 38 | free(node->alias); 39 | } 40 | if(node->type == N_VARIABLE && node->property != NULL) { 41 | free(node->property); 42 | } 43 | free(node); 44 | } 45 | } 46 | 47 | void Free_AST_OrderNode(AST_OrderNode *orderNode) { 48 | if(orderNode != NULL) { 49 | for(int i = 0; i < Vector_Size(orderNode->columns); i++) { 50 | AST_ColumnNode *c = NULL; 51 | Vector_Get(orderNode->columns, i , &c); 52 | Free_AST_ColumnNode(c); 53 | } 54 | 55 | Vector_Free(orderNode->columns); 56 | free(orderNode); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/parser/clauses/order.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_ORDER_H 2 | #define _CLAUSE_ORDER_H 3 | 4 | #include "../../rmutil/vector.h" 5 | #include "../ast_common.h" 6 | 7 | typedef enum { 8 | N_VARIABLE, 9 | N_ALIAS 10 | } AST_ColumnNodeType; 11 | 12 | typedef struct { 13 | char *alias; 14 | char *property; 15 | AST_ColumnNodeType type; 16 | } AST_ColumnNode; 17 | 18 | typedef enum { 19 | ORDER_DIR_ASC, 20 | ORDER_DIR_DESC 21 | } AST_OrderByDirection; 22 | 23 | typedef struct { 24 | Vector *columns; // Vector of ColumnNodes 25 | AST_OrderByDirection direction; 26 | } AST_OrderNode; 27 | 28 | AST_OrderNode* New_AST_OrderNode(Vector* columns, AST_OrderByDirection direction); 29 | AST_ColumnNode* New_AST_ColumnNode(const char *alias, const char *prop, AST_ColumnNodeType type); 30 | AST_ColumnNode* AST_ColumnNodeFromVariable(const AST_Variable *variable); 31 | AST_ColumnNode* AST_ColumnNodeFromAlias(const char *alias); 32 | void Free_AST_OrderNode(AST_OrderNode *orderNode); 33 | void Free_AST_ColumnNode(AST_ColumnNode *node); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/parser/clauses/return.c: -------------------------------------------------------------------------------- 1 | #include "./return.h" 2 | #include "../../arithmetic/repository.h" 3 | 4 | AST_ReturnElementNode* New_AST_ReturnElementNode(AST_ArithmeticExpressionNode *exp, const char* alias) { 5 | AST_ReturnElementNode *returnElementNode = (AST_ReturnElementNode*)malloc(sizeof(AST_ReturnElementNode)); 6 | returnElementNode->exp = exp; 7 | returnElementNode->alias = NULL; 8 | 9 | if(alias != NULL) returnElementNode->alias = strdup(alias); 10 | 11 | return returnElementNode; 12 | } 13 | 14 | AST_ReturnNode* New_AST_ReturnNode(Vector *returnElements, int distinct) { 15 | AST_ReturnNode *returnNode = (AST_ReturnNode*)malloc(sizeof(AST_ReturnNode)); 16 | returnNode->returnElements = returnElements; 17 | returnNode->distinct = distinct; 18 | return returnNode; 19 | } 20 | 21 | int ReturnClause_ContainsCollapsedNodes(const AST_ReturnNode *return_node) { 22 | if(!return_node) return 0; 23 | for(int i = 0; i < Vector_Size(return_node->returnElements); i++) { 24 | AST_ReturnElementNode *ret_elem; 25 | Vector_Get(return_node->returnElements, i, &ret_elem); 26 | AST_ArithmeticExpressionNode *exp = ret_elem->exp; 27 | /* Detect collapsed entity, 28 | * A collapsed entity is represented by an arithmetic expression 29 | * of AST_AR_EXP_OPERAND type, 30 | * The operand type should be AST_AR_EXP_VARIADIC, 31 | * lastly property should be missing. */ 32 | if(exp->type == AST_AR_EXP_OPERAND && 33 | exp->operand.type == AST_AR_EXP_VARIADIC && 34 | exp->operand.variadic.property == NULL) { 35 | return 1; 36 | } 37 | } 38 | return 0; 39 | } 40 | 41 | int _ContainsAggregation(AST_ArithmeticExpressionNode *exp) { 42 | if(exp->type == AST_AR_EXP_OPERAND) { 43 | return 0; 44 | } 45 | 46 | /* Try to get an aggregation function. */ 47 | AggCtx* ctx; 48 | Agg_GetFunc(exp->op.function, &ctx); 49 | if(ctx != NULL) return 1; 50 | 51 | /* Scan sub expressions. */ 52 | for(int i = 0; i < Vector_Size(exp->op.args); i++) { 53 | AST_ArithmeticExpressionNode *child_exp; 54 | Vector_Get(exp->op.args, i, &child_exp); 55 | if(_ContainsAggregation(child_exp)) return 1; 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | /* Checks if return clause uses aggregation. */ 62 | int ReturnClause_ContainsAggregation(const AST_ReturnNode *return_node) { 63 | if(!return_node) return 0; 64 | 65 | for(int i = 0; i < Vector_Size(return_node->returnElements); i++) { 66 | AST_ReturnElementNode *ret_elem; 67 | Vector_Get(return_node->returnElements, i, &ret_elem); 68 | AST_ArithmeticExpressionNode *exp = ret_elem->exp; 69 | 70 | /* Scan expression for an aggregation function. */ 71 | if (_ContainsAggregation(exp)) return 1; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | void Free_AST_ReturnElementNode(AST_ReturnElementNode *returnElementNode) { 78 | if(returnElementNode != NULL) { 79 | Free_AST_ArithmeticExpressionNode(returnElementNode->exp); 80 | 81 | if(returnElementNode->alias != NULL) { 82 | free(returnElementNode->alias); 83 | } 84 | 85 | free(returnElementNode); 86 | } 87 | } 88 | 89 | void Free_AST_ReturnNode(AST_ReturnNode *returnNode) { 90 | for (int i = 0; i < Vector_Size(returnNode->returnElements); i++) { 91 | AST_ReturnElementNode *node; 92 | Vector_Get(returnNode->returnElements, i, &node); 93 | Free_AST_ReturnElementNode(node); 94 | } 95 | 96 | Vector_Free(returnNode->returnElements); 97 | free(returnNode); 98 | } 99 | -------------------------------------------------------------------------------- /src/parser/clauses/return.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_RETURN_H 2 | #define _CLAUSE_RETURN_H 3 | 4 | #include "../ast_arithmetic_expression.h" 5 | #include "../../rmutil/vector.h" 6 | 7 | typedef struct { 8 | char *alias; // Alias given to this return element (using the AS keyword) 9 | AST_ArithmeticExpressionNode *exp; 10 | } AST_ReturnElementNode; 11 | 12 | typedef struct { 13 | Vector *returnElements; // Vector of ReturnElementNode pointers 14 | int distinct; 15 | } AST_ReturnNode; 16 | 17 | AST_ReturnElementNode* New_AST_ReturnElementNode(AST_ArithmeticExpressionNode *exp, const char *alias); 18 | AST_ReturnNode* New_AST_ReturnNode(Vector* returnElements, int distinct); 19 | /* Checks to see if return clause contains a collapsed node. */ 20 | int ReturnClause_ContainsCollapsedNodes(const AST_ReturnNode *return_node); 21 | int ReturnClause_ContainsAggregation(const AST_ReturnNode *return_node); 22 | void Free_AST_ReturnElementNode(AST_ReturnElementNode *returnElementNode); 23 | void Free_AST_ReturnNode(AST_ReturnNode *returnNode); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/parser/clauses/set.c: -------------------------------------------------------------------------------- 1 | #include "./set.h" 2 | 3 | AST_SetNode* New_AST_SetNode(Vector *elements) { 4 | AST_SetNode *set_node = (AST_SetNode*)malloc(sizeof(AST_SetNode)); 5 | set_node->set_elements = elements; 6 | return set_node; 7 | } 8 | 9 | AST_SetElement* New_AST_SetElement(AST_Variable *updated_entity, AST_ArithmeticExpressionNode *exp) { 10 | AST_SetElement *set_element = malloc(sizeof(AST_SetElement)); 11 | set_element->entity = updated_entity; 12 | set_element->exp = exp; 13 | return set_element; 14 | } 15 | 16 | void Free_AST_SetNode(AST_SetNode *setNode) { 17 | if(!setNode) return; 18 | 19 | AST_SetElement *elem = NULL; 20 | while(Vector_Pop(setNode->set_elements, elem)) { 21 | Free_AST_Variable(elem->entity); 22 | Free_AST_ArithmeticExpressionNode(elem->exp); 23 | free(elem); 24 | } 25 | 26 | Vector_Free(setNode->set_elements); 27 | free(setNode); 28 | } 29 | -------------------------------------------------------------------------------- /src/parser/clauses/set.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_SET_H 2 | #define _CLAUSE_SET_H 3 | 4 | #include "../ast_common.h" 5 | #include "../../rmutil/vector.h" 6 | #include "../ast_arithmetic_expression.h" 7 | 8 | typedef struct { 9 | AST_Variable *entity; /* Destination entity to update. */ 10 | AST_ArithmeticExpressionNode *exp; /* Arithmetic expression, evaluated value used for update. */ 11 | } AST_SetElement; 12 | 13 | typedef struct { 14 | Vector *set_elements; /* Vector of AST_SetElement pointers, each describes an entity update. */ 15 | } AST_SetNode; 16 | 17 | /* Set clause individual elements. */ 18 | AST_SetNode* New_AST_SetNode(Vector *elements); 19 | AST_SetElement* New_AST_SetElement(AST_Variable *updated_entity, AST_ArithmeticExpressionNode *exp); 20 | void Free_AST_SetNode(AST_SetNode *setNode); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/parser/clauses/where.c: -------------------------------------------------------------------------------- 1 | #include "./where.h" 2 | 3 | AST_WhereNode* New_AST_WhereNode(AST_FilterNode *filters) { 4 | AST_WhereNode *whereNode = (AST_WhereNode*)malloc(sizeof(AST_WhereNode)); 5 | whereNode->filters = filters; 6 | return whereNode; 7 | } 8 | 9 | AST_FilterNode* New_AST_ConstantPredicateNode(const char* alias, const char* property, int op, SIValue value) { 10 | AST_FilterNode *n = malloc(sizeof(AST_FilterNode)); 11 | n->t = N_PRED; 12 | 13 | n->pn.t = N_CONSTANT; 14 | n->pn.alias = strdup(alias); 15 | n->pn.property = strdup(property); 16 | 17 | n->pn.op = op; 18 | n->pn.constVal = value; 19 | 20 | return n; 21 | } 22 | 23 | AST_FilterNode* New_AST_VaryingPredicateNode(const char* lAlias, const char* lProperty, int op, const char* rAlias, const char* rProperty) { 24 | AST_FilterNode *n = malloc(sizeof(AST_FilterNode)); 25 | n->t = N_PRED; 26 | 27 | n->pn.t = N_VARYING; 28 | n->pn.alias = (char*)malloc(strlen(lAlias) + 1); 29 | n->pn.property = (char*)malloc(strlen(lProperty) + 1); 30 | n->pn.nodeVal.alias = (char*)malloc(strlen(rAlias) + 1); 31 | n->pn.nodeVal.property = (char*)malloc(strlen(rProperty) + 1); 32 | 33 | strcpy(n->pn.alias, lAlias); 34 | strcpy(n->pn.property, lProperty); 35 | strcpy(n->pn.nodeVal.alias, rAlias); 36 | strcpy(n->pn.nodeVal.property, rProperty); 37 | 38 | n->pn.op = op; 39 | 40 | return n; 41 | } 42 | 43 | AST_FilterNode *New_AST_ConditionNode(AST_FilterNode *left, int op, AST_FilterNode *right) { 44 | AST_FilterNode *n = malloc(sizeof(AST_FilterNode)); 45 | n->t = N_COND; 46 | n->cn.left = left; 47 | n->cn.right = right; 48 | n->cn.op = op; 49 | 50 | return n; 51 | } 52 | 53 | void FreePredicateNode(AST_PredicateNode* predicateNode) { 54 | 55 | if(predicateNode->alias) { 56 | free(predicateNode->alias); 57 | } 58 | 59 | if(predicateNode->property) { 60 | free(predicateNode->property); 61 | } 62 | 63 | if(predicateNode->t == N_VARYING) { 64 | if(predicateNode->nodeVal.alias) { 65 | free(predicateNode->nodeVal.alias); 66 | } 67 | 68 | if(predicateNode->nodeVal.property) { 69 | free(predicateNode->nodeVal.property); 70 | } 71 | } 72 | 73 | // TODO: Should I free constVal? 74 | } 75 | 76 | void Free_AST_FilterNode(AST_FilterNode* filterNode) { 77 | if(!filterNode) 78 | return; 79 | 80 | switch(filterNode->t) { 81 | case N_PRED: 82 | FreePredicateNode(&filterNode->pn); 83 | break; 84 | case N_COND: 85 | Free_AST_FilterNode(filterNode->cn.left); 86 | Free_AST_FilterNode(filterNode->cn.right); 87 | break; 88 | } 89 | } 90 | 91 | void Free_AST_WhereNode(AST_WhereNode *whereNode) { 92 | if(!whereNode) return; 93 | 94 | Free_AST_FilterNode(whereNode->filters); 95 | free(whereNode); 96 | } 97 | -------------------------------------------------------------------------------- /src/parser/clauses/where.h: -------------------------------------------------------------------------------- 1 | #ifndef _CLAUSE_WHERE_H 2 | #define _CLAUSE_WHERE_H 3 | 4 | #include "../../value.h" 5 | #include "../../rmutil/vector.h" 6 | 7 | typedef enum { 8 | N_PRED, 9 | N_COND, 10 | } AST_FilterNodeType; 11 | 12 | typedef enum { 13 | N_CONSTANT, 14 | N_VARYING, 15 | } AST_CompareValueType; 16 | 17 | struct filterNode; 18 | 19 | typedef struct { 20 | union { 21 | SIValue constVal; 22 | struct { 23 | char *alias; 24 | char *property; 25 | } nodeVal; 26 | }; 27 | AST_CompareValueType t; // Compared value type, constant/node 28 | char *alias; // Node alias 29 | char *property; // Node property 30 | int op; // Type of comparison 31 | } AST_PredicateNode; 32 | 33 | typedef struct conditionNode { 34 | struct filterNode *left; 35 | struct filterNode *right; 36 | int op; 37 | } AST_ConditionNode; 38 | 39 | typedef struct filterNode { 40 | union { 41 | AST_PredicateNode pn; 42 | AST_ConditionNode cn; 43 | }; 44 | AST_FilterNodeType t; 45 | } AST_FilterNode; 46 | 47 | typedef struct { 48 | AST_FilterNode *filters; 49 | } AST_WhereNode; 50 | 51 | AST_WhereNode* New_AST_WhereNode(AST_FilterNode *filters); 52 | AST_FilterNode* New_AST_ConstantPredicateNode(const char *alias, const char *property, int op, SIValue value); 53 | AST_FilterNode* New_AST_VaryingPredicateNode(const char *lAlias, const char *lProperty, int op, const char *rAlias, const char *rProperty); 54 | AST_FilterNode* New_AST_ConditionNode(AST_FilterNode *left, int op, AST_FilterNode *right); 55 | void Free_AST_FilterNode(AST_FilterNode *filterNode); 56 | void Free_AST_WhereNode(AST_WhereNode *whereNode); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/parser/grammar.h: -------------------------------------------------------------------------------- 1 | #define OR 1 2 | #define AND 2 3 | #define ADD 3 4 | #define DASH 4 5 | #define MUL 5 6 | #define DIV 6 7 | #define EQ 7 8 | #define GT 8 9 | #define GE 9 10 | #define LT 10 11 | #define LE 11 12 | #define MATCH 12 13 | #define CREATE 13 14 | #define INDEX 14 15 | #define ON 15 16 | #define DROP 16 17 | #define COLON 17 18 | #define UQSTRING 18 19 | #define LEFT_PARENTHESIS 19 20 | #define RIGHT_PARENTHESIS 20 21 | #define SET 21 22 | #define COMMA 22 23 | #define DELETE 23 24 | #define RIGHT_ARROW 24 25 | #define LEFT_ARROW 25 26 | #define LEFT_BRACKET 26 27 | #define RIGHT_BRACKET 27 28 | #define LEFT_CURLY_BRACKET 28 29 | #define RIGHT_CURLY_BRACKET 29 30 | #define WHERE 30 31 | #define DOT 31 32 | #define RETURN 32 33 | #define DISTINCT 33 34 | #define AS 34 35 | #define ORDER 35 36 | #define BY 36 37 | #define ASC 37 38 | #define DESC 38 39 | #define LIMIT 39 40 | #define INTEGER 40 41 | #define NE 41 42 | #define STRING 42 43 | #define FLOAT 43 44 | #define TRUE 44 45 | #define FALSE 45 46 | -------------------------------------------------------------------------------- /src/parser/lemon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/src/parser/lemon -------------------------------------------------------------------------------- /src/parser/lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | 3 | #include "grammar.h" 4 | #include "token.h" 5 | #include 6 | #include 7 | 8 | Token tok; 9 | 10 | 11 | /* handle locations */ 12 | int yycolumn = 1; 13 | 14 | #define YY_USER_ACTION yycolumn += yyleng; \ 15 | tok.pos = yycolumn; \ 16 | tok.s = strdup(yytext); 17 | %} 18 | 19 | %% 20 | 21 | "AND" { return AND; } 22 | "OR" { return OR; } 23 | "TRUE" { return TRUE; } 24 | "FALSE" { return FALSE; } 25 | "MATCH" { return MATCH; } 26 | "CREATE" { return CREATE; } 27 | "DELETE" { return DELETE; } 28 | "RETURN" { return RETURN; } 29 | "SET" { return SET; } 30 | "AS" { return AS; } 31 | "DISTINCT" { return DISTINCT; } 32 | "WHERE" { return WHERE; } 33 | "ORDER" { return ORDER; } 34 | "BY" { return BY; } 35 | "ASC" { return ASC; } 36 | "DESC" { return DESC; } 37 | "LIMIT" { return LIMIT; } 38 | "INDEX" { return INDEX; } 39 | "ON" { return ON; } 40 | "DROP" { return DROP; } 41 | 42 | 43 | [0-9]*\.[0-9]+ { 44 | tok.dval = atof(yytext); 45 | return FLOAT; 46 | } 47 | 48 | [0-9]+ { 49 | tok.intval = atoi(yytext); 50 | return INTEGER; 51 | } 52 | 53 | [_A-Za-z][A-Za-z0-9_-]* { 54 | tok.strval = strdup(yytext); 55 | return UQSTRING; // Unquoted string, used for entity alias, prop name and labels. 56 | } 57 | 58 | (\"(\\.|[^\"])*\")|('(\\.|[^'])*') { 59 | /* String literals, with escape sequences - enclosed by "" or '' */ 60 | *(yytext+strlen(yytext)-1) = '\0'; 61 | tok.strval = strdup(yytext+1); 62 | return STRING; 63 | } 64 | 65 | "," { return COMMA; } 66 | "(" { return LEFT_PARENTHESIS; } 67 | ")" { return RIGHT_PARENTHESIS; } 68 | "[" { return LEFT_BRACKET; } 69 | "]" { return RIGHT_BRACKET; } 70 | "{" { return LEFT_CURLY_BRACKET; } 71 | "}" { return RIGHT_CURLY_BRACKET; } 72 | ">=" { return GE; } 73 | "<=" { return LE; } 74 | "->" { return RIGHT_ARROW; } 75 | "<-" { return LEFT_ARROW; } 76 | "!=" { return NE; } 77 | "=" { return EQ; } 78 | ">" { return GT; } 79 | "<" { return LT; } 80 | "-" { return DASH; } 81 | ":" { return COLON; } 82 | "." { return DOT; } 83 | "/" { return DIV; } 84 | "*" { return MUL; } 85 | "+" { return ADD; } 86 | 87 | [ \t]+ /* ignore whitespace */ 88 | \n { yycolumn = 1; } /* ignore whitespace */ 89 | 90 | %% 91 | 92 | /** 93 | * yyerror() is invoked when the lexer or the parser encounter 94 | * an error. The error message is passed via *s 95 | * 96 | * 97 | */ 98 | void yyerror(char *s) { 99 | printf("error: %s at line: %d\n",s,yylineno); 100 | } 101 | 102 | int yywrap(void) { 103 | return 1; 104 | } -------------------------------------------------------------------------------- /src/parser/parse.h: -------------------------------------------------------------------------------- 1 | #ifndef __QUERY_PARSER_PARSE_H__ 2 | #define __QUERY_PARSER_PARSE_H__ 3 | 4 | #include "ast.h" 5 | //#include "../rmutil/alloc.h" 6 | 7 | typedef struct { 8 | AST_Query *root; 9 | int ok; 10 | char *errorMsg; 11 | } parseCtx; 12 | 13 | #endif // !__QUERY_PARSER_PARSE_H__ -------------------------------------------------------------------------------- /src/parser/parser_common.h: -------------------------------------------------------------------------------- 1 | #ifndef __PARSER_COMMON_H__ 2 | #define __PARSER_COMMON_H__ 3 | #include 4 | #include "ast.h" 5 | 6 | AST_Query *Query_Parse(const char *q, size_t len, char **err); 7 | #endif -------------------------------------------------------------------------------- /src/parser/token.h: -------------------------------------------------------------------------------- 1 | #ifndef __TOKEN_H__ 2 | #define __TOKEN_H__ 3 | #include 4 | 5 | typedef struct { 6 | int64_t intval; 7 | double dval; 8 | char *strval; 9 | char *s; // token string 10 | int pos; // position in the query 11 | } Token; 12 | 13 | extern Token tok; 14 | 15 | #endif -------------------------------------------------------------------------------- /src/pytest/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | python -m unittest discover -------------------------------------------------------------------------------- /src/pytest/rmtest/__init__.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from .disposableredis import DisposableRedis 3 | import os 4 | 5 | REDIS_MODULE_PATH_ENVVAR = 'REDIS_MODULE_PATH' 6 | REDIS_PATH_ENVVAR = 'REDIS_PATH' 7 | REDIS_PORT_ENVVAR = 'REDIS_PORT' 8 | 9 | 10 | def ModuleTestCase(module_path, redis_path='redis-server', fixed_port=None): 11 | 12 | module_path = os.getenv(REDIS_MODULE_PATH_ENVVAR, module_path) 13 | redis_path = os.getenv(REDIS_PATH_ENVVAR, redis_path) 14 | fixed_port = os.getenv(REDIS_PORT_ENVVAR, fixed_port) 15 | 16 | class _ModuleTestCase(unittest.TestCase): 17 | 18 | _module_path = os.path.abspath(os.path.join(os.getcwd(), module_path)) 19 | _redis_path = redis_path 20 | 21 | def redis(self, port=None): 22 | if fixed_port is not None: 23 | port = fixed_port 24 | return DisposableRedis(port=port, path=self._redis_path, loadmodule=self._module_path) 25 | 26 | def assertOk(self, x): 27 | self.assertEquals("OK", x) 28 | 29 | def assertExists(self, r, key): 30 | self.assertTrue(r.exists(key)) 31 | 32 | return _ModuleTestCase 33 | -------------------------------------------------------------------------------- /src/pytest/rmtest/disposableredis/__init__.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import socket 3 | import tempfile 4 | import redis 5 | import time 6 | import os 7 | import itertools 8 | from contextlib import contextmanager 9 | 10 | 11 | def get_random_port(): 12 | sock = socket.socket() 13 | sock.listen(0) 14 | _, port = sock.getsockname() 15 | sock.close() 16 | 17 | return port 18 | 19 | 20 | class Client(redis.StrictRedis): 21 | 22 | def __init__(self, disposable_redis, port): 23 | 24 | redis.StrictRedis.__init__(self, port=port) 25 | self.dr = disposable_redis 26 | 27 | def retry_with_rdb_reload(self): 28 | 29 | yield 1 30 | self.dr.dump_and_reload() 31 | yield 2 32 | 33 | class DisposableRedis(object): 34 | 35 | def __init__(self, port=None, path='redis-server', **extra_args): 36 | """ 37 | :param port: port number to start the redis server on. Specify none to automatically generate 38 | :type port: int|None 39 | :param extra_args: any extra arguments kwargs will be passed to redis server as --key val 40 | """ 41 | 42 | self._port = port 43 | 44 | # this will hold the actual port the redis is listening on. It's equal to `_port` unless `_port` is None 45 | # in that case `port` is randomly generated 46 | self.port = None 47 | self.extra_args = list(itertools.chain( 48 | *(('--%s' % k, v) for k, v in extra_args.items()) 49 | )) 50 | self.path = path 51 | self.dumped = False 52 | 53 | def _startProcess(self): 54 | 55 | self.process = subprocess.Popen( 56 | self.args, 57 | stdin=subprocess.PIPE, 58 | stdout=open(os.devnull, 'w') 59 | ) 60 | 61 | while True: 62 | try: 63 | self.client().ping() 64 | break 65 | except redis.ConnectionError: 66 | self.process.poll() 67 | if self.process.returncode is not None: 68 | raise RuntimeError( 69 | "Process has exited with code {}".format(self.process.returncode)) 70 | time.sleep(0.1) 71 | 72 | def __enter__(self): 73 | if self._port is None: 74 | self.port = get_random_port() 75 | else: 76 | self.port = self._port 77 | 78 | self.dumpfile = 'dump.%s.rdb' % self.port 79 | self.args = [self.path, 80 | '--port', str(self.port), 81 | '--dir', tempfile.gettempdir(), 82 | '--save', '', 83 | '--dbfilename', self.dumpfile] + self.extra_args 84 | 85 | 86 | self._startProcess() 87 | return self.client() 88 | 89 | def __exit__(self, exc_type, exc_val, exc_tb): 90 | self.process.terminate() 91 | if self.dumped: 92 | os.unlink(os.path.join(tempfile.gettempdir(), self.dumpfile)) 93 | 94 | def dump_and_reload(self): 95 | 96 | conn = self.client() 97 | conn.save() 98 | self.dumped = True 99 | self.process.terminate() 100 | self._startProcess() 101 | 102 | def client(self): 103 | """ 104 | :rtype: redis.StrictRedis 105 | """ 106 | 107 | return Client(self, self.port) 108 | -------------------------------------------------------------------------------- /src/query_executor.h: -------------------------------------------------------------------------------- 1 | #ifndef __QUERY_EXECUTOR_H 2 | #define __QUERY_EXECUTOR_H 3 | 4 | #include "graph/graph.h" 5 | #include "parser/ast.h" 6 | #include "redismodule.h" 7 | #include "hexastore/triplet.h" 8 | #include "arithmetic/arithmetic_expression.h" 9 | 10 | /* Given AST's MATCH node constructs a graph 11 | * representing queried entities and the relationships 12 | * between them. */ 13 | void BuildGraph(Graph *graph, Vector *entities); 14 | 15 | /* Constructs an arithmetic expression tree foreach none aggregated term. */ 16 | void Build_None_Aggregated_Arithmetic_Expressions(AST_ReturnNode *return_node, AR_ExpNode ***expressions, int *expressions_count, Graph* g); 17 | 18 | /* Checks if query performs write (Create/Delete/Update) */ 19 | int Query_Modifies_KeySpace(const AST_Query *ast); 20 | 21 | /* AST alterations. */ 22 | void Modify_AST(RedisModuleCtx *ctx, AST_Query *ast, const char *graphName); 23 | 24 | AST_Query* ParseQuery(const char *query, size_t qLen, char **errMsg); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/resultset/record.c: -------------------------------------------------------------------------------- 1 | #include "record.h" 2 | #include "../rmutil/strings.h" 3 | #include "../query_executor.h" 4 | 5 | Record* NewRecord(size_t len) { 6 | Record *r = (Record*)malloc(sizeof(Record)); 7 | r->len = len; 8 | r->values = malloc(sizeof(SIValue) * len); 9 | return r; 10 | } 11 | 12 | /* Creates a new result-set record from an aggregated group. */ 13 | Record* Record_FromGroup(const ResultSetHeader *resultset_header, const Group *g) { 14 | Record *r = NewRecord(resultset_header->columns_len); 15 | 16 | int key_idx = 0; 17 | int agg_idx = 0; 18 | 19 | /* Add group elements according to specified return order. */ 20 | for(int i = 0; i < resultset_header->columns_len; i++) { 21 | if(resultset_header->columns[i]->aggregated) { 22 | AR_ExpNode *agg_exp; 23 | Vector_Get(g->aggregationFunctions, agg_idx, &agg_exp); 24 | AR_EXP_Reduce(agg_exp); 25 | r->values[i] = AR_EXP_Evaluate(agg_exp); 26 | agg_idx++; 27 | } else { 28 | r->values[i] = g->keys[key_idx]; 29 | key_idx++; 30 | } 31 | } 32 | 33 | return r; 34 | } 35 | 36 | size_t Record_ToString(const Record *record, char **record_str) { 37 | return SIValue_StringConcat(record->values, record->len, record_str); 38 | } 39 | 40 | int Records_Compare(const Record *A, const Record *B, int* compareIndices, size_t compareIndicesLen) { 41 | SIValue aValue; 42 | SIValue bValue; 43 | 44 | for(int i = 0; i < compareIndicesLen; i++) { 45 | /* Get element index to comapre. */ 46 | int index = compareIndices[i]; 47 | aValue = A->values[index]; 48 | bValue = B->values[index]; 49 | 50 | /* Asuuming both values are of type double. */ 51 | if(aValue.doubleval > bValue.doubleval) { 52 | return 1; 53 | } else if(aValue.doubleval < bValue.doubleval) { 54 | return -1; 55 | } 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | /* Frees given record. */ 62 | void Record_Free(Record *r) { 63 | if(r == NULL) return; 64 | free(r->values); 65 | free(r); 66 | } 67 | -------------------------------------------------------------------------------- /src/resultset/record.h: -------------------------------------------------------------------------------- 1 | #ifndef __GRAPH_RECORD_H__ 2 | #define __GRAPH_RECORD_H__ 3 | 4 | #include "resultset_header.h" 5 | #include "../parser/ast.h" 6 | #include "../grouping/group.h" 7 | #include "../graph/graph.h" 8 | 9 | typedef struct { 10 | unsigned int len; 11 | SIValue *values; 12 | } Record; 13 | 14 | /* Creates a new record which will hold len elements. */ 15 | Record* NewRecord(size_t len); 16 | 17 | /* Creates a new record from an aggregated group. */ 18 | Record* Record_FromGroup(const ResultSetHeader *resultset_header, const Group *g); 19 | 20 | /* Get a string representation of record. */ 21 | size_t Record_ToString(const Record *record, char **record_str); 22 | 23 | /* Compares the two records, using the values at given indeces 24 | * Returns 1 if A >= B, -1 if A <= B, 0 if A = B */ 25 | int Records_Compare(const Record *A, const Record *B, int *compareIndices, size_t compareIndicesLen); 26 | 27 | /* Frees given record. */ 28 | void Record_Free(Record *r); 29 | 30 | #endif -------------------------------------------------------------------------------- /src/resultset/resultset.h: -------------------------------------------------------------------------------- 1 | #ifndef __GRAPH_RESULTSET_H__ 2 | #define __GRAPH_RESULTSET_H__ 3 | 4 | #include "record.h" 5 | #include "resultset_header.h" 6 | #include "../parser/ast.h" 7 | #include "../redismodule.h" 8 | #include "../rmutil/vector.h" 9 | #include "../util/heap.h" 10 | #include "../dep/rax/rax.h" 11 | 12 | #define RESULTSET_UNLIMITED 0 13 | #define RESULTSET_OK 1 14 | #define RESULTSET_FULL 0 15 | 16 | #define DIR_DESC -1 17 | #define DIR_ASC 1 18 | 19 | typedef struct { 20 | Vector* records; /* Vector of Records. */ 21 | heap_t* heap; /* Holds top n records. */ 22 | rax* trie; /* When using distinct, used to identify unique records. */ 23 | AST_Query* ast; 24 | ResultSetHeader* header; /* Describes how records should look like. */ 25 | int aggregated; /* Rather or not this is an aggregated result set. */ 26 | int ordered; /* Rather or not this result set is ordered. */ 27 | int direction; /* Sort direction ASC/DESC. */ 28 | int limit; /* Max number of records in result-set. */ 29 | int distinct; /* Rather or not each record is unique. */ 30 | int labels_added; /* Number of labels added as part of a create query. */ 31 | int nodes_created; /* Number of nodes created as part of a create query. */ 32 | int properties_set; /* Number of properties created as part of a create query. */ 33 | int relationships_created; /* Number of edges created as part of a create query. */ 34 | int nodes_deleted; /* Number of nodes removed as part of a delete query.*/ 35 | int relationships_deleted; /* Number of edges removed as part of a delete query.*/ 36 | } ResultSet; 37 | 38 | ResultSet* NewResultSet(AST_Query* ast); 39 | 40 | int ResultSet_AddRecord(ResultSet* set, Record *record); 41 | 42 | void ResultSet_Free(RedisModuleCtx* ctx, ResultSet* set); 43 | 44 | int ResultSet_Full(const ResultSet* set); 45 | 46 | void ResultSet_Replay(RedisModuleCtx* ctx, ResultSet* set); 47 | 48 | #endif -------------------------------------------------------------------------------- /src/resultset/resultset_header.h: -------------------------------------------------------------------------------- 1 | #ifndef __RESULTSET_HEADER_H__ 2 | #define __RESULTSET_HEADER_H__ 3 | 4 | #include 5 | 6 | /* A column within the result-set 7 | * a column can be referred to either by its name or alias */ 8 | typedef struct { 9 | char* name; 10 | char* alias; 11 | int aggregated; /* 1 if column is aggregated, 0 otherwise. */ 12 | } Column; 13 | 14 | typedef struct { 15 | size_t columns_len; /* Number of columns in record */ 16 | Column** columns; /* Vector of Columns, desired elements specified in return clause */ 17 | size_t orderby_len; /* How many elements are there in orderBys */ 18 | int* orderBys; /* Array of indices into elements */ 19 | } ResultSetHeader; 20 | 21 | #endif -------------------------------------------------------------------------------- /src/rmutil/Makefile: -------------------------------------------------------------------------------- 1 | # set environment variable RM_INCLUDE_DIR to the location of redismodule.h 2 | ifndef RM_INCLUDE_DIR 3 | RM_INCLUDE_DIR=../ 4 | endif 5 | 6 | CFLAGS = -g -fPIC -lc -lm -O3 -std=gnu99 -I$(RM_INCLUDE_DIR) -Wall -Wno-unused-function 7 | CC=gcc 8 | 9 | OBJS=util.o strings.o sds.o vector.o heap.o priority_queue.o 10 | 11 | all: librmutil.a 12 | 13 | clean: 14 | rm -rf *.o *.a 15 | 16 | librmutil.a: $(OBJS) 17 | ar rcs $@ $^ 18 | 19 | test_vector: test_vector.o vector.o 20 | $(CC) -Wall -o test_vector vector.o test_vector.o -lc -O0 21 | @(sh -c ./test_vector) 22 | 23 | test_heap: test_heap.o heap.o vector.o 24 | $(CC) -Wall -o test_heap heap.o vector.o test_heap.o -lc -O0 25 | @(sh -c ./test_heap) 26 | 27 | test_priority_queue: test_priority_queue.o priority_queue.o heap.o vector.o 28 | $(CC) -Wall -o test_priority_queue priority_queue.o heap.o vector.o test_priority_queue.o -lc -O0 29 | @(sh -c ./test_heap) 30 | -------------------------------------------------------------------------------- /src/rmutil/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef __HEAP_H__ 2 | #define __HEAP_H__ 3 | 4 | #include "vector.h" 5 | 6 | 7 | /* Make heap from range 8 | * Rearranges the elements in the range [first,last) in such a way that they form a heap. 9 | * A heap is a way to organize the elements of a range that allows for fast retrieval of the element with the highest 10 | * value at any moment (with pop_heap), even repeatedly, while allowing for fast insertion of new elements (with 11 | * push_heap). 12 | * The element with the highest value is always pointed by first. The order of the other elements depends on the 13 | * particular implementation, but it is consistent throughout all heap-related functions of this header. 14 | * The elements are compared using cmp. 15 | */ 16 | void Make_Heap(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); 17 | 18 | 19 | /* Push element into heap range 20 | * Given a heap in the range [first,last-1), this function extends the range considered a heap to [first,last) by 21 | * placing the value in (last-1) into its corresponding location within it. 22 | * A range can be organized into a heap by calling make_heap. After that, its heap properties are preserved if elements 23 | * are added and removed from it using push_heap and pop_heap, respectively. 24 | */ 25 | void Heap_Push(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); 26 | 27 | 28 | /* Pop element from heap range 29 | * Rearranges the elements in the heap range [first,last) in such a way that the part considered a heap is shortened 30 | * by one: The element with the highest value is moved to (last-1). 31 | * While the element with the highest value is moved from first to (last-1) (which now is out of the heap), the other 32 | * elements are reorganized in such a way that the range [first,last-1) preserves the properties of a heap. 33 | * A range can be organized into a heap by calling make_heap. After that, its heap properties are preserved if elements 34 | * are added and removed from it using push_heap and pop_heap, respectively. 35 | */ 36 | void Heap_Pop(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); 37 | 38 | #endif //__HEAP_H__ 39 | -------------------------------------------------------------------------------- /src/rmutil/priority_queue.c: -------------------------------------------------------------------------------- 1 | #include "priority_queue.h" 2 | #include "heap.h" 3 | 4 | PriorityQueue *__newPriorityQueueSize(size_t elemSize, size_t cap, int (*cmp)(void *, void *)) { 5 | PriorityQueue *pq = malloc(sizeof(PriorityQueue)); 6 | pq->v = __newVectorSize(elemSize, cap); 7 | pq->cmp = cmp; 8 | return pq; 9 | } 10 | 11 | inline size_t Priority_Queue_Size(PriorityQueue *pq) { 12 | return Vector_Size(pq->v); 13 | } 14 | 15 | inline int Priority_Queue_Top(PriorityQueue *pq, void *ptr) { 16 | return Vector_Get(pq->v, 0, ptr); 17 | } 18 | 19 | inline size_t __priority_Queue_PushPtr(PriorityQueue *pq, void *elem) { 20 | size_t top = __vector_PushPtr(pq->v, elem); 21 | Heap_Push(pq->v, 0, top, pq->cmp); 22 | return top; 23 | } 24 | 25 | inline void Priority_Queue_Pop(PriorityQueue *pq) { 26 | if (pq->v->top == 0) { 27 | return; 28 | } 29 | Heap_Pop(pq->v, 0, pq->v->top, pq->cmp); 30 | pq->v->top--; 31 | } 32 | 33 | void Priority_Queue_Free(PriorityQueue *pq) { 34 | Vector_Free(pq->v); 35 | free(pq); 36 | } 37 | -------------------------------------------------------------------------------- /src/rmutil/priority_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __PRIORITY_QUEUE_H__ 2 | #define __PRIORITY_QUEUE_H__ 3 | 4 | #include "vector.h" 5 | 6 | /* Priority queue 7 | * Priority queues are designed such that its first element is always the greatest of the elements it contains. 8 | * This context is similar to a heap, where elements can be inserted at any moment, and only the max heap element can be 9 | * retrieved (the one at the top in the priority queue). 10 | * Priority queues are implemented as Vectors. Elements are popped from the "back" of Vector, which is known as the top 11 | * of the priority queue. 12 | */ 13 | typedef struct { 14 | Vector *v; 15 | 16 | int (*cmp)(void *, void *); 17 | } PriorityQueue; 18 | 19 | /* Construct priority queue 20 | * Constructs a priority_queue container adaptor object. 21 | */ 22 | PriorityQueue *__newPriorityQueueSize(size_t elemSize, size_t cap, int (*cmp)(void *, void *)); 23 | 24 | #define NewPriorityQueue(type, cap, cmp) __newPriorityQueueSize(sizeof(type), cap, cmp) 25 | 26 | /* Return size 27 | * Returns the number of elements in the priority_queue. 28 | */ 29 | size_t Priority_Queue_Size(PriorityQueue *pq); 30 | 31 | /* Access top element 32 | * Copy the top element in the priority_queue to ptr. 33 | * The top element is the element that compares higher in the priority_queue. 34 | */ 35 | int Priority_Queue_Top(PriorityQueue *pq, void *ptr); 36 | 37 | /* Insert element 38 | * Inserts a new element in the priority_queue. 39 | */ 40 | size_t __priority_Queue_PushPtr(PriorityQueue *pq, void *elem); 41 | 42 | #define Priority_Queue_Push(pq, elem) __priority_Queue_PushPtr(pq, &(typeof(elem)){elem}) 43 | 44 | /* Remove top element 45 | * Removes the element on top of the priority_queue, effectively reducing its size by one. The element removed is the 46 | * one with the highest value. 47 | * The value of this element can be retrieved before being popped by calling Priority_Queue_Top. 48 | */ 49 | void Priority_Queue_Pop(PriorityQueue *pq); 50 | 51 | /* free the priority queue and the underlying data. Does not release its elements if 52 | * they are pointers */ 53 | void Priority_Queue_Free(PriorityQueue *pq); 54 | 55 | #endif //__PRIORITY_QUEUE_H__ 56 | -------------------------------------------------------------------------------- /src/rmutil/sdsalloc.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Redis Labs, Inc 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /* SDS allocator selection. 33 | * 34 | * This file is used in order to change the SDS allocator at compile time. 35 | * Just define the following defines to what you want to use. Also add 36 | * the include of your alternate allocator if needed (not needed in order 37 | * to use the default libc allocator). */ 38 | 39 | //#include "zmalloc.h" 40 | #define s_malloc malloc 41 | #define s_realloc realloc 42 | #define s_free free 43 | -------------------------------------------------------------------------------- /src/rmutil/strings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "strings.h" 5 | 6 | 7 | #include "sds.h" 8 | 9 | RedisModuleString *RMUtil_CreateFormattedString(RedisModuleCtx *ctx, const char *fmt, ...) { 10 | sds s = sdsempty(); 11 | 12 | va_list ap; 13 | va_start(ap, fmt); 14 | s = sdscatvprintf(s, fmt, ap); 15 | va_end(ap); 16 | 17 | RedisModuleString *ret = RedisModule_CreateString(ctx, (const char *)s, sdslen(s)); 18 | sdsfree(s); 19 | return ret; 20 | } 21 | 22 | int RMUtil_StringEquals(RedisModuleString *s1, RedisModuleString *s2) { 23 | const char *c1, *c2; 24 | size_t l1, l2; 25 | c1 = RedisModule_StringPtrLen(s1, &l1); 26 | c2 = RedisModule_StringPtrLen(s2, &l2); 27 | if (l1 != l2) return 0; 28 | 29 | return strncmp(c1, c2, l1) == 0; 30 | } 31 | 32 | int RMUtil_StringEqualsC(RedisModuleString *s1, const char *s2) { 33 | const char *c1; 34 | size_t l1, l2 = strlen(s2); 35 | c1 = RedisModule_StringPtrLen(s1, &l1); 36 | if (l1 != l2) return 0; 37 | 38 | return strncmp(c1, s2, l1) == 0; 39 | } 40 | 41 | void RMUtil_StringToLower(RedisModuleString *s) { 42 | size_t l; 43 | char *c = (char *)RedisModule_StringPtrLen(s, &l); 44 | size_t i; 45 | for (i = 0; i < l; i++) { 46 | *c = tolower(*c); 47 | ++c; 48 | } 49 | } 50 | 51 | void RMUtil_StringToUpper(RedisModuleString *s) { 52 | size_t l; 53 | char *c = (char *)RedisModule_StringPtrLen(s, &l); 54 | size_t i; 55 | for (i = 0; i < l; i++) { 56 | *c = toupper(*c); 57 | ++c; 58 | } 59 | } 60 | 61 | void RMUtil_StringConcat(const Vector* rmStrings, const char* delimiter, char** concat) { 62 | size_t length = 0; 63 | 64 | // Compute length 65 | for(int i = 0; i < Vector_Size(rmStrings); i++) { 66 | RedisModuleString* string; 67 | Vector_Get(rmStrings, i, &string); 68 | 69 | size_t len; 70 | RedisModule_StringPtrLen(string, &len); 71 | length += len; 72 | } 73 | 74 | length += strlen(delimiter) * Vector_Size(rmStrings) + 1; 75 | *concat = calloc(length, sizeof(char)); 76 | 77 | for(int i = 0; i < Vector_Size(rmStrings); i++) { 78 | RedisModuleString* rmString; 79 | Vector_Get(rmStrings, i, &rmString); 80 | 81 | const char* string = RedisModule_StringPtrLen(rmString, NULL); 82 | 83 | strcat(*concat, string); 84 | strcat(*concat, delimiter); 85 | } 86 | // Discard last delimiter. 87 | (*concat)[strlen(*concat) - strlen(delimiter)] = 0; 88 | } 89 | -------------------------------------------------------------------------------- /src/rmutil/strings.h: -------------------------------------------------------------------------------- 1 | #ifndef __RMUTIL_STRINGS_H__ 2 | #define __RMUTIL_STRINGS_H__ 3 | 4 | #include "../redismodule.h" 5 | #include "./vector.h" 6 | 7 | /* 8 | * Create a new RedisModuleString object from a printf-style format and arguments. 9 | * Note that RedisModuleString objects CANNOT be used as formatting arguments. 10 | */ 11 | RedisModuleString *RMUtil_CreateFormattedString(RedisModuleCtx *ctx, const char *fmt, ...); 12 | 13 | /* Return 1 if the two strings are equal. Case *sensitive* */ 14 | int RMUtil_StringEquals(RedisModuleString *s1, RedisModuleString *s2); 15 | 16 | /* Return 1 if the string is equal to a C NULL terminated string. Case *sensitive* */ 17 | int RMUtil_StringEqualsC(RedisModuleString *s1, const char *s2); 18 | 19 | /* Converts a redis string to lowercase in place without reallocating anything */ 20 | void RMUtil_StringToLower(RedisModuleString *s); 21 | 22 | /* Converts a redis string to uppercase in place without reallocating anything */ 23 | void RMUtil_StringToUpper(RedisModuleString *s); 24 | 25 | /* Concat redis strings within vector using delimiter */ 26 | void RMUtil_StringConcat(const Vector* rmStrings, const char* delimiter, char** concat); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/rmutil/test_heap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/src/rmutil/test_heap -------------------------------------------------------------------------------- /src/rmutil/test_heap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "heap.h" 3 | #include "assert.h" 4 | 5 | int cmp(void *a, void *b) { 6 | int *__a = (int *) a; 7 | int *__b = (int *) b; 8 | return *__a - *__b; 9 | } 10 | 11 | int main(int argc, char **argv) { 12 | int myints[] = {10, 20, 30, 5, 15}; 13 | Vector *v = NewVector(int, 5); 14 | for (int i = 0; i < 5; i++) { 15 | Vector_Push(v, myints[i]); 16 | } 17 | 18 | Make_Heap(v, 0, v->top, cmp); 19 | 20 | int n; 21 | Vector_Get(v, 0, &n); 22 | assert(30 == n); 23 | 24 | Heap_Pop(v, 0, v->top, cmp); 25 | v->top = 4; 26 | Vector_Get(v, 0, &n); 27 | assert(20 == n); 28 | 29 | Vector_Push(v, 99); 30 | Heap_Push(v, 0, v->top, cmp); 31 | Vector_Get(v, 0, &n); 32 | assert(99 == n); 33 | 34 | Vector_Free(v); 35 | printf("PASS!"); 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/rmutil/test_priority_queue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/src/rmutil/test_priority_queue -------------------------------------------------------------------------------- /src/rmutil/test_priority_queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "assert.h" 3 | #include "priority_queue.h" 4 | 5 | int cmp(void* i1, void* i2) { 6 | int *__i1 = (int*) i1; 7 | int *__i2 = (int*) i2; 8 | return *__i1 - *__i2; 9 | } 10 | 11 | int main(int argc, char **argv) { 12 | PriorityQueue *pq = NewPriorityQueue(int, 10, cmp); 13 | assert(0 == Priority_Queue_Size(pq)); 14 | 15 | for (int i = 0; i < 5; i++) { 16 | Priority_Queue_Push(pq, i); 17 | } 18 | assert(5 == Priority_Queue_Size(pq)); 19 | 20 | Priority_Queue_Pop(pq); 21 | assert(4 == Priority_Queue_Size(pq)); 22 | 23 | Priority_Queue_Push(pq, 10); 24 | Priority_Queue_Push(pq, 20); 25 | Priority_Queue_Push(pq, 15); 26 | int n; 27 | Priority_Queue_Top(pq, &n); 28 | assert(20 == n); 29 | 30 | Priority_Queue_Pop(pq); 31 | Priority_Queue_Top(pq, &n); 32 | assert(15 == n); 33 | 34 | Priority_Queue_Free(pq); 35 | printf("PASS!"); 36 | return 0; 37 | } -------------------------------------------------------------------------------- /src/rmutil/test_util.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEST_UTIL_H__ 2 | #define __TEST_UTIL_H__ 3 | 4 | #include "util.h" 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #define RMUtil_Test(f) \ 11 | if (argc < 2 || RMUtil_ArgExists(__STRING(f), argv, argc, 1)) { \ 12 | int rc = f(ctx); \ 13 | if (rc != REDISMODULE_OK) { \ 14 | RedisModule_ReplyWithError(ctx, "Test " __STRING(f) " FAILED"); \ 15 | return REDISMODULE_ERR;\ 16 | }\ 17 | } 18 | 19 | 20 | #define RMUtil_Assert(expr) if (!(expr)) { fprintf (stderr, "Assertion '%s' Failed\n", __STRING(expr)); return REDISMODULE_ERR; } 21 | 22 | #define RMUtil_AssertReplyEquals(rep, cstr) RMUtil_Assert( \ 23 | RMUtil_StringEquals(RedisModule_CreateStringFromCallReply(rep), RedisModule_CreateString(ctx, cstr, strlen(cstr))) \ 24 | ) 25 | # 26 | 27 | /** 28 | * Create an arg list to pass to a redis command handler manually, based on the format in fmt. 29 | * The accepted format specifiers are: 30 | * c - for null terminated c strings 31 | * s - for RedisModuleString* objects 32 | * l - for longs 33 | * 34 | * Example: RMUtil_MakeArgs(ctx, &argc, "clc", "hello", 1337, "world"); 35 | * 36 | * Returns an array of RedisModuleString pointers. The size of the array is store in argcp 37 | */ 38 | RedisModuleString **RMUtil_MakeArgs(RedisModuleCtx *ctx, int *argcp, const char *fmt, ...) { 39 | 40 | va_list ap; 41 | va_start(ap, fmt); 42 | RedisModuleString **argv = calloc(strlen(fmt), sizeof(RedisModuleString*)); 43 | int argc = 0; 44 | const char *p = fmt; 45 | while(*p) { 46 | if (*p == 'c') { 47 | char *cstr = va_arg(ap,char*); 48 | argv[argc++] = RedisModule_CreateString(ctx, cstr, strlen(cstr)); 49 | } else if (*p == 's') { 50 | argv[argc++] = va_arg(ap,void*);; 51 | } else if (*p == 'l') { 52 | long ll = va_arg(ap,long long); 53 | argv[argc++] = RedisModule_CreateStringFromLongLong(ctx, ll); 54 | } else { 55 | goto fmterr; 56 | } 57 | p++; 58 | } 59 | *argcp = argc; 60 | 61 | return argv; 62 | fmterr: 63 | free(argv); 64 | return NULL; 65 | } 66 | 67 | #endif -------------------------------------------------------------------------------- /src/rmutil/test_vector: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/src/rmutil/test_vector -------------------------------------------------------------------------------- /src/rmutil/test_vector.c: -------------------------------------------------------------------------------- 1 | #include "vector.h" 2 | #include 3 | #include "assert.h" 4 | 5 | int main(int argc, char **argv) { 6 | 7 | 8 | Vector *v = NewVector(int, 1); 9 | 10 | // Vector_Put(v, 0, 1); 11 | // Vector_Put(v, 1, 3); 12 | for (int i = 0; i < 10; i++) { 13 | Vector_Push(v, i); 14 | } 15 | 16 | for (int i = 0; i < Vector_Size(v); i++) { 17 | int n; 18 | int rc = Vector_Get(v, i, &n); 19 | printf("%d %d\n", rc, n); 20 | assert ( 1== rc ); 21 | assert (n == i); 22 | } 23 | 24 | Vector_Free(v); 25 | 26 | v = NewVector(char *, 0); 27 | int N = 4; 28 | char *strings[4] = {"hello", "world", "foo", "bar"}; 29 | 30 | for (int i = 0; i < N; i++) { 31 | Vector_Push(v, strings[i]); 32 | } 33 | assert(Vector_Size(v) == N); 34 | assert(Vector_Cap(v) >= N); 35 | 36 | for (size_t i = 0; i < Vector_Size(v); i++) { 37 | char *x; 38 | int rc = Vector_Get(v, i, &x); 39 | assert (rc == 1); 40 | assert (!strcmp(x, strings[i])); 41 | } 42 | 43 | int rc = Vector_Get(v, 100, NULL); 44 | assert (rc == 0); 45 | 46 | Vector_Free(v); 47 | printf("PASS!"); 48 | 49 | return 0; 50 | //Vector_Push(v, "hello"); 51 | //Vector_Push(v, "world"); 52 | // char *x = NULL; 53 | // int rc = Vector_Getx(v, 0, &x); 54 | // printf("rc: %d got %s\n", rc, x); 55 | 56 | } -------------------------------------------------------------------------------- /src/rmutil/vector.c: -------------------------------------------------------------------------------- 1 | #include "vector.h" 2 | #include 3 | 4 | inline int __vector_PushPtr(Vector *v, void *elem) { 5 | if (v->top == v->cap) { 6 | Vector_Resize(v, v->cap ? v->cap * 2 : 1); 7 | } 8 | 9 | __vector_PutPtr(v, v->top++, elem); 10 | return v->top; 11 | } 12 | 13 | inline int Vector_Get(const Vector *v, size_t pos, void *ptr) { 14 | // return 0 if pos is out of bounds 15 | if (pos >= v->top) { 16 | return 0; 17 | } 18 | 19 | memcpy(ptr, v->data + (pos * v->elemSize), v->elemSize); 20 | return 1; 21 | } 22 | 23 | /* Get the element at the end of the vector, decreasing the size by one */ 24 | inline int Vector_Pop(Vector *v, void *ptr) { 25 | if (v->top > 0) { 26 | if (ptr != NULL) { 27 | Vector_Get(v, v->top - 1, ptr); 28 | } 29 | v->top--; 30 | return 1; 31 | } 32 | return 0; 33 | } 34 | 35 | inline int __vector_PutPtr(Vector *v, size_t pos, void *elem) { 36 | // resize if pos is out of bounds 37 | if (pos >= v->cap) { 38 | Vector_Resize(v, pos + 1); 39 | } 40 | 41 | if (elem) { 42 | memcpy(v->data + pos * v->elemSize, elem, v->elemSize); 43 | } else { 44 | memset(v->data + pos * v->elemSize, 0, v->elemSize); 45 | } 46 | // move the end offset to pos if we grew 47 | if (pos > v->top) { 48 | v->top = pos; 49 | } 50 | return 1; 51 | } 52 | 53 | int Vector_Resize(Vector *v, size_t newcap) { 54 | int oldcap = v->cap; 55 | v->cap = newcap; 56 | 57 | v->data = realloc(v->data, v->cap * v->elemSize); 58 | 59 | // If we grew: 60 | // put all zeros at the newly realloc'd part of the vector 61 | if (newcap > oldcap) { 62 | int offset = oldcap * v->elemSize; 63 | memset(v->data + offset, 0, v->cap * v->elemSize - offset); 64 | } 65 | return v->cap; 66 | } 67 | 68 | Vector *__newVectorSize(size_t elemSize, size_t cap) { 69 | Vector *vec = malloc(sizeof(Vector)); 70 | vec->data = calloc(cap, elemSize); 71 | vec->top = 0; 72 | vec->elemSize = elemSize; 73 | vec->cap = cap; 74 | 75 | return vec; 76 | } 77 | 78 | void Vector_Free(Vector *v) { 79 | free(v->data); 80 | free(v); 81 | } 82 | 83 | inline int Vector_Size(const Vector *v) { return v->top; } 84 | 85 | /* return the actual capacity */ 86 | inline int Vector_Cap(Vector *v) { return v->cap; } 87 | 88 | // Convenience function for debugging vector elements 89 | void* Vector_Val(Vector *v, size_t pos) { 90 | if (pos >= v->top) return NULL; 91 | 92 | return v->data + (pos * v->elemSize); 93 | } 94 | -------------------------------------------------------------------------------- /src/rmutil/vector.h: -------------------------------------------------------------------------------- 1 | #ifndef __VECTOR_H__ 2 | #define __VECTOR_H__ 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * Generic resizable vector that can be used if you just want to store stuff 9 | * temporarily. 10 | * Works like C++ std::vector with an underlying resizable buffer 11 | */ 12 | typedef struct { 13 | char *data; 14 | size_t elemSize; 15 | size_t cap; 16 | size_t top; 17 | 18 | } Vector; 19 | 20 | /* Create a new vector with element size. This should generally be used 21 | * internall by the NewVector macro */ 22 | Vector *__newVectorSize(size_t elemSize, size_t cap); 23 | 24 | // Put a pointer in the vector. To be used internally by the library 25 | int __vector_PutPtr(Vector *v, size_t pos, void *elem); 26 | 27 | /* 28 | * Create a new vector for a given type and a given capacity. 29 | * e.g. NewVector(int, 0) - empty vector of ints 30 | */ 31 | #define NewVector(type, cap) __newVectorSize(sizeof(type), cap) 32 | 33 | /* 34 | * get the element at index pos. The value is copied in to ptr. If pos is outside 35 | * the vector capacity, we return 0 36 | * otherwise 1 37 | */ 38 | int Vector_Get(const Vector *v, size_t pos, void *ptr); 39 | 40 | /* Get the element at the end of the vector, decreasing the size by one */ 41 | int Vector_Pop(Vector *v, void *ptr); 42 | 43 | //#define Vector_Getx(v, pos, ptr) pos < v->cap ? 1 : 0; *ptr = 44 | //*(typeof(ptr))(v->data + v->elemSize*pos) 45 | 46 | /* 47 | * Put an element at pos. 48 | * Note: If pos is outside the vector capacity, we resize it accordingly 49 | */ 50 | #define Vector_Put(v, pos, elem) \ 51 | __vector_PutPtr(v, pos, elem ? &(typeof(elem)){elem} : NULL) 52 | 53 | /* Push an element at the end of v, resizing it if needed. This macro wraps 54 | * __vector_PushPtr */ 55 | #define Vector_Push(v, elem) \ 56 | __vector_PushPtr(v, elem ? &(typeof(elem)){elem} : NULL) 57 | 58 | int __vector_PushPtr(Vector *v, void *elem); 59 | 60 | /* resize capacity of v */ 61 | int Vector_Resize(Vector *v, size_t newcap); 62 | 63 | /* return the used size of the vector, regardless of capacity */ 64 | int Vector_Size(const Vector *v); 65 | 66 | /* return the actual capacity */ 67 | int Vector_Cap(Vector *v); 68 | 69 | /* free the vector and the underlying data. Does not release its elements if 70 | * they are pointers*/ 71 | void Vector_Free(Vector *v); 72 | 73 | void* Vector_Val(Vector *v, size_t pos); 74 | 75 | #endif -------------------------------------------------------------------------------- /src/stores/store.h: -------------------------------------------------------------------------------- 1 | #ifndef __LABEL_STORE_H__ 2 | #define __LABEL_STORE_H__ 3 | 4 | #include "../redismodule.h" 5 | #include "../graph/graph_entity.h" 6 | #include "../dep/rax/rax.h" 7 | 8 | #define LABELSTORE_PREFIX "redis_graph_store" 9 | 10 | typedef enum { 11 | STORE_NODE, 12 | STORE_EDGE, 13 | } LabelStoreType; 14 | 15 | /* Keeps track on label schema. */ 16 | typedef struct { 17 | rax *properties; /* Entities under this Label, have a subset of properties */ 18 | } LabelStatistics; 19 | 20 | typedef struct { 21 | rax *items; 22 | LabelStatistics stats; 23 | char *label; 24 | } LabelStore; 25 | 26 | typedef raxIterator LabelStoreIterator; 27 | 28 | /* Generates an ID for a new LabelStore. */ 29 | int LabelStore_Id(char **id, LabelStoreType type, const char *graph, const char *label); 30 | 31 | /* Get a label store. */ 32 | LabelStore *LabelStore_Get(RedisModuleCtx *ctx, LabelStoreType type, const char *graph, const char* label); 33 | 34 | /* Get all stores of given type. */ 35 | void LabelStore_Get_ALL(RedisModuleCtx *ctx, LabelStoreType type, const char *graph, LabelStore **stores, size_t *stores_len); 36 | 37 | /* Returns the number of items within the store */ 38 | int LabelStore_Cardinality(LabelStore *store); 39 | 40 | /* Inserts a new graph entity. */ 41 | void LabelStore_Insert(LabelStore *store, char *id, GraphEntity *entity); 42 | 43 | /* Removes entity with ID. */ 44 | int LabelStore_Remove(LabelStore *store, char *id); 45 | 46 | /* Scans through the entire store. */ 47 | void LabelStore_Scan(LabelStore *store, LabelStoreIterator *it); 48 | 49 | /* Free store. */ 50 | void LabelStore_Free(LabelStore *store); 51 | 52 | /* Returns the next id from cursor. */ 53 | int LabelStoreIterator_Next(LabelStoreIterator *cursor, char **key, uint16_t *len, void **value); 54 | 55 | /* Free iterator. */ 56 | void LabelStoreIterator_Free(LabelStoreIterator* iterator); 57 | 58 | #endif /* __LABEL_STORE_H__ */ -------------------------------------------------------------------------------- /src/util/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAP_H 2 | #define HEAP_H 3 | #include 4 | 5 | typedef struct heap_s heap_t; 6 | 7 | /** 8 | * Create new heap and initialise it. 9 | * 10 | * malloc()s space for heap. 11 | * 12 | * @param[in] cmp Callback used to get an item's priority 13 | * @param[in] udata User data passed through to cmp callback 14 | * @return initialised heap */ 15 | heap_t *heap_new(int (*cmp) (const void *, 16 | const void *, 17 | const void *udata), 18 | const void *udata); 19 | 20 | /** 21 | * Initialise heap. Use memory passed by user. 22 | * 23 | * No malloc()s are performed. 24 | * 25 | * @param[in] cmp Callback used to get an item's priority 26 | * @param[in] udata User data passed through to cmp callback 27 | * @param[in] size Initial size of the heap's array */ 28 | void heap_init(heap_t* h, 29 | int (*cmp) (const void *, 30 | const void *, 31 | const void *udata), 32 | const void *udata, 33 | unsigned int size); 34 | 35 | void heap_free(heap_t * hp); 36 | 37 | /** 38 | * Add item 39 | * 40 | * Ensures that the data structure can hold the item. 41 | * 42 | * NOTE: 43 | * realloc() possibly called. 44 | * The heap pointer will be changed if the heap needs to be enlarged. 45 | * 46 | * @param[in/out] hp_ptr Pointer to the heap. Changed when heap is enlarged. 47 | * @param[in] item The item to be added 48 | * @return 0 on success; -1 on failure */ 49 | int heap_offer(heap_t **hp_ptr, void *item); 50 | 51 | /** 52 | * Add item 53 | * 54 | * An error will occur if there isn't enough space for this item. 55 | * 56 | * NOTE: 57 | * no malloc()s called. 58 | * 59 | * @param[in] item The item to be added 60 | * @return 0 on success; -1 on error */ 61 | int heap_offerx(heap_t * hp, void *item); 62 | 63 | /** 64 | * Remove the item with the top priority 65 | * 66 | * @return top item */ 67 | void *heap_poll(heap_t * hp); 68 | 69 | /** 70 | * @return top item of the heap */ 71 | void *heap_peek(const heap_t * hp); 72 | 73 | /** 74 | * Clear all items 75 | * 76 | * NOTE: 77 | * Does not free items. 78 | * Only use if item memory is managed outside of heap */ 79 | void heap_clear(heap_t * hp); 80 | 81 | /** 82 | * @return number of items in heap */ 83 | int heap_count(const heap_t * hp); 84 | 85 | /** 86 | * @return size of array */ 87 | int heap_size(const heap_t * hp); 88 | 89 | /** 90 | * @return number of bytes needed for a heap of this size. */ 91 | size_t heap_sizeof(unsigned int size); 92 | 93 | /** 94 | * Remove item 95 | * 96 | * @param[in] item The item that is to be removed 97 | * @return item to be removed; NULL if item does not exist */ 98 | void *heap_remove_item(heap_t * hp, const void *item); 99 | 100 | /** 101 | * Test membership of item 102 | * 103 | * @param[in] item The item to test 104 | * @return 1 if the heap contains this item; otherwise 0 */ 105 | int heap_contains_item(const heap_t * hp, const void *item); 106 | 107 | #endif /* HEAP_H */ -------------------------------------------------------------------------------- /src/util/prng.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "prng.h" 4 | #include "sha1.h" 5 | #include "snowflake.h" 6 | 7 | long int get_new_id() { 8 | return snowflake_id(); 9 | } 10 | -------------------------------------------------------------------------------- /src/util/prng.h: -------------------------------------------------------------------------------- 1 | #ifndef PRNG_H 2 | #define PRNG_H 3 | 4 | long int get_new_id(); 5 | 6 | #endif -------------------------------------------------------------------------------- /src/util/sha1.h: -------------------------------------------------------------------------------- 1 | #ifndef SHA1_H 2 | #define SHA1_H 3 | 4 | #include 5 | /* ================ sha1.h ================ */ 6 | /* 7 | SHA-1 in C 8 | By Steve Reid 9 | 100% Public Domain 10 | */ 11 | 12 | typedef struct { 13 | uint32_t state[5]; 14 | uint32_t count[2]; 15 | unsigned char buffer[64]; 16 | } SHA1_CTX; 17 | 18 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); 19 | void SHA1Init(SHA1_CTX* context); 20 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); 21 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context); 22 | 23 | #endif -------------------------------------------------------------------------------- /src/util/skiplist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2014, Salvatore Sanfilippo 3 | * Copyright (c) 2009-2014, Pieter Noordhuis 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Disque nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __DISQUE_SKIPLIST_H 32 | #define __DISQUE_SKIPLIST_H 33 | 34 | #define SKIPLIST_MAXLEVEL 32 /* Should be enough for 2^32 elements */ 35 | #define SKIPLIST_P 0.25 /* Skiplist P = 1/4 */ 36 | 37 | typedef struct skiplistNode { 38 | void *key; 39 | void **vals; 40 | unsigned int numVals; 41 | unsigned int valsAllocated; 42 | struct skiplistNode *backward; 43 | struct skiplistLevel { 44 | struct skiplistNode *forward; 45 | unsigned int span; 46 | } level[]; 47 | } skiplistNode; 48 | 49 | typedef int (*skiplistCmpFunc)(void *p1, void *p2, void *ctx); 50 | typedef int (*skiplistValCmpFunc)(const void *p1, const void *p2); 51 | 52 | typedef void (*skiplistCloneKeyFunc)(void **key); 53 | typedef void (*skiplistFreeKeyFunc)(void *key); 54 | 55 | typedef struct skiplist { 56 | struct skiplistNode *header, *tail; 57 | skiplistCmpFunc compare; 58 | skiplistValCmpFunc valcmp; 59 | 60 | skiplistCloneKeyFunc cloneKey; 61 | skiplistFreeKeyFunc freeKey; 62 | 63 | void *cmpCtx; 64 | unsigned long length; 65 | int level; 66 | } skiplist; 67 | 68 | skiplist *skiplistCreate(skiplistCmpFunc cmp, void *cmpCtx, skiplistValCmpFunc vcmp, 69 | skiplistCloneKeyFunc cloneKey, skiplistFreeKeyFunc freeKey); 70 | void skiplistFree(skiplist *sl); 71 | skiplistNode *skiplistInsert(skiplist *sl, void *key, void *val); 72 | int skiplistDelete(skiplist *sl, void *key, void *val); 73 | skiplistNode *skiplistFind(skiplist *sl, void *key); 74 | skiplistNode *skiplistFindAtLeast(skiplist *sl, void *key, int exclusive); 75 | void *skiplistPopHead(skiplist *sl); 76 | void *skiplistPopTail(skiplist *sl); 77 | 78 | typedef struct { 79 | skiplistNode *current; 80 | unsigned int currentValOffset; 81 | void *rangeMin; 82 | void *rangeMax; 83 | int minExclusive; 84 | int maxExclusive; 85 | skiplist *sl; 86 | } skiplistIterator; 87 | 88 | skiplistIterator* skiplistIterateRange(skiplist *sl, void *min, void *max, 89 | int minExclusive, int maxExclusive); 90 | 91 | skiplistIterator* skiplistIterateAll(skiplist *sl); 92 | 93 | void skiplistIterate_Reset(skiplistIterator *iter); 94 | void skiplistIterate_Free(skiplistIterator *iter); 95 | 96 | void *skiplistIterator_Next(skiplistIterator *it); 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/util/snowflake.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Dwayn Matthies 3 | 4 | Minor adjustments by Eran Sandler 5 | to be used in redissnowflake project 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | */ 25 | 26 | #include "stats.h" 27 | #include "snowflake.h" 28 | #include 29 | #include 30 | 31 | long int snowflake_id() { 32 | struct timeval tp; 33 | gettimeofday(&tp, NULL); 34 | long int millisecs = tp.tv_sec * 1000 + tp.tv_usec / 1000 - SNOWFLAKE_EPOCH; 35 | long int id = 0L; 36 | 37 | // Catch NTP clock adjustment that rolls time backwards and sequence number overflow 38 | if ((snowflake_global_state.seq > snowflake_global_state.seq_max ) || snowflake_global_state.time > millisecs) { 39 | ++app_stats.waits; 40 | while (snowflake_global_state.time >= millisecs) { 41 | gettimeofday(&tp, NULL); 42 | millisecs = tp.tv_sec * 1000 + tp.tv_usec / 1000 - SNOWFLAKE_EPOCH; 43 | } 44 | } 45 | 46 | if (snowflake_global_state.time < millisecs) { 47 | snowflake_global_state.time = millisecs; 48 | snowflake_global_state.seq = 0L; 49 | } 50 | 51 | 52 | id = (millisecs << snowflake_global_state.time_shift_bits) 53 | | (snowflake_global_state.region_id << snowflake_global_state.region_shift_bits) 54 | | (snowflake_global_state.worker_id << snowflake_global_state.worker_shift_bits) 55 | | (snowflake_global_state.seq++); 56 | 57 | if (app_stats.seq_max < snowflake_global_state.seq) 58 | app_stats.seq_max = snowflake_global_state.seq; 59 | 60 | ++app_stats.ids; 61 | return id; 62 | } 63 | 64 | int snowflake_init(int region_id, int worker_id) { 65 | int max_region_id = (1 << SNOWFLAKE_REGIONID_BITS) - 1; 66 | if(region_id < 0 || region_id > max_region_id){ 67 | //printf("Region ID must be in the range : 0-%d\n", max_region_id); 68 | return -1; 69 | } 70 | int max_worker_id = (1 << SNOWFLAKE_WORKERID_BITS) - 1; 71 | if(worker_id < 0 || worker_id > max_worker_id){ 72 | //printf("Worker ID must be in the range: 0-%d\n", max_worker_id); 73 | return -1; 74 | } 75 | 76 | snowflake_global_state.time_shift_bits = SNOWFLAKE_REGIONID_BITS + SNOWFLAKE_WORKERID_BITS + SNOWFLAKE_SEQUENCE_BITS; 77 | snowflake_global_state.region_shift_bits = SNOWFLAKE_WORKERID_BITS + SNOWFLAKE_SEQUENCE_BITS; 78 | snowflake_global_state.worker_shift_bits = SNOWFLAKE_SEQUENCE_BITS; 79 | 80 | snowflake_global_state.worker_id = worker_id; 81 | snowflake_global_state.region_id = region_id; 82 | snowflake_global_state.seq_max = (1L << SNOWFLAKE_SEQUENCE_BITS) - 1; 83 | snowflake_global_state.seq = 0L; 84 | snowflake_global_state.time = 0L; 85 | app_stats.seq_cap = snowflake_global_state.seq_max; 86 | app_stats.waits = 0L; 87 | app_stats.seq_max = 0L; 88 | app_stats.ids = 0L; 89 | app_stats.region_id = region_id; 90 | app_stats.worker_id = worker_id; 91 | return 1; 92 | } -------------------------------------------------------------------------------- /src/util/snowflake.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Dwayn Matthies 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __SNOWFLAKE__ 24 | #define __SNOWFLAKE__ 25 | 26 | // the timestamp in milliseconds of the start of the custom epoch 27 | #define SNOWFLAKE_EPOCH 1388534400000 //Midnight January 1, 2014 28 | 29 | #define SNOWFLAKE_TIME_BITS 41 30 | #define SNOWFLAKE_REGIONID_BITS 4 31 | #define SNOWFLAKE_WORKERID_BITS 10 32 | #define SNOWFLAKE_SEQUENCE_BITS 8 33 | 34 | struct _snowflake_state { 35 | // milliseconds since SNOWFLAKE_EPOCH 36 | long int time; 37 | long int seq_max; 38 | long int worker_id; 39 | long int region_id; 40 | long int seq; 41 | long int time_shift_bits; 42 | long int region_shift_bits; 43 | long int worker_shift_bits; 44 | } snowflake_global_state; 45 | 46 | long int snowflake_id(); 47 | int snowflake_init(int region_id, int worker_id); 48 | 49 | #endif /* __SNOWFLAKE__ */ -------------------------------------------------------------------------------- /src/util/stats.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Dwayn Matthies 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __SNOWFLAKED_STATS__ 24 | #define __SNOWFLAKED_STATS__ 25 | 26 | #include 27 | 28 | 29 | struct _app_stats { 30 | time_t started_at; 31 | char *version; 32 | long int ids; 33 | long int waits; 34 | long int seq_max; 35 | int region_id; 36 | int worker_id; 37 | long int seq_cap; 38 | } app_stats; 39 | 40 | 41 | 42 | #endif -------------------------------------------------------------------------------- /src/value.h: -------------------------------------------------------------------------------- 1 | #ifndef __SECONDARY_VALUE_H__ 2 | #define __SECONDARY_VALUE_H__ 3 | #include 4 | #include 5 | #include 6 | #include "./rmutil/vector.h" 7 | 8 | typedef char *SIId; 9 | 10 | /* Type defines the supported types by the indexing system. The types are powers 11 | * of 2 so they can be used in bitmasks of matching types */ 12 | typedef enum { 13 | T_NULL = 0, 14 | T_STRING = 0x001, 15 | T_INT32 = 0x002, 16 | T_INT64 = 0x004, 17 | T_UINT = 0x008, 18 | T_BOOL = 0x010, 19 | T_FLOAT = 0x020, 20 | T_DOUBLE = 0x040, 21 | T_PTR = 0x080, 22 | 23 | // special types for +inf and -inf on all types: 24 | T_INF = 0x100, 25 | T_NEGINF = 0x200, 26 | 27 | } SIType; 28 | 29 | #define SI_NUMERIC (T_INT32 | T_INT64 | T_UINT | T_FLOAT | T_DOUBLE) 30 | 31 | typedef struct { 32 | union { 33 | int32_t intval; 34 | int64_t longval; 35 | u_int64_t uintval; 36 | float floatval; 37 | double doubleval; 38 | int boolval; 39 | char *stringval; 40 | void* ptrval; 41 | }; 42 | SIType type; 43 | } SIValue; 44 | 45 | typedef struct { 46 | SIValue *vals; 47 | size_t len; 48 | size_t cap; 49 | } SIValueVector; 50 | 51 | SIValueVector SI_NewValueVector(size_t cap); 52 | 53 | /* Free an SIValue. Since we usually allocate values on the stack, this does not 54 | * free the actual value object, but the underlying value if needed - basically 55 | * when it's a string */ 56 | void SIValue_Free(SIValue *v); 57 | 58 | void SIValueVector_Append(SIValueVector *v, SIValue val); 59 | void SIValueVector_Free(SIValueVector *v); 60 | 61 | SIValue SI_StringVal(const char *s); 62 | SIValue SI_IntVal(int i); 63 | SIValue SI_LongVal(int64_t i); 64 | SIValue SI_UintVal(u_int64_t i); 65 | SIValue SI_FloatVal(float f); 66 | SIValue SI_DoubleVal(double d); 67 | SIValue SI_NullVal(); 68 | SIValue SI_BoolVal(int b); 69 | SIValue SI_PtrVal(void* v); 70 | SIValue SI_Clone(SIValue v); 71 | 72 | int SIValue_IsNull(SIValue v); 73 | int SIValue_IsNullPtr(SIValue *v); 74 | 75 | SIValue SI_InfVal(); 76 | SIValue SI_NegativeInfVal(); 77 | int SIValue_IsInf(SIValue *v); 78 | int SIValue_IsNegativeInf(SIValue *v); 79 | 80 | /* 81 | * Conversion functions used to make sure a comparison value in a query is of 82 | * the right type 83 | */ 84 | int SI_LongVal_Cast(SIValue *v, SIType type); 85 | int SI_DoubleVal_Cast(SIValue *v, SIType type); 86 | int SI_StringVal_Cast(SIValue *v, SIType type); 87 | 88 | /* Try to parse a value by string. The value's type should be set to 89 | * anything other than T_NULL, to force strict parsing. */ 90 | int SI_ParseValue(SIValue *v, char *str); 91 | 92 | int SIValue_ToString(SIValue v, char *buf, size_t len); 93 | 94 | int SIValue_ToDouble(SIValue *v, double *d); 95 | 96 | /* Try to parse a value by string. */ 97 | void SIValue_FromString(SIValue *v, char *s); 98 | 99 | /* Concats strings as a comma seperated string. */ 100 | size_t SIValue_StringConcat(SIValue* strings, unsigned int string_count, char** concat); 101 | 102 | void SIValue_Print(FILE *outstream, SIValue *v); 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/value_cmp.c: -------------------------------------------------------------------------------- 1 | #include "value.h" 2 | #include "value_cmp.h" 3 | 4 | #include 5 | #include 6 | 7 | // Comparators for all simple types 8 | GENERIC_CMP_FUNC_IMPL(cmp_float, floatval); 9 | GENERIC_CMP_FUNC_IMPL(cmp_int, intval); 10 | GENERIC_CMP_FUNC_IMPL(cmp_long, longval); 11 | GENERIC_CMP_FUNC_IMPL(cmp_double, doubleval); 12 | GENERIC_CMP_FUNC_IMPL(cmp_uint, uintval); 13 | 14 | int cmp_string(void *p1, void *p2) { 15 | SIValue *v1 = p1, *v2 = p2; 16 | if (SIValue_IsInf(v1) || SIValue_IsNegativeInf(v2)) 17 | return 1; 18 | if (SIValue_IsInf(v2) || SIValue_IsNegativeInf(v1)) 19 | return -1; 20 | 21 | 22 | int v1_len = strlen(v1->stringval); 23 | int v2_len = strlen(v2->stringval); 24 | // compare the longest length possible, which is the shortest length of the 25 | // two strings 26 | int cmp = strncasecmp(v1->stringval, v2->stringval, 27 | MIN(v1_len, v2_len)); 28 | 29 | // if the strings are equal at the common length but are not of the same 30 | // length, the longer string wins 31 | if (cmp == 0 && v1_len != v2_len) { 32 | return v1_len > v2_len ? 1 : -1; 33 | } 34 | // if they are not equal, or equal and same length - return the original cmp 35 | return cmp; 36 | } 37 | -------------------------------------------------------------------------------- /src/value_cmp.h: -------------------------------------------------------------------------------- 1 | #ifndef __CMP_H__ 2 | #define __CMP_H__ 3 | 4 | #include 5 | #include "value.h" 6 | 7 | typedef int (*CmpFunc)(void *p1, void *p2); 8 | 9 | #define GENERIC_CMP_FUNC_DECL(F) int F(void *p1, void *p2); 10 | 11 | #define GENERIC_CMP_FUNC_IMPL(F, memb) \ 12 | int F(void *p1, void *p2) { \ 13 | SIValue *v1 = p1, *v2 = p2; \ 14 | if (SIValue_IsInf(v1) || SIValue_IsNegativeInf(v2)) \ 15 | return 1; \ 16 | if (SIValue_IsInf(v2) || SIValue_IsNegativeInf(v1)) \ 17 | return -1; \ 18 | return (v1->memb < v2->memb ? -1 : (v1->memb > v2->memb ? 1 : 0)); \ 19 | } 20 | 21 | 22 | GENERIC_CMP_FUNC_DECL(cmp_string); 23 | GENERIC_CMP_FUNC_DECL(cmp_float); 24 | GENERIC_CMP_FUNC_DECL(cmp_int); 25 | GENERIC_CMP_FUNC_DECL(cmp_long); 26 | GENERIC_CMP_FUNC_DECL(cmp_double); 27 | GENERIC_CMP_FUNC_DECL(cmp_uint); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | # unit tests 3 | $(MAKE) -C unit all 4 | 5 | # flow tests 6 | python -m unittest discover --verbose 7 | 8 | clean: 9 | find . -name '*.[oad]' -type f -delete 10 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/tests/__init__.py -------------------------------------------------------------------------------- /tests/flow/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swilly22/redis-graph/84e819b194a100a654c3766f994a7d8af42cd711/tests/flow/__init__.py -------------------------------------------------------------------------------- /tests/flow/base.py: -------------------------------------------------------------------------------- 1 | import os 2 | import warnings 3 | from rmtest import ModuleTestCase 4 | 5 | 6 | class FlowTestsBase(ModuleTestCase(os.path.dirname(os.path.abspath(__file__)) + '/../../src/redisgraph.so')): 7 | 8 | def _skip_header_row(self, resultset): 9 | self.assertGreaterEqual(len(resultset), 1) 10 | return resultset[1:] 11 | 12 | def _assert_number_of_results(self, actual_resultset, expected_resultset): 13 | self.assertEqual(len(actual_resultset), len(expected_resultset)) 14 | 15 | # Make sure resultset is as expected, but we don't care for the order 16 | # of the columns. 17 | def _assert_results_ignore_col_order(self, actual_result, query_info): 18 | actual_result_set = self._skip_header_row(actual_result.result_set) 19 | 20 | # assert num results 21 | self._assert_number_of_results(actual_result_set, query_info.expected_result) 22 | 23 | # As we don't care for the order of the columns 24 | # replace row array representation with set. 25 | for i in range(len(query_info.expected_result)): 26 | query_info.expected_result[i] = set(query_info.expected_result[i]) 27 | actual_result_set[i] = set(actual_result_set[i]) 28 | 29 | # assert actual values vs expected values 30 | for res in query_info.expected_result: 31 | self.assertTrue(res in actual_result_set, 32 | 'The item %s is NOT in the actual result\n' 33 | 'The actual result: %s\nThe expected result: %s' % 34 | (str(res), str(actual_result_set), str(query_info.expected_result))) 35 | 36 | def _assert_only_expected_resuls_are_in_actual_results(self, 37 | actual_result, 38 | query_info): 39 | actual_result_set = self._skip_header_row(actual_result.result_set) 40 | 41 | # assert num results 42 | self._assert_number_of_results(actual_result_set, query_info.expected_result) 43 | 44 | # assert actual values vs expected values 45 | for res in query_info.expected_result: 46 | self.assertTrue(res in actual_result_set, 47 | 'The item %s is NOT in the actual result\n' 48 | 'The actual result: %s\nThe expected result: %s' % 49 | (str(res), str(actual_result_set), str(query_info.expected_result))) 50 | 51 | def _assert_actual_results_contained_in_expected_results(self, 52 | actual_result, 53 | query_info, 54 | num_contained_results): 55 | actual_result_set = self._skip_header_row(actual_result.result_set) 56 | 57 | # assert num results 58 | self.assertEqual(len(actual_result_set), num_contained_results) 59 | 60 | # assert actual values vs expected values 61 | expected_result = query_info.expected_result 62 | count = len([res for res in expected_result if res in actual_result_set]) 63 | 64 | # assert number of different results is as expected 65 | self.assertEqual(count, 66 | num_contained_results, 67 | 'The actual result is: %s\nThe expected result is: %s' % 68 | (str(actual_result_set), str(query_info.expected_result))) 69 | 70 | def _assert_run_time(self, actual_result, query_info): 71 | if actual_result.run_time_ms > query_info.max_run_time_ms: 72 | warnings.warn('Query \"%s\" execution took too long' % query_info.description, 73 | Warning) 74 | -------------------------------------------------------------------------------- /tests/flow/requirments.txt: -------------------------------------------------------------------------------- 1 | redis==2.10.5 2 | redisgraph 3 | rmtest -------------------------------------------------------------------------------- /tests/unit/.gitignore: -------------------------------------------------------------------------------- 1 | # Extension-less binaries 2 | * 3 | !/**/ 4 | !*.* 5 | 6 | # Object files 7 | *.o 8 | *.ko 9 | *.obj 10 | *.elf -------------------------------------------------------------------------------- /tests/unit/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS:=$(CFLAGS) 2 | CC_OBJECTS:=$(CC_OBJECTS) 3 | LIBRMUTIL=../../src/rmutil/librmutil.a 4 | 5 | LIBS=$(LIBRMUTIL) 6 | DEPS=$(CC_OBJECTS) $(LIBS) 7 | 8 | SRCDIR := $(shell pwd) 9 | LDFLAGS := -lc -lm -ldl -lpthread 10 | 11 | CC=gcc 12 | .SUFFIXES: .c .so .xo .o 13 | 14 | agg_sum: test_agg_sum.o 15 | $(CC) $(CFLAGS) -o test_agg_sum test_agg_sum.o $(DEPS) $(LDFLAGS) 16 | test_agg_sum: 17 | @(sh -c ./test_agg_sum) 18 | .PHONY: test_agg_sum 19 | 20 | aggregate_functions: test_aggregate_functions.o 21 | $(CC) $(CFLAGS) -o test_aggregate_functions test_aggregate_functions.o $(DEPS) $(LDFLAGS) 22 | test_aggregate_functions: 23 | @(sh -c ./test_aggregate_functions) 24 | .PHONY: test_aggregate_functions 25 | 26 | edge: test_edge.o 27 | $(CC) $(CFLAGS) -o test_edge test_edge.o $(DEPS) $(LDFLAGS) 28 | test_edge: 29 | @(sh -c ./test_edge) 30 | .PHONY: test_edge 31 | 32 | filter_tree: test_filter_tree.o 33 | $(CC) $(CFLAGS) -o test_filter_tree test_filter_tree.o $(DEPS) $(LDFLAGS) 34 | test_filter_tree: 35 | @(sh -c ./test_filter_tree) 36 | .PHONY: test_filter_tree 37 | 38 | graph: test_graph.o 39 | $(CC) $(CFLAGS) -o test_graph test_graph.o $(DEPS) $(LDFLAGS) 40 | test_graph: 41 | @(sh -c ./test_graph) 42 | .PHONY: test_graph 43 | 44 | hexastore: test_hexastore.o 45 | $(CC) $(CFLAGS) -o test_hexastore test_hexastore.o $(DEPS) $(LDFLAGS) 46 | test_hexastore: 47 | @(sh -c ./test_hexastore) 48 | .PHONY: test_hexastore 49 | 50 | node: test_node.o 51 | $(CC) $(CFLAGS) -o test_node test_node.o $(DEPS) $(LDFLAGS) 52 | test_node: 53 | @(sh -c ./test_node) 54 | .PHONY: test_node 55 | 56 | resultset_record: test_resultset_record.o 57 | $(CC) $(CFLAGS) -o test_resultset_record test_resultset_record.o $(DEPS) $(LDFLAGS) 58 | test_resultset_record: 59 | @(sh -c ./test_resultset_record) 60 | .PHONY: test_resultset_record 61 | 62 | triplet: test_triplet.o 63 | $(CC) $(CFLAGS) -o test_triplet test_triplet.o $(DEPS) $(LDFLAGS) 64 | test_triplet: 65 | @(sh -c ./test_triplet) 66 | .PHONY: test_triplet 67 | 68 | value: test_value.o 69 | $(CC) $(CFLAGS) -o test_value test_value.o $(DEPS) $(LDFLAGS) 70 | test_value: 71 | @(sh -c ./test_value) 72 | .PHONY: test_value 73 | 74 | arithmetic_expression: test_arithmetic_expression.o 75 | $(CC) $(CFLAGS) -o test_arithmetic_expression test_arithmetic_expression.o $(DEPS) $(LDFLAGS) 76 | test_arithmetic_expression: 77 | @(sh -c ./test_arithmetic_expression) 78 | .PHONY: test_arithmetic_expression 79 | 80 | ast: test_ast.o 81 | $(CC) $(CFLAGS) -o test_ast test_ast.o $(DEPS) $(LDFLAGS) 82 | test_ast: 83 | @(sh -c ./test_ast) 84 | .PHONY: test_ast 85 | 86 | skiplist_generic: test_skiplist_generic.o 87 | $(CC) $(CFLAGS) -o test_skiplist_generic test_skiplist_generic.o $(DEPS) $(LDFLAGS) 88 | test_skiplist_generic: 89 | @(sh -c ./test_skiplist_generic) 90 | .PHONY: test_skiplist_generic 91 | 92 | skiplist_graph: test_skiplist_graph.o 93 | $(CC) $(CFLAGS) -o test_skiplist_graph test_skiplist_graph.o $(DEPS) $(LDFLAGS) 94 | test_skiplist_graph: 95 | @(sh -c ./test_skiplist_graph) 96 | .PHONY: test_skiplist_graph 97 | 98 | all: build test 99 | 100 | build: agg_sum edge node graph value triplet resultset_record hexastore filter_tree arithmetic_expression aggregate_functions ast skiplist_generic skiplist_graph 101 | 102 | test: test_agg_sum test_edge test_node test_graph test_value test_triplet test_resultset_record test_hexastore test_filter_tree test_arithmetic_expression test_aggregate_functions test_ast test_skiplist_generic test_skiplist_graph 103 | -------------------------------------------------------------------------------- /tests/unit/test_agg_sum.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "assert.h" 4 | #include "../../src/arithmetic/agg_funcs.h" 5 | #include "../../src/value.h" 6 | 7 | int main(int argc, char **argv) { 8 | 9 | // Empty node 10 | AggCtx* sumCtx = Agg_SumFunc(); 11 | double sum = 0; 12 | SIValue item; 13 | 14 | for(int i = 0; i < 10; i++) { 15 | item = SI_IntVal(i); 16 | sumCtx->Step(sumCtx, &item, 1); 17 | sum += i; 18 | } 19 | 20 | sumCtx->ReduceNext(sumCtx); 21 | assert(sumCtx->result.doubleval == sum); 22 | 23 | printf("test_agg_sum - PASS!\n"); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /tests/unit/test_edge.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../../src/graph/node.h" 4 | #include "../../src/graph/edge.h" 5 | #include "assert.h" 6 | 7 | void test_edge_creation() { 8 | Node *src_node = NewNode(1l, "person"); 9 | Node *dest_node = NewNode(2l, "city"); 10 | Edge *edge = NewEdge(3l, src_node, dest_node, "lives"); 11 | 12 | assert(edge->src == src_node); 13 | assert(edge->dest == dest_node); 14 | assert(strcmp(edge->relationship, "lives") == 0); 15 | assert(edge->prop_count == 0); 16 | 17 | FreeNode(src_node); 18 | FreeNode(dest_node); 19 | FreeEdge(edge); 20 | } 21 | 22 | void test_edge_props() { 23 | Node *src_node = NewNode(1l, "person"); 24 | Node *dest_node = NewNode(2l, "city"); 25 | Edge *edge = NewEdge(3l, src_node, dest_node, "lives"); 26 | 27 | char *keys[2] = {"rent", "since"}; 28 | SIValue vals[2] = { SI_BoolVal(1), SI_UintVal(1984)}; 29 | 30 | GraphEntity_Add_Properties((GraphEntity*)edge, 2, keys, vals); 31 | 32 | SIValue *val = GraphEntity_Get_Property((GraphEntity*)edge, "rent"); 33 | assert(val->boolval == 1); 34 | 35 | val = GraphEntity_Get_Property((GraphEntity*)edge, "since"); 36 | assert(val->intval == 1984); 37 | 38 | val = GraphEntity_Get_Property((GraphEntity*)edge, "fake"); 39 | assert(val == PROPERTY_NOTFOUND); 40 | 41 | FreeNode(src_node); 42 | FreeNode(dest_node); 43 | FreeEdge(edge); 44 | } 45 | 46 | int main(int argc, char **argv) { 47 | test_edge_creation(); 48 | test_edge_props(); 49 | printf("test_edge - PASS!\n"); 50 | return 0; 51 | } -------------------------------------------------------------------------------- /tests/unit/test_filter_tree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "assert.h" 4 | #include "../../src/rmutil/vector.h" 5 | #include "../../src/parser/grammar.h" 6 | #include "../../src/filter_tree/filter_tree.h" 7 | 8 | void compareFilterTreeVaryingNode(const FT_FilterNode *a, const FT_FilterNode *b) { 9 | assert(a->t == b->t); 10 | assert(a->pred.op == b->pred.op); 11 | assert(a->pred.t == b->pred.t); 12 | assert(a->pred.cf == b->pred.cf); 13 | assert(strcmp(a->pred.Lop.alias, b->pred.Lop.alias) == 0); 14 | assert(strcmp(a->pred.Lop.property, b->pred.Lop.property) == 0); 15 | assert(strcmp(a->pred.Rop.alias, b->pred.Rop.alias) == 0); 16 | assert(strcmp(a->pred.Rop.property, b->pred.Rop.property) == 0); 17 | } 18 | 19 | void compareFilterTreeConstNode(const FT_FilterNode *a, const FT_FilterNode *b) { 20 | assert(a->t == b->t); 21 | assert(a->pred.op == b->pred.op); 22 | assert(a->pred.t == b->pred.t); 23 | assert(a->pred.cf == b->pred.cf); 24 | assert(strcmp(a->pred.Lop.alias, b->pred.Lop.alias) == 0); 25 | assert(strcmp(a->pred.Lop.property, b->pred.Lop.property) == 0); 26 | assert(a->pred.constVal.type == b->pred.constVal.type); 27 | } 28 | 29 | void compareFilterTrees(const FT_FilterNode *a, const FT_FilterNode *b) { 30 | assert(a->t == b->t); 31 | 32 | if(a->t == FT_N_PRED) { 33 | if(a->pred.t == FT_N_CONSTANT) { 34 | compareFilterTreeConstNode(a, b); 35 | } else { 36 | compareFilterTreeVaryingNode(a, b); 37 | } 38 | } else { 39 | assert(a->cond.op == b->cond.op); 40 | compareFilterTrees(a->cond.left, b->cond.left); 41 | compareFilterTrees(a->cond.right, b->cond.right); 42 | } 43 | } 44 | 45 | int main(int argc, char **argv) { 46 | printf("test_filter_tree - PASS!\n"); 47 | return 0; 48 | } -------------------------------------------------------------------------------- /tests/unit/test_hexastore.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "assert.h" 4 | #include "../../src/util/prng.h" 5 | #include "../../src/hexastore/hexastore.h" 6 | #include "../../src/hexastore/triplet.h" 7 | 8 | void test_hexastore() { 9 | Triplet *t; 10 | TripletIterator it; 11 | long int id; 12 | 13 | id = get_new_id(); 14 | Node *subject_node = NewNode(id, "actor"); 15 | id = get_new_id(); 16 | Node *object_node = NewNode(id, "movie"); 17 | id = get_new_id(); 18 | Edge *predicate_edge = NewEdge(id, subject_node, object_node, "act"); 19 | Triplet *triplet = NewTriplet(subject_node, predicate_edge, object_node); 20 | 21 | HexaStore *hexastore = _NewHexaStore(); 22 | assert(hexastore); 23 | 24 | HexaStore_InsertAllPerm(hexastore, triplet); 25 | assert(raxSize(hexastore)); 26 | 27 | /* Search hexastore. 28 | * Scan entire hexastore. */ 29 | HexaStore_Search(hexastore, "", &it); 30 | 31 | for(int i = 0; i < 6; i++) { 32 | assert(TripletIterator_Next(&it, &t)); 33 | assert(t == triplet); 34 | } 35 | assert(!TripletIterator_Next(&it, &t)); 36 | 37 | /* Searching all possible permutations. */ 38 | HexaStore_Search(hexastore, "SPO", &it); 39 | assert(TripletIterator_Next(&it, &t)); 40 | assert(!TripletIterator_Next(&it, &t)); 41 | 42 | HexaStore_Search(hexastore, "SOP", &it); 43 | assert(TripletIterator_Next(&it, &t)); 44 | assert(!TripletIterator_Next(&it, &t)); 45 | 46 | HexaStore_Search(hexastore, "PSO", &it); 47 | assert(TripletIterator_Next(&it, &t)); 48 | assert(!TripletIterator_Next(&it, &t)); 49 | 50 | HexaStore_Search(hexastore, "POS", &it); 51 | assert(TripletIterator_Next(&it, &t)); 52 | assert(!TripletIterator_Next(&it, &t)); 53 | 54 | HexaStore_Search(hexastore, "OSP", &it); 55 | assert(TripletIterator_Next(&it, &t)); 56 | assert(!TripletIterator_Next(&it, &t)); 57 | 58 | HexaStore_Search(hexastore, "OPS", &it); 59 | assert(TripletIterator_Next(&it, &t)); 60 | assert(!TripletIterator_Next(&it, &t)); 61 | 62 | HexaStore_RemoveAllPerm(hexastore, triplet); 63 | assert(raxSize(hexastore) == 0); 64 | 65 | /* Searching an empty hexastore */ 66 | HexaStore_Search(hexastore, "", &it); 67 | assert(!TripletIterator_Next(&it, &t)); 68 | } 69 | 70 | int main(int argc, char **argv) { 71 | test_hexastore(); 72 | printf("test_hexastore - PASS!\n"); 73 | return 0; 74 | } -------------------------------------------------------------------------------- /tests/unit/test_node.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "assert.h" 4 | #include "../../src/graph/node.h" 5 | #include "../../src/graph/edge.h" 6 | 7 | void test_node_creation() { 8 | Node *node = NewNode(1l, "city"); 9 | 10 | assert(node != NULL); 11 | assert(node->id == 1l); 12 | assert(node->prop_count == 0); 13 | assert(node->properties == NULL); 14 | assert(Vector_Size(node->outgoing_edges) == 0); 15 | assert(Vector_Size(node->incoming_edges) == 0); 16 | assert(strcmp(node->label, "city") == 0); 17 | FreeNode(node); 18 | } 19 | 20 | void test_node_props() { 21 | Node *node = NewNode(1l, "city"); 22 | 23 | char *keys[2] = {"neighborhood", "block"}; 24 | SIValue vals[2] = { SI_StringVal("rambam"), SI_IntVal(10)}; 25 | 26 | GraphEntity_Add_Properties((GraphEntity*)node, 2, keys, vals); 27 | 28 | SIValue *val = GraphEntity_Get_Property((GraphEntity*)node, "neighborhood"); 29 | assert(strcmp(val->stringval, "rambam") == 0); 30 | 31 | val = GraphEntity_Get_Property((GraphEntity*)node, "block"); 32 | assert(val->intval == 10); 33 | 34 | val = GraphEntity_Get_Property((GraphEntity*)node, "fake"); 35 | assert(val == PROPERTY_NOTFOUND); 36 | 37 | FreeNode(node); 38 | } 39 | 40 | void test_node_edges() { 41 | Node *src = NewNode(1l, "city"); 42 | Node *dest = NewNode(2l, "flight"); 43 | Edge *edge = NewEdge(3l, src, dest, "lands"); 44 | 45 | Node_ConnectNode(src, dest, edge); 46 | 47 | assert(Node_IncomeDegree(src) == 0); 48 | assert(Node_IncomeDegree(dest) == 1); 49 | 50 | FreeNode(src); 51 | FreeNode(dest); 52 | FreeEdge(edge); 53 | } 54 | 55 | int main(int argc, char **argv) { 56 | test_node_creation(); 57 | test_node_props(); 58 | test_node_edges(); 59 | printf("test_node - PASS!\n"); 60 | return 0; 61 | } -------------------------------------------------------------------------------- /tests/unit/test_resultset_record.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "assert.h" 3 | #include "../../src/resultset/record.h" 4 | #include "../../src/value.h" 5 | 6 | void test_record_to_string () { 7 | Record *record = NewRecord(6); 8 | SIValue v_string = SI_StringVal("Hello"); 9 | SIValue v_int = SI_IntVal(-24); 10 | SIValue v_uint = SI_UintVal(24); 11 | SIValue v_float = SI_FloatVal(0.314); 12 | SIValue v_null = SI_NullVal(); 13 | SIValue v_bool = SI_BoolVal(1); 14 | 15 | record->values[0] = v_string; 16 | record->values[1] = v_int; 17 | record->values[2] = v_uint; 18 | record->values[3] = v_float; 19 | record->values[4] = v_null; 20 | record->values[5] = v_bool; 21 | 22 | char *record_str; 23 | size_t record_str_len = Record_ToString(record, &record_str); 24 | 25 | assert(strcmp(record_str, "Hello,-24,24,0.314000,NULL,true") == 0); 26 | assert(record_str_len == 31); 27 | 28 | free(record_str); 29 | Record_Free(record); 30 | } 31 | 32 | int main(int argc, char **argv) { 33 | test_record_to_string(); 34 | printf("test_resultset_record - PASS!\n"); 35 | return 0; 36 | } -------------------------------------------------------------------------------- /tests/unit/test_value.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "assert.h" 4 | #include "../../src/value.h" 5 | 6 | void test_value_parse() { 7 | SIValue v; 8 | char *str = "12345"; 9 | SIValue_FromString(&v, str); 10 | assert(v.type == T_DOUBLE); 11 | assert(v.doubleval == 12345); 12 | 13 | str = "3.14"; 14 | SIValue_FromString(&v, str); 15 | assert(v.type == T_DOUBLE); 16 | 17 | /* Almost equals. */ 18 | assert((v.doubleval - 3.14) < 0.0001); 19 | 20 | str = "-9876"; 21 | SIValue_FromString(&v, str); 22 | assert(v.type == T_DOUBLE); 23 | assert(v.doubleval == -9876); 24 | 25 | str = "Test!"; 26 | SIValue_FromString(&v, str); 27 | assert(v.type == T_STRING); 28 | assert(strcmp(v.stringval, "Test!") == 0); 29 | } 30 | 31 | int main(int argc, char **argv) { 32 | test_value_parse(); 33 | printf("test_value - PASS!\n"); 34 | return 0; 35 | } --------------------------------------------------------------------------------