├── CMakeLists.txt ├── COPYING ├── README ├── TODO ├── cmake ├── FindApr.cmake ├── FindApu.cmake ├── FindBerkeleyDB.cmake ├── FindBison.cmake ├── FindFlex.cmake └── FindSQLite3.cmake ├── example ├── basic_net.olg ├── facts.olg ├── facts_net.olg ├── loop.olg └── t1.olg └── src ├── CMakeLists.txt ├── bench ├── CMakeLists.txt └── bench.c ├── c4i ├── CMakeLists.txt └── c4i.c ├── libc4 ├── CMakeLists.txt ├── c4-api.c ├── include │ ├── c4-api-callback.h │ ├── c4-api.h │ ├── c4-internal.h │ ├── net │ │ └── network.h │ ├── nodes │ │ ├── copyfuncs.h │ │ ├── makefuncs.h │ │ └── nodes.h │ ├── operator │ │ ├── agg.h │ │ ├── filter.h │ │ ├── insert.h │ │ ├── operator.h │ │ ├── project.h │ │ ├── scan.h │ │ └── scancursor.h │ ├── parser │ │ ├── analyze.h │ │ ├── ast.h │ │ ├── parser-internal.h │ │ ├── parser.h │ │ └── walker.h │ ├── planner │ │ ├── installer.h │ │ ├── planner-internal.h │ │ └── planner.h │ ├── router.h │ ├── runtime.h │ ├── storage │ │ ├── mem_table.h │ │ ├── sqlite.h │ │ ├── sqlite_table.h │ │ └── table.h │ ├── timer.h │ ├── types │ │ ├── agg_funcs.h │ │ ├── catalog.h │ │ ├── datum.h │ │ ├── expr.h │ │ ├── schema.h │ │ └── tuple.h │ └── util │ │ ├── dump_table.h │ │ ├── error.h │ │ ├── hash.h │ │ ├── hash_func.h │ │ ├── list.h │ │ ├── logger.h │ │ ├── mem.h │ │ ├── rbtree.h │ │ ├── rset.h │ │ ├── socket.h │ │ ├── strbuf.h │ │ ├── thread_sync.h │ │ ├── tuple_buf.h │ │ └── tuple_pool.h ├── net │ └── network.c ├── nodes │ ├── copyfuncs.c │ ├── makefuncs.c │ └── nodes.c ├── operator │ ├── agg.c │ ├── filter.c │ ├── insert.c │ ├── operator.c │ ├── project.c │ └── scan.c ├── parser │ ├── analyze.c │ ├── ol_parse.y │ ├── ol_scan.l │ ├── parser.c │ └── walker.c ├── planner │ ├── installer.c │ ├── plan_expr.c │ └── planner.c ├── router.c ├── runtime.c ├── storage │ ├── mem_table.c │ ├── sqlite.c │ ├── sqlite_table.c │ └── table.c ├── timer.c ├── types │ ├── agg_funcs.c │ ├── catalog.c │ ├── datum.c │ ├── expr.c │ ├── schema.c │ └── tuple.c └── util │ ├── dump_table.c │ ├── error.c │ ├── hash.c │ ├── hash_func.c │ ├── list.c │ ├── logger.c │ ├── mem.c │ ├── rset.c │ ├── socket.c │ ├── strbuf.c │ ├── thread_sync.c │ ├── tuple_buf.c │ └── tuple_pool.c └── regress ├── C4.rb ├── README ├── expected ├── aggregation ├── arithmetic ├── fqpath ├── issue_5 ├── join ├── negation ├── not_equal └── parts ├── input ├── aggregation ├── arithmetic ├── fqpath ├── issue_5 ├── join ├── negation ├── not_equal └── parts └── regression.rb /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(C4 C) 3 | subdirs(src) 4 | 5 | if(C4_BUILD_TYPE STREQUAL "Release") 6 | message(STATUS "*** C4 BUILD TYPE: Release ***") 7 | set(CMAKE_BUILD_TYPE Release) 8 | else() 9 | if(DEFINED C4_BUILD_TYPE AND NOT C4_BUILD_TYPE STREQUAL "Debug") 10 | message(FATAL_ERROR "Unrecognized C4 Build Type: ${C4_BUILD_TYPE}") 11 | endif() 12 | message(STATUS "*** C4 BUILD TYPE: Debug ***") 13 | set(CMAKE_BUILD_TYPE Debug) 14 | endif() 15 | 16 | set(PACKAGE_VERSION 1) 17 | set(BUILD_SHARED_LIBS on) 18 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 19 | 20 | find_package(Apr REQUIRED) 21 | find_package(Apu REQUIRED) 22 | find_package(Bison REQUIRED) 23 | find_package(Flex REQUIRED) 24 | find_package(SQLite3 REQUIRED) 25 | 26 | message (STATUS "APR version: ${APR_VERSION}") 27 | message (STATUS "APU version: ${APU_VERSION}") 28 | 29 | set(CMAKE_C_FLAGS "-g -Wall -Wextra -Wredundant-decls -Wstrict-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-prototypes -Winline -pedantic -std=gnu99 -fno-strict-aliasing ${APR_CFLAGS} ${CMAKE_C_FLAGS}") 30 | 31 | if(CMAKE_BUILD_TYPE STREQUAL Debug) 32 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -DC4_ASSERT_ENABLED") 33 | # $APR_CFLAGS often contains -O2, which makes debugging harder 34 | string(REGEX REPLACE "-O2" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) 35 | endif() 36 | 37 | message (STATUS "CFLAGS: ${CMAKE_C_FLAGS}") 38 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Regents of the University of California 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Build dependencies: flex (>= 2.3.35), bison, cmake (>= 2.8), apr (>= 1.4), 2 | apr-util (>= 1.3) and SQLite (>= 3). At the moment, the binaries provided by 3 | each of these packages (flex, bison, apr-1-config, and apu-1-config) must be in 4 | your $PATH. 5 | 6 | To build: 7 | 8 | mkdir build 9 | cd build 10 | cmake .. 11 | make 12 | 13 | By default, this performs a "debug" build: assertions are enabled, and 14 | compiler optimizations are disabled. To disable assertions and enable 15 | optimizations, specify a release build when running cmake: 16 | 17 | cmake -D C4_BUILD_TYPE=Release .. 18 | 19 | To run: 20 | 21 | cd build 22 | ./src/c4i/c4i foo.olg 23 | 24 | Code Style: 25 | 26 | * Use four spaces for indentation, not tabs. 27 | 28 | * When including headers, external headers should be included first, 29 | then "c4-internal.h", and finally other required C4 headers. Within 30 | each group, includes should be sorted alphabetically. 31 | 32 | * Error messages (e.g. ERROR()) should begin with a capital letter. 33 | 34 | * Lines should generally be wrapped at 80 characters. 35 | 36 | * Declarations and statements should NOT be intermixed. 37 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Parser and lexer: 2 | 3 | * Support for parentheses 4 | * Type casts 5 | * Trinary conditional operator ("?:") 6 | * Allow location specifiers to be attached to constants 7 | * Unicode identifiers and string literals 8 | * In string literals, support the "\000" syntax (Octal ASCII 9 | character) from C89; or perhaps some syntax for specifying Unicode 10 | code points? 11 | * Error reporting; location of syntax error 12 | * Support for assignments? 13 | * Warn about redundant joins: "foo(A), foo(A);" is redundant 14 | 15 | Planner: 16 | 17 | * Add support for stratification 18 | * Exploit implied equalities more effectively, suppress duplicate 19 | predicate evaluations 20 | * Avoid Cartesian products, if possible 21 | * We can skip projection in an operator if no operator in the 22 | remainder of the op chain requires values that are NOT in the input 23 | to that operator; e.g. if we do a join and there isn't anything in 24 | the rest of the op chain that needs a value in the scan_rel, we can 25 | skip projection on the output of the join 26 | * This might be complicated by the uniqification of variable names 27 | 28 | Executor/Router/Operators: 29 | 30 | * More efficient joins: the existing approach to joins essentially 31 | re-materializes intermediate join results. Stems and stairs from 32 | earlier Eddies work might point toward a better way of doing this. 33 | * Use a heap to implement timer deadlines 34 | * Aggs: don't emit a deletion/insertion pair if the agg value is 35 | unchanged (e.g. sum<> on input 0, min<> on non-min input, etc.) 36 | * Similarly, if an agg moves from n => m, we currently delete n and insert 37 | m. Might be more efficient to emit a single "update n => m" tuple 38 | * Implement DRED 39 | 40 | Network: 41 | 42 | * Add an ad-hoc compression method, to avoid resending table names in 43 | every single packet 44 | * Have the client and server negotiate that "table 10" means "table 45 | foo, with schema bar" once, and use that information for the 46 | remainder of the session 47 | * Add a UDP transport 48 | * Consider adding an SCTP transport 49 | * Consider adding an SSL-over-TCP transport, and/or secure 50 | communication in general 51 | * Consider adding a multicast transport? 52 | * Consider using TCP_NODELAY in TCP transport 53 | 54 | Data types and expressions: 55 | 56 | * Consider using a variable-size length word for C4String: more 57 | storage-efficient for short strings, which is the common case (or 58 | special-case this just for network format?) 59 | * Replace string location specifier type with an IPv4 endpoint (scalar 60 | value containing IPv4 address + port) 61 | * Consider removing refcount from Tuple OR use the resulting padding 62 | on LP64 machines for something useful (e.g. cache tuple_hash()) 63 | * Consider using a packed tuple representation; reorder Tuple fields 64 | to reduce padding requirements 65 | * Allow sum, avg to work on a broader range of data types 66 | * The sum of int4s might be an int8 67 | * Check for integer overflow in addition, multiply, etc. 68 | 69 | Tables and storage: 70 | 71 | * Add internal "table IDs", and use them instead of table names 72 | * Support for event tables 73 | * Support for BDB persistent tables 74 | * Can we store in-memory Tuple/Datum format directly to BDB? 75 | * Consider adding a "regexp" table type: given a string input, parses 76 | into tuple format by applying a regular expression 77 | * Rather than regexp, look at PADS and related work 78 | * Also do output to external format 79 | * Optimize the hash table implementation 80 | * Consider using Judy trees/arrays instead of hash tables 81 | * Import red-black tree code 82 | * Support for secondary indexes, index scans on PK 83 | * Push predicates down to SQLite table scan 84 | 85 | Build system: 86 | 87 | * Make use of profile-guided optimization with GCC 88 | * Support GCC 4.5's interprocedural optimization mode 89 | * Only export the official C4 client API from the shared library 90 | * All symbols prefixed with c4_, etc. 91 | * Implement via linker scripts? 92 | 93 | APR: 94 | 95 | * Report queue performance issue 96 | * Add support for "apr-config --configure" 97 | * Modify queue type to allow variably-sized queue elements, to avoid 98 | the need to malloc() small queue messages 99 | 100 | Broader issues: 101 | 102 | * Unit testing framework 103 | * Error handling: exceptions via longjmp? 104 | * Add a "$LOCALHOST" variable that expands to the network address of 105 | the evaluating C4 instance 106 | * Complicated by the fact that a machine can have multiple network 107 | addresses (one per interface + localhost + IPv4 vs. IPv6, etc.) 108 | * Perhaps adopt something like Reactor's "ref" concept instead 109 | * Invent something similar to makeNode() from Postgres: infer node 110 | size from node type tag 111 | * Consider caching per-tuple hash code 112 | * Change the node system to work with strict aliasing per C99 113 | * Add a concept of "programs" or "modules" 114 | * Implement a simple interactive shell 115 | * As a first step, read input program from stdin unless terminal 116 | * Locking / concurrency control 117 | 118 | Minor: 119 | 120 | * If a fact is defined at node X but has a location specifier for node 121 | Y, should we send the tuple to node Y, or simply ignore it? 122 | * Make core dumps more obvious 123 | * Print backtrace on core dump 124 | -------------------------------------------------------------------------------- /cmake/FindApr.cmake: -------------------------------------------------------------------------------- 1 | # Source: http://svn.trolocsis.com/repos/projects/templates/apr/build/FindAPR.cmake 2 | # Locate APR include paths and libraries 3 | 4 | # This module defines 5 | # APR_INCLUDES, where to find apr.h, etc. 6 | # APR_LIBS, linker switches to use with ld to link against APR 7 | # APR_EXTRALIBS, additional libraries to link against 8 | # APR_CFLAGS, the flags to use to compile 9 | # APR_FOUND, set to TRUE if found, FALSE otherwise 10 | # APR_VERSION, the version of APR that was found 11 | 12 | set(APR_FOUND FALSE) 13 | 14 | find_program(APR_CONFIG_EXECUTABLE apr-1-config) 15 | mark_as_advanced(APR_CONFIG_EXECUTABLE) 16 | 17 | macro(_apr_invoke _varname _regexp) 18 | execute_process( 19 | COMMAND ${APR_CONFIG_EXECUTABLE} ${ARGN} 20 | OUTPUT_VARIABLE _apr_output 21 | RESULT_VARIABLE _apr_failed 22 | ) 23 | 24 | if(_apr_failed) 25 | message(FATAL_ERROR "${APR_CONFIG_EXECUTABLE} ${ARGN} failed") 26 | else() 27 | string(REGEX REPLACE "[\r\n]" "" _apr_output "${_apr_output}") 28 | string(REGEX REPLACE " +$" "" _apr_output "${_apr_output}") 29 | 30 | if(NOT ${_regexp} STREQUAL "") 31 | string(REGEX REPLACE "${_regexp}" " " _apr_output "${_apr_output}") 32 | endif() 33 | 34 | # XXX: We don't want to invoke separate_arguments() for APR_CFLAGS; 35 | # just leave as-is 36 | if(NOT ${_varname} STREQUAL "APR_CFLAGS") 37 | separate_arguments(_apr_output) 38 | endif() 39 | 40 | set(${_varname} "${_apr_output}") 41 | endif() 42 | endmacro(_apr_invoke) 43 | 44 | _apr_invoke(APR_CFLAGS "" --cppflags --cflags) 45 | _apr_invoke(APR_INCLUDES "(^| )-I" --includes) 46 | _apr_invoke(APR_LIBS "" --link-ld) 47 | _apr_invoke(APR_EXTRALIBS "(^| )-l" --libs) 48 | _apr_invoke(APR_VERSION "" --version) 49 | 50 | INCLUDE(FindPackageHandleStandardArgs) 51 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(APR DEFAULT_MSG APR_INCLUDES APR_LIBS APR_VERSION) 52 | -------------------------------------------------------------------------------- /cmake/FindApu.cmake: -------------------------------------------------------------------------------- 1 | # Locate apr-util include paths and libraries. Based on findapr.cmake; 2 | # simple modifications to apply to apr-util instead. 3 | 4 | # This module defines 5 | # APU_INCLUDES, where to find apu.h, etc. 6 | # APU_LIBS, linker switches to use with ld to link against apr-util 7 | # APU_EXTRALIBS, additional libraries to link against 8 | # APU_LDFLAGS, additional linker flags that must be used 9 | # APU_FOUND, set to TRUE if found, FALSE otherwise 10 | # APU_VERSION, set to the version of apr-util found 11 | 12 | set(APU_FOUND FALSE) 13 | 14 | find_program(APU_CONFIG_EXECUTABLE apu-1-config) 15 | mark_as_advanced(APU_CONFIG_EXECUTABLE) 16 | 17 | macro(_apu_invoke _varname _regexp) 18 | execute_process( 19 | COMMAND ${APU_CONFIG_EXECUTABLE} ${ARGN} 20 | OUTPUT_VARIABLE _apu_output 21 | RESULT_VARIABLE _apu_failed 22 | ) 23 | 24 | if(_apu_failed) 25 | message(FATAL_ERROR "${APU_CONFIG_EXECUTABLE} ${ARGN} failed") 26 | else() 27 | string(REGEX REPLACE "[\r\n]" "" _apu_output "${_apu_output}") 28 | string(REGEX REPLACE " +$" "" _apu_output "${_apu_output}") 29 | 30 | if(NOT ${_regexp} STREQUAL "") 31 | string(REGEX REPLACE "${_regexp}" " " _apu_output "${_apu_output}") 32 | endif() 33 | 34 | # XXX: We don't want to invoke separate_arguments() for APU_LDFLAGS; 35 | # just leave as-is 36 | if(NOT ${_varname} STREQUAL "APU_LDFLAGS") 37 | separate_arguments(_apu_output) 38 | endif() 39 | 40 | set(${_varname} "${_apu_output}") 41 | endif() 42 | endmacro(_apu_invoke) 43 | 44 | _apu_invoke(APU_INCLUDES "(^| )-I" --includes) 45 | _apu_invoke(APU_EXTRALIBS "(^| )-l" --libs) 46 | _apu_invoke(APU_LIBS "" --link-ld) 47 | _apu_invoke(APU_LDFLAGS "" --ldflags) 48 | _apu_invoke(APU_VERSION "" --version) 49 | 50 | INCLUDE(FindPackageHandleStandardArgs) 51 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(APU DEFAULT_MSG APU_INCLUDES APU_LIBS APU_VERSION) 52 | -------------------------------------------------------------------------------- /cmake/FindBerkeleyDB.cmake: -------------------------------------------------------------------------------- 1 | # Find the Berkeley DB include files and libraries. Sets the following 2 | # variables: 3 | # BERKELEY_DB_INCLUDE_DIR 4 | # BERKELEY_DB_FOUND 5 | # BERKELEY_DB_LIBRARIES 6 | 7 | find_path(BERKELEY_DB_INCLUDE_DIR db.h 8 | /opt/local/include/db47 9 | /usr/local/include/db4 10 | /usr/local/include 11 | /usr/include/db4 12 | /usr/include 13 | ) 14 | 15 | find_library(BERKELEY_DB_LIBRARY NAMES db) 16 | 17 | if (BERKELEY_DB_LIBRARY AND BERKELEY_DB_INCLUDE_DIR) 18 | set(BERKELEY_DB_LIBRARIES ${BDB_LIBRARY}) 19 | set(BERKELEY_DB_FOUND "YES") 20 | else () 21 | set(BERKELEY_DB_FOUND "NO") 22 | endif () 23 | 24 | 25 | if (BERKELEY_DB_FOUND) 26 | if (NOT BERKELEY_DB_FIND_QUIETLY) 27 | message(STATUS "Found BerkeleyDB: ${BDB_LIBRARIES}") 28 | else () 29 | message(STATUS "NOT FOUND BDB") 30 | endif () 31 | else () 32 | if (BERKELEY_DB_FIND_REQUIRED) 33 | message(FATAL_ERROR "Could not find BerkeleyDB library") 34 | endif () 35 | endif () 36 | -------------------------------------------------------------------------------- /cmake/FindBison.cmake: -------------------------------------------------------------------------------- 1 | # - Find bison executable and provides macros to generate custom build rules 2 | # The module defined the following variables: 3 | # BISON_EXECUTABLE - path to the bison program 4 | # BISON_VERSION - version of bison 5 | # BISON_FOUND - true if the program was found 6 | # If bison is found, the module defines the macros: 7 | # BISON_TARGET( [VERBOSE ] 8 | # [COMPILE_FLAGS ]) 9 | # which will create a custom rule to generate a parser. is 10 | # the path to a yacc file. is the name of the source file 11 | # generated by bison. A header file is also be generated, and contains 12 | # the token list. If COMPILE_FLAGS option is specified, the next 13 | # parameter is added in the bison command line. if VERBOSE option is 14 | # specified, is created and contains verbose descriptions of the 15 | # grammar and parser. The macro defines a set of variables: 16 | # BISON_${Name}_DEFINED - true is the macro ran successfully 17 | # BISON_${Name}_INPUT - The input source file, an alias for 18 | # BISON_${Name}_OUTPUT_SOURCE - The source file generated by bison 19 | # BISON_${Name}_OUTPUT_HEADER - The header file generated by bison 20 | # BISON_${Name}_OUTPUTS - The sources files generated by bison 21 | # BISON_${Name}_COMPILE_FLAGS - Options used in the bison command line 22 | # 23 | # Example: 24 | # FIND_PACKAGE(BISON) 25 | # BISON_TARGET(MyParser parser.y ${PROJECT_BINARY_DIR}/parser.cpp) 26 | # ADD_EXECUTABLE(Foo main.cpp ${BISON_MyParser_OUTPUTS}) 27 | # 28 | 29 | # Copyright (c) 2006, Tristan Carel 30 | # All rights reserved. 31 | # Redistribution and use in source and binary forms, with or without 32 | # modification, are permitted provided that the following conditions are met: 33 | # 34 | # * Redistributions of source code must retain the above copyright 35 | # notice, this list of conditions and the following disclaimer. 36 | # * Redistributions in binary form must reproduce the above copyright 37 | # notice, this list of conditions and the following disclaimer in the 38 | # documentation and/or other materials provided with the distribution. 39 | # * Neither the name of the University of California, Berkeley nor the 40 | # names of its contributors may be used to endorse or promote products 41 | # derived from this software without specific prior written permission. 42 | # 43 | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 44 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 45 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 46 | # DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 47 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 48 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 49 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 50 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 51 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 52 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | 54 | # $Id: FindBISON.cmake 1218 2007-03-22 22:10:45Z jmh $ 55 | 56 | SET(BISON_FOUND FALSE) 57 | 58 | FIND_PROGRAM(BISON_EXECUTABLE bison DOC "path to the bison executable") 59 | MARK_AS_ADVANCED(BISON_EXECUTABLE) 60 | 61 | IF(BISON_EXECUTABLE) 62 | SET(BISON_FOUND TRUE) 63 | 64 | EXECUTE_PROCESS(COMMAND ${BISON_EXECUTABLE} --version 65 | OUTPUT_VARIABLE BISON_version_output 66 | ERROR_VARIABLE BISON_version_error 67 | RESULT_VARIABLE BISON_version_result 68 | OUTPUT_STRIP_TRAILING_WHITESPACE) 69 | IF(NOT ${BISON_version_result} EQUAL 0) 70 | MESSAGE(SEND_ERROR "Command \"${BISON_EXECUTABLE} --version\" failed with output:\n${BISON_version_error}") 71 | ELSE(NOT ${BISON_version_result} EQUAL 0) 72 | STRING(REGEX REPLACE "^bison \\(GNU Bison\\) ([^\n]+)\n.*" "\\1" 73 | BISON_VERSION "${BISON_version_output}") 74 | ENDIF(NOT ${BISON_version_result} EQUAL 0) 75 | 76 | # internal macro 77 | MACRO(BISON_TARGET_option_verbose Name BisonOutput filename) 78 | LIST(APPEND BISON_TARGET_cmdopt "--verbose") 79 | GET_FILENAME_COMPONENT(BISON_TARGET_output_path "${BisonOutput}" PATH) 80 | GET_FILENAME_COMPONENT(BISON_TARGET_output_name "${BisonOutput}" NAME_WE) 81 | ADD_CUSTOM_COMMAND(OUTPUT ${filename} 82 | COMMAND ${CMAKE_COMMAND} -E copy 83 | "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output" 84 | "${filename}" 85 | DEPENDS 86 | "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output" 87 | COMMENT "[BISON][${Name}] Copying bison verbose table to ${filename}" 88 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) 89 | SET(BISON_${Name}_VERBOSE_FILE ${filename}) 90 | LIST(APPEND BISON_TARGET_extraoutputs 91 | "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output") 92 | ENDMACRO(BISON_TARGET_option_verbose) 93 | 94 | # internal macro 95 | MACRO(BISON_TARGET_option_extraopts Options) 96 | SET(BISON_TARGET_extraopts "${Options}") 97 | SEPARATE_ARGUMENTS(BISON_TARGET_extraopts) 98 | LIST(APPEND BISON_TARGET_cmdopt ${BISON_TARGET_extraopts}) 99 | ENDMACRO(BISON_TARGET_option_extraopts) 100 | 101 | MACRO(BISON_TARGET Name BisonInput BisonOutput) 102 | SET(BISON_TARGET_output_header "") 103 | SET(BISON_TARGET_command_opt "") 104 | SET(BISON_TARGET_outputs "${BisonOutput}") 105 | IF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7) 106 | MESSAGE(SEND_ERROR "Usage") 107 | ELSE(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7) 108 | # Parsing parameters 109 | IF(${ARGC} GREATER 5 OR ${ARGC} EQUAL 5) 110 | IF("${ARGV3}" STREQUAL "VERBOSE") 111 | BISON_TARGET_option_verbose(${Name} ${BisonOutput} "${ARGV4}") 112 | ENDIF("${ARGV3}" STREQUAL "VERBOSE") 113 | IF("${ARGV3}" STREQUAL "COMPILE_FLAGS") 114 | BISON_TARGET_option_extraopts("${ARGV4}") 115 | ENDIF("${ARGV3}" STREQUAL "COMPILE_FLAGS") 116 | ENDIF(${ARGC} GREATER 5 OR ${ARGC} EQUAL 5) 117 | IF(${ARGC} EQUAL 7) 118 | IF("${ARGV5}" STREQUAL "VERBOSE") 119 | BISON_TARGET_option_verbose(${Name} ${BisonOutput} "${ARGV6}") 120 | ENDIF("${ARGV5}" STREQUAL "VERBOSE") 121 | IF("${ARGV5}" STREQUAL "COMPILE_FLAGS") 122 | BISON_TARGET_option_extraopts("${ARGV6}") 123 | ENDIF("${ARGV5}" STREQUAL "COMPILE_FLAGS") 124 | ENDIF(${ARGC} EQUAL 7) 125 | 126 | # Header's name generated by bison (see option -d) 127 | LIST(APPEND BISON_TARGET_cmdopt "-d") 128 | STRING(REGEX REPLACE "^(.*)\\.c([^.]*)$" "\\1.h\\2" 129 | BISON_${Name}_OUTPUT_HEADER "${ARGV2}") 130 | LIST(APPEND BISON_TARGET_outputs "${BISON_${Name}_OUTPUT_HEADER}") 131 | 132 | ADD_CUSTOM_COMMAND(OUTPUT ${BISON_TARGET_outputs} 133 | ${BISON_TARGET_extraoutputs} 134 | COMMAND ${BISON_EXECUTABLE} ${BISON_TARGET_cmdopt} -o ${ARGV2} ${ARGV1} 135 | DEPENDS ${ARGV1} 136 | COMMENT "[BISON][${Name}] Building parser with bison ${BISON_VERSION}" 137 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) 138 | 139 | # define target variables 140 | SET(BISON_${Name}_DEFINED TRUE) 141 | SET(BISON_${Name}_INPUT ${ARGV1}) 142 | SET(BISON_${Name}_OUTPUTS ${BISON_TARGET_outputs}) 143 | SET(BISON_${Name}_COMPILE_FLAGS ${BISON_TARGET_cmdopt}) 144 | SET(BISON_${Name}_OUTPUT_SOURCE "${BisonOutput}") 145 | 146 | ENDIF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7) 147 | ENDMACRO(BISON_TARGET) 148 | 149 | ENDIF(BISON_EXECUTABLE) 150 | 151 | 152 | IF(NOT BISON_FOUND) 153 | IF(NOT BISON_FIND_QUIETLY) 154 | MESSAGE(STATUS "BISON was not found.") 155 | ELSE(NOT BISON_FIND_QUIETLY) 156 | IF(BISON_FIND_REQUIRED) 157 | MESSAGE(FATAL_ERROR "BISON was not found.") 158 | ENDIF(BISON_FIND_REQUIRED) 159 | ENDIF(NOT BISON_FIND_QUIETLY) 160 | ENDIF(NOT BISON_FOUND) 161 | 162 | -------------------------------------------------------------------------------- /cmake/FindFlex.cmake: -------------------------------------------------------------------------------- 1 | # - Find flex executable and provides a macro to generate custom build rules 2 | # The module defines the following variables: 3 | # FLEX_FOUND - true is flex executable is found 4 | # FLEX_VERSION - the version of flex 5 | # If flex is found on the system, the module provides the macro: 6 | # FLEX_TARGET(Name FlexInput FlexOutput [COMPILE_FLAGS ]) 7 | # which creates a custom command to generate the file from 8 | # the file. If COMPILE_FLAGS option is specified, the next 9 | # parameter is added to the flex command line. Name is an alias used to 10 | # get details of this custom command. Indeed the macro defines the 11 | # following variables: 12 | # FLEX_${Name}_DEFINED - true is the macro ran successfully 13 | # FLEX_${Name}_OUTPUTS - the source file generated by the custom rule, an 14 | # alias for FlexOutput 15 | # FLEX_${Name}_INPUT - the flex source file, an alias for ${FlexInput} 16 | # 17 | # Flex scanners oftenly use tokens defined by Bison: the code generated 18 | # by Flex depends of the header generated by Bison. This module also 19 | # defines a macro: 20 | # ADD_FLEX_BISON_DEPENDENCY(FlexTarget BisonTarget) 21 | # which adds the required dependency between a scanner and a parser 22 | # where and are the first parameters of 23 | # respectively FLEX_TARGET and BISON_TARGET macros. 24 | # 25 | # Example: 26 | # FIND_PACKAGE(BISON) 27 | # FIND_PACKAGE(FLEX) 28 | # BISON_TARGET(MyParser parser.y ${PROJECT_BINARY_DIR}/parser.cpp 29 | # FLEX_TARGET(MyScanner lexer.l ${PROJECT_BINARY_DIR}/lexer.cpp) 30 | # ADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser) 31 | # 32 | 33 | # Copyright (c) 2006, Tristan Carel 34 | # All rights reserved. 35 | # Redistribution and use in source and binary forms, with or without 36 | # modification, are permitted provided that the following conditions are met: 37 | # 38 | # * Redistributions of source code must retain the above copyright 39 | # notice, this list of conditions and the following disclaimer. 40 | # * Redistributions in binary form must reproduce the above copyright 41 | # notice, this list of conditions and the following disclaimer in the 42 | # documentation and/or other materials provided with the distribution. 43 | # * Neither the name of the University of California, Berkeley nor the 44 | # names of its contributors may be used to endorse or promote products 45 | # derived from this software without specific prior written permission. 46 | # 47 | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 48 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 49 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 50 | # DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 51 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 52 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 53 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 54 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 55 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 56 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 | 58 | # $Id:: FindFLEX.cmake 3 2006-11-03 02:42:02Z ken $ 59 | 60 | SET(FLEX_FOUND FALSE) 61 | 62 | FIND_PROGRAM(FLEX_EXECUTABLE flex DOC "path to the flex executable") 63 | MARK_AS_ADVANCED(FLEX_EXECUTABLE) 64 | 65 | FIND_LIBRARY(FL_LIBRARY NAMES fl 66 | PATHS /usr/lib DOC "path to the fl library") 67 | SET(FLEX_LIBRARIES ${FL_LIBRARY}) 68 | 69 | IF(FLEX_EXECUTABLE) 70 | SET(FLEX_FOUND TRUE) 71 | 72 | EXECUTE_PROCESS(COMMAND ${FLEX_EXECUTABLE} --version 73 | OUTPUT_VARIABLE FLEX_version_output 74 | ERROR_VARIABLE FLEX_version_error 75 | RESULT_VARIABLE FLEX_version_result 76 | OUTPUT_STRIP_TRAILING_WHITESPACE) 77 | IF(NOT ${FLEX_version_result} EQUAL 0) 78 | MESSAGE(SEND_ERROR "Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_error}") 79 | ELSE(NOT ${FLEX_version_result} EQUAL 0) 80 | STRING(REGEX REPLACE "^flex (.*)$" "\\1" 81 | FLEX_VERSION "${FLEX_version_output}") 82 | ENDIF(NOT ${FLEX_version_result} EQUAL 0) 83 | 84 | MACRO(FLEX_TARGET Name Input Output) 85 | SET(FLEX_TARGET_usage "FLEX_TARGET( [COMPILE_FLAGS ]") 86 | IF(${ARGC} GREATER 3) 87 | IF(${ARGC} EQUAL 5) 88 | IF("${ARGV3}" STREQUAL "COMPILE_FLAGS") 89 | SET(FLEX_EXECUTABLE_opts "${ARGV4}") 90 | SEPARATE_ARGUMENTS(FLEX_EXECUTABLE_opts) 91 | ELSE("${ARGV3}" STREQUAL "COMPILE_FLAGS") 92 | MESSAGE(SEND_ERROR ${FLEX_TARGET_usage}) 93 | ENDIF("${ARGV3}" STREQUAL "COMPILE_FLAGS") 94 | ELSE(${ARGC} EQUAL 5) 95 | MESSAGE(SEND_ERROR ${FLEX_TARGET_usage}) 96 | ENDIF(${ARGC} EQUAL 5) 97 | ENDIF(${ARGC} GREATER 3) 98 | ADD_CUSTOM_COMMAND(OUTPUT ${Output} 99 | COMMAND ${FLEX_EXECUTABLE} ${FLEX_EXECUTABLE_opts} -o${Output} ${Input} 100 | DEPENDS ${Input} 101 | COMMENT "[FLEX][${Name}] Building scanner with flex ${FLEX_VERSION}" 102 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 103 | 104 | SET(FLEX_${Name}_DEFINED TRUE) 105 | SET(FLEX_${Name}_OUTPUTS ${Output}) 106 | SET(FLEX_${Name}_INPUT ${Input}) 107 | SET(FLEX_${Name}_COMPILE_FLAGS ${FLEX_EXECUTABLE_opts}) 108 | ENDMACRO(FLEX_TARGET) 109 | 110 | MACRO(ADD_FLEX_BISON_DEPENDENCY FlexTarget BisonTarget) 111 | IF(NOT FLEX_${FlexTarget}_OUTPUTS) 112 | MESSAGE(SEND_ERROR "Flex target `${FlexTarget}' does not exist.") 113 | ENDIF(NOT FLEX_${FlexTarget}_OUTPUTS) 114 | IF(NOT BISON_${BisonTarget}_OUTPUT_HEADER) 115 | MESSAGE(SEND_ERROR "Bison target `${BisonTarget}' does not exist.") 116 | ENDIF(NOT BISON_${BisonTarget}_OUTPUT_HEADER) 117 | 118 | SET_SOURCE_FILES_PROPERTIES(${FLEX_${FlexTarget}_OUTPUTS} 119 | PROPERTIES OBJECT_DEPENDS ${BISON_${BisonTarget}_OUTPUT_HEADER}) 120 | ENDMACRO(ADD_FLEX_BISON_DEPENDENCY) 121 | 122 | ENDIF(FLEX_EXECUTABLE) 123 | 124 | IF(NOT FLEX_FOUND) 125 | IF(NOT FLEX_FIND_QUIETLY) 126 | MESSAGE(STATUS "FLEX was not found.") 127 | ELSE(NOT FLEX_FIND_QUIETLY) 128 | IF(FLEX_FIND_REQUIRED) 129 | MESSAGE(FATAL_ERROR "FLEX was not found.") 130 | ENDIF(FLEX_FIND_REQUIRED) 131 | ENDIF(NOT FLEX_FIND_QUIETLY) 132 | ENDIF(NOT FLEX_FOUND) 133 | 134 | # FindFLEX.cmake ends here 135 | -------------------------------------------------------------------------------- /cmake/FindSQLite3.cmake: -------------------------------------------------------------------------------- 1 | # Find the SQLite3 include files and libraries. Sets the following 2 | # variables: 3 | # SQLITE3_INCLUDE_DIR 4 | # SQLITE3_FOUND 5 | # SQLITE3_LIBRARIES 6 | 7 | find_path(SQLITE3_INCLUDE_DIR sqlite3.h 8 | /opt/local/include 9 | /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | find_library(SQLITE3_LIBRARY NAMES sqlite3) 14 | 15 | INCLUDE(FindPackageHandleStandardArgs) 16 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLite3 DEFAULT_MSG SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR) 17 | 18 | if(SQLITE3_FOUND) 19 | set(SQLITE3_LIBRARIES ${SQLITE3_LIBRARY}) 20 | endif(SQLITE3_FOUND) 21 | -------------------------------------------------------------------------------- /example/basic_net.olg: -------------------------------------------------------------------------------- 1 | define(foo, {@string, string}); 2 | define(bar, {@string, string}); 3 | define(baz, {@string, string}); 4 | define(baz2, {@string, string, string}); 5 | define(self, {string}); 6 | 7 | self("tcp:W88256X9YJX.local:10001"); 8 | 9 | bar(D, X) :- foo(D, X), baz(D, X), baz2(E, X, K), D == "tcp:W88256X9YJX.local:10001", D == E; 10 | -------------------------------------------------------------------------------- /example/facts.olg: -------------------------------------------------------------------------------- 1 | define(foo, {int, int, int}); 2 | 3 | foo(5, 10, 15); 4 | foo(10, 15, 20); 5 | -------------------------------------------------------------------------------- /example/facts_net.olg: -------------------------------------------------------------------------------- 1 | define(foo, {@string, int}); 2 | 3 | foo("tcp:W88256X9YJX.local:5555", 10); 4 | -------------------------------------------------------------------------------- /example/loop.olg: -------------------------------------------------------------------------------- 1 | define(foo, {int}); 2 | 3 | foo(0); 4 | 5 | foo(A + 1) :- foo(A); 6 | -------------------------------------------------------------------------------- /example/t1.olg: -------------------------------------------------------------------------------- 1 | define(foo, {string, string, int}); 2 | define(bar, {string, string, int}); 3 | define(bax, {string, string, int}); 4 | define(baz, {string, string, int}); 5 | 6 | foo("XXX", "YYY", 5); 7 | foo("XXX", "YYY", 10); 8 | bax("XXX", "ZZZ", 1000); 9 | bax("XXX", "KKK", 1001); 10 | 11 | bar(A, D, C) :- bax(A, D, _), foo(A, B, C), C == 5; 12 | baz(A, B, C) :- bar(A, B, C); 13 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | subdirs(bench c4i libc4) 2 | -------------------------------------------------------------------------------- /src/bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_SOURCE_DIR}/src/libc4/include ${APR_INCLUDES}) 2 | link_directories(${CMAKE_BINARY_DIR}/src/libc4) 3 | 4 | add_executable(bench bench.c) 5 | target_link_libraries(bench c4) 6 | if(APU_LDFLAGS) 7 | set_target_properties(bench PROPERTIES LINK_FLAGS ${APU_LDFLAGS}) 8 | endif(APU_LDFLAGS) 9 | -------------------------------------------------------------------------------- /src/bench/bench.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "c4-api.h" 11 | #include "util/thread_sync.h" 12 | 13 | typedef void (*program_install_f)(C4Client *c); 14 | 15 | static void 16 | usage(void) 17 | { 18 | printf("Usage: bench [ -a | -n | -j ]\n"); 19 | exit(1); 20 | } 21 | 22 | static void 23 | done_table_cb(struct Tuple *tuple, struct TableDef *tbl_def, 24 | bool is_delete, void *data) 25 | { 26 | C4ThreadSync *sync = (C4ThreadSync *) data; 27 | 28 | printf("%s invoked!\n", __func__); 29 | thread_sync_signal(sync); 30 | } 31 | 32 | static void 33 | net_install_program(C4Client *c) 34 | { 35 | c4_install_str(c, "define(ping, {@string, string, int});"); 36 | c4_install_str(c, "define(done, {int});"); 37 | c4_install_str(c, "ping(X, Y, C + 1) :- ping(Y, X, C), C < 100000;"); 38 | c4_install_str(c, "done(C) :- ping(_, _, C), C >= 100000;"); 39 | } 40 | 41 | static void 42 | do_net_bench(apr_pool_t *pool) 43 | { 44 | C4Client *c1; 45 | C4Client *c2; 46 | C4ThreadSync *sync; 47 | char *ping_fact; 48 | 49 | c1 = c4_make(pool, 0); 50 | c2 = c4_make(pool, 0); 51 | 52 | net_install_program(c1); 53 | net_install_program(c2); 54 | 55 | sync = thread_sync_make(pool); 56 | c4_register_callback(c1, "done", done_table_cb, sync); 57 | 58 | ping_fact = apr_psprintf(pool, "ping(\"tcp:localhost:%d\", \"tcp:localhost:%d\", 0);", 59 | c4_get_port(c1), c4_get_port(c2)); 60 | 61 | c4_install_str(c1, ping_fact); 62 | thread_sync_wait(sync); 63 | } 64 | 65 | static void 66 | agg_install_program(C4Client *c) 67 | { 68 | c4_install_str(c, "define(t, {int});"); 69 | c4_install_str(c, "define(b, {int, int});"); 70 | c4_install_str(c, "define(c, {int, int});"); 71 | c4_install_str(c, "b(X, Y + 1) :- b(X, Y), Y < 150000;"); 72 | c4_install_str(c, "b(X, 0) :- t(X);"); 73 | c4_install_str(c, "t(X + 1) :- t(X), X < 30;"); 74 | c4_install_str(c, "c(X, count) :- b(X, Y);"); 75 | } 76 | 77 | static void 78 | perf_install_program(C4Client *c) 79 | { 80 | c4_install_str(c, "define(t, {int});"); 81 | c4_install_str(c, "define(s, {int});"); 82 | c4_install_str(c, "t(A + 1) :- t(A), A < 3000000;"); 83 | c4_install_str(c, "s(A) :- t(A);"); 84 | } 85 | 86 | static void 87 | join_install_program(C4Client *c) 88 | { 89 | c4_install_str(c, "define(t, {int});"); 90 | c4_install_str(c, "define(s, {int});"); 91 | c4_install_str(c, "s(0);"); 92 | c4_install_str(c, "t(A + 1) :- t(A), s(B), A >= B, A < 3000000;"); 93 | } 94 | 95 | static void 96 | do_simple_bench(program_install_f prog, apr_pool_t *pool) 97 | { 98 | C4Client *c; 99 | 100 | c = c4_make(pool, 0); 101 | (*prog)(c); 102 | c4_install_str(c, "t(0);"); 103 | } 104 | 105 | int 106 | main(int argc, const char *argv[]) 107 | { 108 | static const apr_getopt_option_t opt_option[] = 109 | { 110 | {"agg", 'a', false, "agg benchmark"}, 111 | {"join", 'j', false, "join benchmark"}, 112 | {"net", 'n', false, "network benchmark"}, 113 | { NULL, 0, 0, NULL } 114 | }; 115 | apr_pool_t *pool; 116 | apr_getopt_t *opt; 117 | int optch; 118 | const char *optarg; 119 | apr_status_t s; 120 | bool agg_bench = false; 121 | bool join_bench = false; 122 | bool net_bench = false; 123 | apr_time_t start_time; 124 | 125 | c4_initialize(); 126 | 127 | (void) apr_pool_create(&pool, NULL); 128 | (void) apr_getopt_init(&opt, pool, argc, argv); 129 | 130 | while ((s = apr_getopt_long(opt, opt_option, 131 | &optch, &optarg)) == APR_SUCCESS) 132 | { 133 | switch (optch) 134 | { 135 | case 'a': 136 | agg_bench = true; 137 | break; 138 | 139 | case 'j': 140 | join_bench = true; 141 | break; 142 | 143 | case 'n': 144 | net_bench = true; 145 | break; 146 | 147 | default: 148 | printf("Unrecognized option: %c\n", optch); 149 | usage(); 150 | } 151 | } 152 | 153 | if (s != APR_EOF || (join_bench && net_bench)) 154 | usage(); 155 | 156 | start_time = apr_time_now(); 157 | 158 | if (agg_bench) 159 | do_simple_bench(agg_install_program, pool); 160 | else if (join_bench) 161 | do_simple_bench(join_install_program, pool); 162 | else if (net_bench) 163 | do_net_bench(pool); 164 | else 165 | do_simple_bench(perf_install_program, pool); 166 | 167 | printf("Benchmark duration: %" APR_TIME_T_FMT " usec\n", 168 | (apr_time_now() - start_time)); 169 | 170 | start_time = apr_time_now(); 171 | apr_pool_destroy(pool); 172 | printf("Shutdown duration: %" APR_TIME_T_FMT " usec\n", 173 | (apr_time_now() - start_time)); 174 | 175 | c4_terminate(); 176 | return 0; 177 | } 178 | -------------------------------------------------------------------------------- /src/c4i/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_SOURCE_DIR}/src/libc4/include ${APR_INCLUDES}) 2 | link_directories(${CMAKE_BINARY_DIR}/src/libc4) 3 | 4 | add_executable(c4i c4i.c) 5 | target_link_libraries(c4i c4) 6 | if(APU_LDFLAGS) 7 | set_target_properties(c4i PROPERTIES LINK_FLAGS ${APU_LDFLAGS}) 8 | endif(APU_LDFLAGS) 9 | -------------------------------------------------------------------------------- /src/c4i/c4i.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "c4-api.h" 11 | 12 | #define MAX_SOURCE_STRINGS 32 13 | 14 | static void usage(void); 15 | static C4Client *setup_c4(apr_pool_t *pool, apr_int16_t port, 16 | const char *srcfile); 17 | 18 | int 19 | main(int argc, const char *argv[]) 20 | { 21 | static const apr_getopt_option_t opt_option[] = 22 | { 23 | { "src-string", 's', true, "install source" }, 24 | { "help", 'h', false, "show help" }, 25 | { "port", 'p', true, "port number" }, 26 | { NULL, 0, 0, NULL } 27 | }; 28 | apr_pool_t *pool; 29 | apr_getopt_t *opt; 30 | int optch; 31 | const char *optarg; 32 | apr_status_t s; 33 | apr_int64_t port = 0; 34 | char **src_strings; 35 | int num_strings; 36 | int i; 37 | C4Client *c; 38 | 39 | c4_initialize(); 40 | 41 | (void) apr_pool_create(&pool, NULL); 42 | (void) apr_getopt_init(&opt, pool, argc, argv); 43 | 44 | num_strings = 0; 45 | src_strings = apr_palloc(pool, sizeof(*src_strings) * MAX_SOURCE_STRINGS); 46 | 47 | while ((s = apr_getopt_long(opt, opt_option, 48 | &optch, &optarg)) == APR_SUCCESS) 49 | { 50 | switch (optch) 51 | { 52 | case 'h': 53 | usage(); 54 | break; 55 | 56 | case 'p': 57 | if (port != 0) /* Only allow a single "-p" option */ 58 | usage(); 59 | port = apr_atoi64(optarg); 60 | if (port < 0 || port > APR_INT16_MAX) 61 | usage(); 62 | break; 63 | 64 | case 's': 65 | if (num_strings + 1 == MAX_SOURCE_STRINGS) 66 | usage(); 67 | src_strings[num_strings] = apr_pstrdup(pool, optarg); 68 | num_strings++; 69 | break; 70 | 71 | default: 72 | printf("Unrecognized option: %c\n", optch); 73 | usage(); 74 | } 75 | } 76 | 77 | if (s != APR_EOF) 78 | usage(); 79 | 80 | /* 81 | * opt->ind has the index of the last element of argv that was processed 82 | * by getopt. We expect there to be exactly one more argv element (input 83 | * source file). 84 | */ 85 | if (opt->ind + 1 != argc) 86 | usage(); 87 | 88 | c = setup_c4(pool, (apr_int16_t) port, argv[opt->ind]); 89 | 90 | for (i = 0; i < num_strings; i++) 91 | c4_install_str(c, src_strings[i]); 92 | 93 | while (true) 94 | sleep(1); 95 | 96 | apr_pool_destroy(pool); 97 | c4_terminate(); 98 | return 0; 99 | } 100 | 101 | static void 102 | usage(void) 103 | { 104 | printf("Usage: c4i [ -h | -p port | -s srctext ] srcfile\n"); 105 | exit(1); 106 | } 107 | 108 | static C4Client * 109 | setup_c4(apr_pool_t *pool, apr_int16_t port, const char *srcfile) 110 | { 111 | C4Client *c; 112 | C4Status s; 113 | 114 | c = c4_make(pool, port); 115 | s = c4_install_file(c, srcfile); 116 | if (s) 117 | printf("Failed to install file \"%s\": %d\n", srcfile, (int) s); 118 | else 119 | printf("Successfully installed file \"%s\"\n", srcfile); 120 | 121 | return c; 122 | } 123 | -------------------------------------------------------------------------------- /src/libc4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Make sure that C4-internal headers precede system / library headers 2 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}) 3 | include_directories(${APR_INCLUDES} ${APU_INCLUDES} ${SQLITE3_INCLUDE_DIR}) 4 | 5 | file(GLOB_RECURSE libc4_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.c) 6 | 7 | flex_target(Lexer ${CMAKE_CURRENT_SOURCE_DIR}/parser/ol_scan.l ${PROJECT_BINARY_DIR}/ol_scan.c COMPILE_FLAGS "--header-file=${PROJECT_BINARY_DIR}/ol_scan.h") 8 | bison_target(Parser ${CMAKE_CURRENT_SOURCE_DIR}/parser/ol_parse.y ${PROJECT_BINARY_DIR}/ol_parse.c COMPILE_FLAGS "-d") 9 | add_flex_bison_dependency(Lexer Parser) 10 | 11 | set(libc4_SRCS ${libc4_SRCS} ${FLEX_Lexer_OUTPUTS} ${BISON_Parser_OUTPUTS}) 12 | 13 | add_library(c4 ${libc4_SRCS}) 14 | target_link_libraries(c4 sqlite3) 15 | target_link_libraries(c4 ${APR_LIBS} ${APR_EXTRALIBS} ${APU_LIBS} ${APU_EXTRALIBS}) 16 | 17 | if(APU_LDFLAGS) 18 | set_target_properties(c4 PROPERTIES LINK_FLAGS ${APU_LDFLAGS}) 19 | endif(APU_LDFLAGS) 20 | -------------------------------------------------------------------------------- /src/libc4/c4-api.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "c4-api.h" 5 | #include "c4-internal.h" 6 | #include "router.h" 7 | #include "runtime.h" 8 | #include "util/thread_sync.h" 9 | 10 | /* 11 | * Client state. This is manipulated by C4 client APIs; they mostly insert 12 | * elements into the thread-safe queue, which is consumed by the C4 13 | * runtime. Note that it is NOT safe for the runtime to allocate resources 14 | * (other than sub-pools) from the client state's pool. 15 | */ 16 | struct C4Client 17 | { 18 | apr_pool_t *pool; 19 | /* Reset after each API command */ 20 | apr_pool_t *tmp_pool; 21 | /* Runtime state */ 22 | C4Runtime *runtime; 23 | apr_thread_t *runtime_thread; 24 | /* State for passing messages => runtime */ 25 | WorkItem *wi; 26 | C4ThreadSync *thread_sync; 27 | }; 28 | 29 | static apr_status_t c4_client_cleanup(void *data); 30 | 31 | void 32 | c4_initialize(void) 33 | { 34 | apr_status_t s = apr_initialize(); 35 | if (s != APR_SUCCESS) 36 | FAIL_APR(s); 37 | } 38 | 39 | void 40 | c4_terminate(void) 41 | { 42 | apr_terminate(); 43 | } 44 | 45 | C4Client * 46 | c4_make(apr_pool_t *pool, int port) 47 | { 48 | apr_pool_t *client_pool; 49 | C4Client *client; 50 | 51 | client_pool = make_subpool(pool); 52 | client = apr_pcalloc(client_pool, sizeof(*client)); 53 | client->pool = client_pool; 54 | client->tmp_pool = make_subpool(client->pool); 55 | client->thread_sync = thread_sync_make(client->pool); 56 | client->runtime = c4_runtime_start(port, client->thread_sync, client->pool, 57 | &client->runtime_thread); 58 | client->wi = apr_palloc(client->pool, sizeof(WorkItem)); 59 | client->wi->sync = client->thread_sync; 60 | 61 | apr_pool_pre_cleanup_register(client->pool, client, c4_client_cleanup); 62 | 63 | return client; 64 | } 65 | 66 | static apr_status_t 67 | c4_client_cleanup(void *data) 68 | { 69 | C4Client *client = (C4Client *) data; 70 | 71 | WorkItem *wi = client->wi; 72 | apr_status_t s; 73 | apr_status_t thread_status; 74 | 75 | wi->kind = WI_SHUTDOWN; 76 | runtime_enqueue_work(client->runtime, wi); 77 | 78 | s = apr_thread_join(&thread_status, client->runtime_thread); 79 | if (s != APR_SUCCESS) 80 | FAIL_APR(s); 81 | if (thread_status != APR_SUCCESS) 82 | FAIL_APR(thread_status); 83 | 84 | return APR_SUCCESS; 85 | } 86 | 87 | C4Status 88 | c4_destroy(C4Client *client) 89 | { 90 | apr_pool_destroy(client->pool); 91 | return C4_OK; 92 | } 93 | 94 | int 95 | c4_get_port(C4Client *client) 96 | { 97 | return client->runtime->port; 98 | } 99 | 100 | /* 101 | * Read the file at the specified filesystem path into memory, parse it, and 102 | * then install the resulting program into the specified C4 runtime. XXX: 103 | * We assume that the file is small enough that it can be slurped into a 104 | * single memory buffer without too much pain. 105 | */ 106 | C4Status 107 | c4_install_file(C4Client *client, const char *path) 108 | { 109 | apr_status_t s; 110 | apr_file_t *file; 111 | apr_finfo_t finfo; 112 | char *buf; 113 | apr_size_t file_size; 114 | C4Status result; 115 | 116 | s = apr_file_open(&file, path, APR_READ | APR_BUFFERED, 117 | APR_OS_DEFAULT, client->tmp_pool); 118 | if (s != APR_SUCCESS) 119 | { 120 | result = C4_ERROR; 121 | goto done; 122 | } 123 | 124 | /* 125 | * Get the file size, and allocate an appropriately-sized buffer to hold 126 | * the file contents. XXX: There is a trivial race condition here. 127 | */ 128 | s = apr_file_info_get(&finfo, APR_FINFO_SIZE, file); 129 | if (s != APR_SUCCESS) 130 | FAIL_APR(s); 131 | 132 | file_size = (apr_size_t) finfo.size; 133 | buf = apr_palloc(client->tmp_pool, file_size + 1); 134 | 135 | s = apr_file_read(file, buf, &file_size); 136 | if (s != APR_SUCCESS) 137 | FAIL_APR(s); 138 | if (file_size != (apr_size_t) finfo.size) 139 | FAIL(); 140 | 141 | buf[file_size] = '\0'; 142 | result = c4_install_str(client, buf); 143 | 144 | done: 145 | apr_pool_clear(client->tmp_pool); 146 | return result; 147 | } 148 | 149 | /* 150 | * Install the program contained in the specified string into the C4 151 | * runtime. 152 | */ 153 | C4Status 154 | c4_install_str(C4Client *client, const char *str) 155 | { 156 | WorkItem *wi = client->wi; 157 | 158 | wi->kind = WI_PROGRAM; 159 | wi->program_src = str; 160 | runtime_enqueue_work(client->runtime, wi); 161 | 162 | return C4_OK; 163 | } 164 | 165 | char * 166 | c4_dump_table(C4Client *client, const char *tbl_name) 167 | { 168 | WorkItem *wi = client->wi; 169 | 170 | wi->kind = WI_DUMP_TABLE; 171 | wi->tbl_name = tbl_name; 172 | wi->buf = sbuf_make(client->pool); 173 | runtime_enqueue_work(client->runtime, wi); 174 | 175 | return wi->buf->data; 176 | } 177 | 178 | C4Status 179 | c4_register_callback(C4Client *client, const char *tbl_name, 180 | C4TupleCallback callback, void *data) 181 | { 182 | WorkItem *wi = client->wi; 183 | 184 | wi->kind = WI_CALLBACK; 185 | wi->cb_tbl_name = tbl_name; 186 | wi->cb_func = callback; 187 | wi->cb_data = data; 188 | runtime_enqueue_work(client->runtime, wi); 189 | 190 | return C4_OK; 191 | } 192 | -------------------------------------------------------------------------------- /src/libc4/include/c4-api-callback.h: -------------------------------------------------------------------------------- 1 | #ifndef C4_API_CALLBACK_H 2 | #define C4_API_CALLBACK_H 3 | 4 | #include 5 | 6 | /* 7 | * Declarations related to callbacks that are part of the C4 client API. These 8 | * are located in a separate header, so that the runtime can get access to the 9 | * callback definitions without including the rest of the client API. 10 | */ 11 | struct TableDef; 12 | struct Tuple; 13 | 14 | typedef void (*C4TupleCallback)(struct Tuple *tuple, 15 | struct TableDef *tbl_def, 16 | bool is_delete, void *data); 17 | 18 | #endif /* C4_API_CALLBACK_H */ 19 | -------------------------------------------------------------------------------- /src/libc4/include/c4-api.h: -------------------------------------------------------------------------------- 1 | #ifndef C4_API_H 2 | #define C4_API_H 3 | 4 | #include "c4-api-callback.h" 5 | 6 | /* 7 | * An opaque type that holds the client-side state associated with a 8 | * single instance of the C4 runtime. C4 API calls use this state to 9 | * communicate with an instance of the C4 runtime, which runs 10 | * asynchronously as a separate thread. 11 | */ 12 | typedef struct C4Client C4Client; 13 | 14 | /* 15 | * The return status of a C4 API call. C4_OK is "success"; any other 16 | * value means that a failure occurred. 17 | */ 18 | typedef enum C4Status 19 | { 20 | C4_OK = 0, 21 | C4_ERROR 22 | } C4Status; 23 | 24 | /* 25 | * Initialize and terminate the C4 library for the current process. Must be the 26 | * first and last API functions called, respectively. 27 | */ 28 | void c4_initialize(void); 29 | void c4_terminate(void); 30 | 31 | /* 32 | * Create/destroy an instance of C4. The lifetime of this instance is tied to 33 | * the given pool; it can be destroyed earlier by manually invoking 34 | * c4_destroy(). 35 | */ 36 | C4Client *c4_make(apr_pool_t *pool, int port); 37 | C4Status c4_destroy(C4Client *c4); 38 | int c4_get_port(C4Client *c4); 39 | 40 | C4Status c4_install_file(C4Client *c4, const char *path); 41 | C4Status c4_install_str(C4Client *c4, const char *str); 42 | 43 | char *c4_dump_table(C4Client *c4, const char *tbl_name); 44 | 45 | /* 46 | * Register a callback that is invoked for each tuple inserted into the 47 | * specified table. The "data" argument is passed into the callback. 48 | * 49 | * In the current implementation, callbacks are called synchronously (blocking 50 | * the runtime until the callback returns), and are invoked immediately after a 51 | * table insertion occurs (i.e. not at fixpoint boundaries). 52 | */ 53 | C4Status c4_register_callback(C4Client *c4, const char *tbl_name, 54 | C4TupleCallback callback, void *data); 55 | 56 | #endif /* C4_API_H */ 57 | -------------------------------------------------------------------------------- /src/libc4/include/c4-internal.h: -------------------------------------------------------------------------------- 1 | #ifndef C4_INTERNAL_H 2 | #define C4_INTERNAL_H 3 | 4 | /* Commonly-used external headers */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct C4Runtime C4Runtime; 13 | 14 | /* Commonly-used internal headers */ 15 | #include "types/datum.h" 16 | #include "util/error.h" 17 | #include "util/logger.h" 18 | #include "util/mem.h" 19 | 20 | /* 21 | * Private runtime state. This is not visible to the client, and can be safely 22 | * modified by the router thread. 23 | */ 24 | struct C4Runtime 25 | { 26 | /* 27 | * The root APR pool for this instance of C4. This is created when the 28 | * instance is created, and destroyed when the instance is shutdown. 29 | */ 30 | apr_pool_t *pool; 31 | 32 | /* 33 | * APR pool for temporary allocations. This is cleared at the end of each 34 | * fixpoint. 35 | */ 36 | apr_pool_t *tmp_pool; 37 | 38 | /* Various C4 subsystems */ 39 | C4Logger *log; 40 | struct C4Catalog *cat; 41 | struct C4Network *net; 42 | struct C4Router *router; 43 | struct SQLiteState *sql; 44 | struct C4Timer *timer; 45 | struct TuplePoolMgr *tpool_mgr; 46 | 47 | int port; 48 | Datum local_addr; 49 | char *base_dir; 50 | }; 51 | 52 | /* Utility macros */ 53 | #define Max(x, y) ((x) > (y) ? (x) : (y)) 54 | #define Min(x, y) ((x) < (y) ? (x) : (y)) 55 | #define Abs(x) ((x) >= 0 ? (x) : -(x)) 56 | 57 | /* Mark that a function argument is intentionally unused */ 58 | #ifndef __unused 59 | #define __unused __attribute__ ((__unused__)) 60 | #endif 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/libc4/include/net/network.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_H 2 | #define NETWORK_H 3 | 4 | #include "types/catalog.h" 5 | #include "types/tuple.h" 6 | 7 | typedef struct C4Network C4Network; 8 | 9 | C4Network *network_make(C4Runtime *c4, int port); 10 | void network_destroy(C4Network *net); 11 | 12 | bool network_poll(C4Network *net, apr_interval_time_t timeout); 13 | void network_wakeup(C4Network *net); 14 | void network_send(C4Network *net, Tuple *tuple, TableDef *tbl_def); 15 | 16 | int network_get_port(C4Network *net); 17 | 18 | #endif /* NETWORK_H */ 19 | -------------------------------------------------------------------------------- /src/libc4/include/nodes/copyfuncs.h: -------------------------------------------------------------------------------- 1 | #ifndef COPYFUNCS_H 2 | #define COPYFUNCS_H 3 | 4 | void *copy_node(void *n, apr_pool_t *pool); 5 | 6 | #endif /* COPYFUNCS_H */ 7 | -------------------------------------------------------------------------------- /src/libc4/include/nodes/makefuncs.h: -------------------------------------------------------------------------------- 1 | #ifndef MAKEFUNCS_H 2 | #define MAKEFUNCS_H 3 | 4 | #include "parser/ast.h" 5 | #include "planner/planner.h" 6 | #include "types/expr.h" 7 | 8 | AstProgram *make_program(List *defines, List *timers, List *facts, 9 | List *rules, apr_pool_t *p); 10 | AstDefine *make_define(const char *name, AstStorageKind storage, 11 | List *schema, apr_pool_t *p); 12 | AstTimer *make_ast_timer(const char *name, apr_int64_t period, 13 | apr_pool_t *p); 14 | AstSchemaElt *make_schema_elt(const char *type_name, bool is_loc_spec, 15 | apr_pool_t *p); 16 | AstRule *make_rule(const char *name, bool is_delete, bool is_network, 17 | bool has_agg, AstTableRef *head, List *joins, 18 | List *quals, apr_pool_t *p); 19 | AstFact *make_fact(AstTableRef *head, apr_pool_t *p); 20 | AstTableRef *make_table_ref(const char *name, List *cols, apr_pool_t *p); 21 | AstJoinClause *make_join_clause(AstTableRef *ref, bool not, apr_pool_t *p); 22 | AstQualifier *make_qualifier(C4Node *expr, apr_pool_t *p); 23 | AstOpExpr *make_ast_op_expr(C4Node *lhs, C4Node *rhs, 24 | AstOperKind op_kind, apr_pool_t *p); 25 | AstVarExpr *make_ast_var_expr(const char *name, DataType type, apr_pool_t *p); 26 | AstConstExpr *make_ast_const_expr(AstConstKind c_kind, const char *value, 27 | apr_pool_t *p); 28 | 29 | AggPlan *make_agg_plan(AstTableRef *head, bool planned, 30 | List *proj_list, apr_pool_t *p); 31 | FilterPlan *make_filter_plan(const char *tbl_name, List *quals, 32 | List *qual_exprs, List *proj_list, apr_pool_t *p); 33 | InsertPlan *make_insert_plan(AstTableRef *head, List *proj_list, apr_pool_t *p); 34 | ProjectPlan *make_project_plan(List *proj_list, apr_pool_t *p); 35 | ScanPlan *make_scan_plan(AstJoinClause *scan_rel, List *quals, 36 | List *qual_exprs, List *proj_list, 37 | apr_pool_t *p); 38 | 39 | ExprOp *make_expr_op(DataType type, AstOperKind op_kind, 40 | ExprNode *lhs, ExprNode *rhs, apr_pool_t *p); 41 | ExprVar *make_expr_var(DataType type, int attno, bool is_outer, 42 | const char *name, apr_pool_t *p); 43 | ExprConst *make_expr_const(DataType type, Datum val, apr_pool_t *p); 44 | AstAggExpr *make_ast_agg_expr(AstAggKind a_kind, C4Node *expr, apr_pool_t *p); 45 | 46 | #endif /* MAKEFUNCS_H */ 47 | -------------------------------------------------------------------------------- /src/libc4/include/nodes/nodes.h: -------------------------------------------------------------------------------- 1 | #ifndef NODES_H 2 | #define NODES_H 3 | 4 | #include "util/strbuf.h" 5 | 6 | typedef enum C4NodeKind 7 | { 8 | /* AST nodes */ 9 | AST_PROGRAM, 10 | AST_DEFINE, 11 | AST_TIMER, 12 | AST_SCHEMA_ELT, 13 | AST_RULE, 14 | AST_FACT, 15 | AST_TABLE_REF, 16 | AST_JOIN_CLAUSE, 17 | AST_QUALIFIER, 18 | AST_OP_EXPR, 19 | AST_VAR_EXPR, 20 | AST_CONST_EXPR, 21 | AST_AGG_EXPR, 22 | 23 | /* Plan nodes */ 24 | PLAN_AGG, 25 | PLAN_FILTER, 26 | PLAN_INSERT, 27 | PLAN_PROJECT, 28 | PLAN_SCAN, 29 | 30 | /* Executor nodes */ 31 | OPER_AGG, 32 | OPER_FILTER, 33 | OPER_INSERT, 34 | OPER_PROJECT, 35 | OPER_SCAN, 36 | 37 | /* Expr nodes */ 38 | EXPR_OP, 39 | EXPR_VAR, 40 | EXPR_CONST 41 | } C4NodeKind; 42 | 43 | typedef struct C4Node 44 | { 45 | C4NodeKind kind; 46 | } C4Node; 47 | 48 | void node_to_str(C4Node *node, StrBuf *sbuf); 49 | char *node_get_kind_str(C4Node *node); 50 | 51 | #endif /* NODES_H */ 52 | -------------------------------------------------------------------------------- /src/libc4/include/operator/agg.h: -------------------------------------------------------------------------------- 1 | #ifndef AGG_H 2 | #define AGG_H 3 | 4 | #include "operator/operator.h" 5 | #include "types/agg_funcs.h" 6 | #include "types/catalog.h" 7 | #include "util/hash.h" 8 | #include "util/rset.h" 9 | 10 | typedef struct AggExprInfo 11 | { 12 | int colno; 13 | AstAggExpr *ast_expr; 14 | AggFuncDesc *desc; 15 | } AggExprInfo; 16 | 17 | typedef struct AggGroupState 18 | { 19 | /* 20 | * We keep a pointer (+ refcount pin) to the first tuple that was inserted 21 | * into the group; this forms the key for the group hash table. Annoyingly, 22 | * we also need to keep a pointer to the key in the group state itself, so 23 | * that we can release the pin when the group is removed. 24 | */ 25 | Tuple *key; 26 | Tuple *output_tup; 27 | int count; 28 | AggStateVal *state_vals; 29 | struct AggGroupState *next; 30 | } AggGroupState; 31 | 32 | typedef struct AggOperator 33 | { 34 | Operator op; 35 | int num_aggs; 36 | AggExprInfo **agg_info; 37 | int num_group_cols; 38 | int *group_colnos; 39 | rset_t *tuple_set; 40 | c4_hash_t *group_tbl; 41 | AggGroupState *free_groups; 42 | TableDef *output_tbl; 43 | } AggOperator; 44 | 45 | AggOperator *agg_op_make(AggPlan *plan, OpChain *chain); 46 | 47 | #endif /* AGG_H */ 48 | -------------------------------------------------------------------------------- /src/libc4/include/operator/filter.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTER_H 2 | #define FILTER_H 3 | 4 | #include "operator/operator.h" 5 | #include "planner/planner.h" 6 | 7 | typedef struct FilterOperator 8 | { 9 | Operator op; 10 | int nquals; 11 | ExprState **qual_ary; 12 | } FilterOperator; 13 | 14 | FilterOperator *filter_op_make(FilterPlan *plan, Operator *next_op, 15 | OpChain *chain); 16 | 17 | #endif /* FILTER_H */ 18 | -------------------------------------------------------------------------------- /src/libc4/include/operator/insert.h: -------------------------------------------------------------------------------- 1 | #ifndef INSERT_H 2 | #define INSERT_H 3 | 4 | #include "operator/operator.h" 5 | #include "planner/planner.h" 6 | #include "types/catalog.h" 7 | 8 | typedef struct InsertOperator 9 | { 10 | Operator op; 11 | TableDef *tbl_def; 12 | } InsertOperator; 13 | 14 | InsertOperator *insert_op_make(InsertPlan *plan, OpChain *chain); 15 | 16 | #endif /* INSERT_H */ 17 | -------------------------------------------------------------------------------- /src/libc4/include/operator/operator.h: -------------------------------------------------------------------------------- 1 | #ifndef OPERATOR_H 2 | #define OPERATOR_H 3 | 4 | #include "parser/ast.h" 5 | #include "planner/planner.h" 6 | #include "types/catalog.h" 7 | #include "types/expr.h" 8 | #include "types/tuple.h" 9 | 10 | typedef struct Operator Operator; 11 | typedef struct OpChain OpChain; 12 | 13 | /* 14 | * An OpChain is a sequence of operators that begins with a "delta 15 | * table". When a new tuple is inserted into a table, the tuple is passed 16 | * down each op chain that begins with that delta table. 17 | */ 18 | struct OpChain 19 | { 20 | apr_pool_t *pool; 21 | C4Runtime *c4; 22 | TableDef *delta_tbl; 23 | AstTableRef *head; 24 | bool anti_chain; 25 | Operator *chain_start; 26 | int length; 27 | 28 | /* 29 | * In the router, a pointer to the next op chain for the same delta 30 | * table 31 | */ 32 | OpChain *next; 33 | }; 34 | 35 | typedef void (*op_invoke_func)(Operator *op, Tuple *t); 36 | 37 | struct Operator 38 | { 39 | C4Node node; 40 | apr_pool_t *pool; 41 | PlanNode *plan; 42 | Operator *next; 43 | ExprEvalContext *exec_cxt; 44 | OpChain *chain; 45 | 46 | /* Projection info */ 47 | int nproj; 48 | ExprState **proj_ary; 49 | Schema *proj_schema; 50 | 51 | op_invoke_func invoke; 52 | }; 53 | 54 | /* 55 | * A list of OpChains that all have the same delta_tbl. This is just used 56 | * for convenience in the router (specifically, we need something for 57 | * TableDef to point at, even if the list happens to be empty). 58 | */ 59 | typedef struct OpChainList 60 | { 61 | OpChain *head; 62 | int length; 63 | } OpChainList; 64 | 65 | /* Generic support routines for operators */ 66 | Operator *operator_make(C4NodeKind kind, apr_size_t sz, PlanNode *plan, 67 | Operator *next_op, OpChain *chain, 68 | op_invoke_func invoke_f); 69 | 70 | Tuple *operator_do_project(Operator *op); 71 | 72 | OpChainList *opchain_list_make(apr_pool_t *pool); 73 | void opchain_list_add(OpChainList *list, OpChain *op_chain); 74 | 75 | #endif /* OPERATOR_H */ 76 | -------------------------------------------------------------------------------- /src/libc4/include/operator/project.h: -------------------------------------------------------------------------------- 1 | #ifndef PROJECT_H 2 | #define PROJECT_H 3 | 4 | #include "operator/operator.h" 5 | #include "planner/planner.h" 6 | 7 | typedef struct ProjectOperator 8 | { 9 | Operator op; 10 | } ProjectOperator; 11 | 12 | ProjectOperator *project_op_make(ProjectPlan *plan, Operator *next_op, 13 | OpChain *chain); 14 | 15 | #endif /* PROJECT_H */ 16 | -------------------------------------------------------------------------------- /src/libc4/include/operator/scan.h: -------------------------------------------------------------------------------- 1 | #ifndef SCAN_H 2 | #define SCAN_H 3 | 4 | #include "operator/operator.h" 5 | #include "operator/scancursor.h" 6 | #include "planner/planner.h" 7 | #include "storage/table.h" 8 | 9 | typedef struct ScanOperator 10 | { 11 | Operator op; 12 | int nquals; 13 | ExprState **qual_ary; 14 | AbstractTable *table; 15 | ScanCursor *cursor; 16 | bool anti_scan; 17 | } ScanOperator; 18 | 19 | ScanOperator *scan_op_make(ScanPlan *plan, Operator *next_op, OpChain *chain); 20 | 21 | #endif /* SCAN_H */ 22 | -------------------------------------------------------------------------------- /src/libc4/include/operator/scancursor.h: -------------------------------------------------------------------------------- 1 | #ifndef SCAN_CURSOR_H 2 | #define SCAN_CURSOR_H 3 | 4 | #include 5 | 6 | #include "util/rset.h" 7 | 8 | typedef struct ScanCursor 9 | { 10 | apr_pool_t *pool; 11 | /* Cursor over MemTable */ 12 | rset_index_t *rset_iter; 13 | /* Cursor over SQLiteTable */ 14 | sqlite3_stmt *sqlite_stmt; 15 | } ScanCursor; 16 | 17 | #endif /* SCAN_CURSOR_H */ 18 | -------------------------------------------------------------------------------- /src/libc4/include/parser/analyze.h: -------------------------------------------------------------------------------- 1 | #ifndef ANALYZE_H 2 | #define ANALYZE_H 3 | 4 | #include 5 | 6 | #include "parser/ast.h" 7 | #include "types/schema.h" 8 | #include "util/list.h" 9 | 10 | void analyze_ast(AstProgram *program, apr_pool_t *pool, C4Runtime *c4); 11 | 12 | /* Utility functions */ 13 | DataType expr_get_type(C4Node *node); 14 | bool is_simple_equality(AstQualifier *qual, AstVarExpr **lhs, AstVarExpr **rhs); 15 | 16 | List *get_eq_list(const char *var_name, bool make_new, apr_hash_t *eq_tbl, 17 | apr_pool_t *p); 18 | void add_var_eq(char *v1, char *v2, apr_hash_t *eq_tbl, apr_pool_t *p); 19 | 20 | #endif /* ANALYZE_H */ 21 | -------------------------------------------------------------------------------- /src/libc4/include/parser/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_H 2 | #define AST_H 3 | 4 | #include "nodes/nodes.h" 5 | #include "types/schema.h" 6 | #include "util/list.h" 7 | 8 | typedef struct AstTableRef AstTableRef; 9 | 10 | typedef struct AstProgram 11 | { 12 | C4Node node; 13 | List *defines; 14 | List *timers; 15 | List *facts; 16 | List *rules; 17 | } AstProgram; 18 | 19 | typedef enum AstStorageKind 20 | { 21 | AST_STORAGE_MEMORY, 22 | AST_STORAGE_SQLITE 23 | } AstStorageKind; 24 | 25 | typedef struct AstDefine 26 | { 27 | C4Node node; 28 | char *name; 29 | AstStorageKind storage; 30 | List *schema; 31 | } AstDefine; 32 | 33 | typedef struct AstTimer 34 | { 35 | C4Node node; 36 | char *name; 37 | apr_int64_t period; /* Timer period in milliseconds */ 38 | } AstTimer; 39 | 40 | typedef struct AstSchemaElt 41 | { 42 | C4Node node; 43 | char *type_name; 44 | bool is_loc_spec; 45 | } AstSchemaElt; 46 | 47 | typedef struct AstFact 48 | { 49 | C4Node node; 50 | AstTableRef *head; 51 | } AstFact; 52 | 53 | typedef struct AstRule 54 | { 55 | C4Node node; 56 | char *name; 57 | AstTableRef *head; 58 | /* The rule body, divided by node kind */ 59 | List *joins; 60 | List *quals; 61 | bool is_delete; 62 | /* Filled-in during parse-analysis */ 63 | bool is_network; 64 | bool has_agg; 65 | } AstRule; 66 | 67 | struct AstTableRef 68 | { 69 | C4Node node; 70 | char *name; /* Name of the referenced relation */ 71 | List *cols; /* List of expressions */ 72 | }; 73 | 74 | typedef struct AstJoinClause 75 | { 76 | C4Node node; 77 | AstTableRef *ref; 78 | bool not; 79 | } AstJoinClause; 80 | 81 | /* XXX: get rid of this? */ 82 | typedef struct AstQualifier 83 | { 84 | C4Node node; 85 | C4Node *expr; 86 | } AstQualifier; 87 | 88 | typedef enum AstOperKind 89 | { 90 | AST_OP_PLUS, 91 | AST_OP_MINUS, 92 | AST_OP_TIMES, 93 | AST_OP_DIVIDE, 94 | AST_OP_MODULUS, 95 | AST_OP_UMINUS, 96 | AST_OP_LT, 97 | AST_OP_LTE, 98 | AST_OP_GT, 99 | AST_OP_GTE, 100 | AST_OP_EQ, 101 | AST_OP_NEQ 102 | } AstOperKind; 103 | 104 | typedef struct AstOpExpr 105 | { 106 | C4Node node; 107 | C4Node *lhs; 108 | C4Node *rhs; 109 | AstOperKind op_kind; 110 | } AstOpExpr; 111 | 112 | typedef struct AstVarExpr 113 | { 114 | C4Node node; 115 | char *name; 116 | /* Filled-in by the analysis phase */ 117 | DataType type; 118 | } AstVarExpr; 119 | 120 | typedef enum AstConstKind 121 | { 122 | AST_CONST_BOOL, 123 | AST_CONST_CHAR, 124 | AST_CONST_DOUBLE, 125 | AST_CONST_INT, 126 | AST_CONST_STRING 127 | } AstConstKind; 128 | 129 | typedef struct AstConstExpr 130 | { 131 | C4Node node; 132 | AstConstKind const_kind; 133 | char *value; 134 | } AstConstExpr; 135 | 136 | typedef enum AstAggKind 137 | { 138 | AST_AGG_AVG, 139 | AST_AGG_COUNT, 140 | AST_AGG_MAX, 141 | AST_AGG_MIN, 142 | AST_AGG_SUM 143 | } AstAggKind; 144 | 145 | typedef struct AstAggExpr 146 | { 147 | C4Node node; 148 | AstAggKind agg_kind; 149 | C4Node *expr; 150 | } AstAggExpr; 151 | 152 | #endif /* AST_H */ 153 | -------------------------------------------------------------------------------- /src/libc4/include/parser/parser-internal.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_INTERNAL_H 2 | #define PARSER_INTERNAL_H 3 | 4 | #include "parser/ast.h" 5 | #include "parser/parser.h" 6 | #include "util/strbuf.h" 7 | 8 | typedef struct C4Parser 9 | { 10 | apr_pool_t *pool; 11 | void *yyscanner; 12 | AstProgram *result; 13 | 14 | /* Contents of the currently-being-lexed string literal */ 15 | StrBuf *lit_buf; 16 | } C4Parser; 17 | 18 | /* Generated by Bison from ol_parse.y */ 19 | int yyparse(C4Parser *context, void *scanner); 20 | 21 | /* ol_scan.l */ 22 | char *setup_scan_buf(const char *str, apr_size_t len, apr_pool_t *pool); 23 | 24 | #endif /* PARSER_INTERNAL_H */ 25 | -------------------------------------------------------------------------------- /src/libc4/include/parser/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include "parser/ast.h" 5 | 6 | AstProgram *parse_str(const char *str, apr_pool_t *pool, C4Runtime *c4); 7 | 8 | #endif /* PARSER_H */ 9 | -------------------------------------------------------------------------------- /src/libc4/include/parser/walker.h: -------------------------------------------------------------------------------- 1 | #ifndef WALKER_H 2 | #define WALKER_H 3 | 4 | #include "parser/ast.h" 5 | 6 | /* 7 | * A callback that is invoked on a node in an expression tree. If the 8 | * callback returns "false", the walk of the tree is halted immediately; 9 | * otherwise it continues in a depth-first fashion. 10 | */ 11 | typedef bool (*expr_callback)(C4Node *n, void *data); 12 | 13 | bool expr_tree_walker(C4Node *n, expr_callback fn, void *data); 14 | 15 | /* 16 | * A callback that is invoked on each AstVarExpr that appears in an expression 17 | * tree. This is just syntax sugar for using expr_tree_walker(). 18 | */ 19 | typedef bool (*var_expr_callback)(AstVarExpr *var, void *data); 20 | 21 | bool expr_tree_var_walker(C4Node *n, var_expr_callback fn, void *data); 22 | 23 | #endif /* WALKER_H */ 24 | -------------------------------------------------------------------------------- /src/libc4/include/planner/installer.h: -------------------------------------------------------------------------------- 1 | #ifndef INSTALLER_H 2 | #define INSTALLER_H 3 | 4 | #include "planner/planner.h" 5 | 6 | void install_plan(ProgramPlan *plan, apr_pool_t *pool, C4Runtime *c4); 7 | 8 | #endif /* INSTALLER_H */ 9 | -------------------------------------------------------------------------------- /src/libc4/include/planner/planner-internal.h: -------------------------------------------------------------------------------- 1 | #ifndef PLANNER_INTERNAL_H 2 | #define PLANNER_INTERNAL_H 3 | 4 | #include 5 | 6 | #include "planner/planner.h" 7 | #include "types/expr.h" 8 | 9 | typedef struct PlannerState 10 | { 11 | C4Runtime *c4; 12 | ProgramPlan *plan; 13 | List *join_set_todo; 14 | List *qual_set_todo; 15 | List *join_set; 16 | List *qual_set; 17 | List *join_set_refs; 18 | 19 | /* The set of variable names projected by the current operator */ 20 | List *current_plist; 21 | /* Map from var name => list of equal variables name, s.t. the equality has 22 | * been applied by a previous element in the current OpChain */ 23 | apr_hash_t *var_eq_tbl; 24 | 25 | /* The pool in which the output plan is allocated */ 26 | apr_pool_t *plan_pool; 27 | /* 28 | * Storage for temporary allocations made during the planning 29 | * process. This is a subpool of the plan pool; it is deleted when 30 | * planning is complete. 31 | */ 32 | apr_pool_t *tmp_pool; 33 | } PlannerState; 34 | 35 | /* plan_expr.c */ 36 | void fix_chain_exprs(OpChainPlan *chain_plan, PlannerState *state); 37 | 38 | #endif /* PLANNER_INTERNAL_H */ 39 | -------------------------------------------------------------------------------- /src/libc4/include/planner/planner.h: -------------------------------------------------------------------------------- 1 | #ifndef PLANNER_H 2 | #define PLANNER_H 3 | 4 | #include "parser/ast.h" 5 | #include "util/list.h" 6 | 7 | typedef struct PlanNode 8 | { 9 | C4Node node; 10 | /* AST representation of quals: list of AstQualifier */ 11 | List *quals; 12 | /* Runtime representation of quals */ 13 | List *qual_exprs; 14 | /* Runtime representation of proj list */ 15 | List *proj_list; 16 | } PlanNode; 17 | 18 | typedef struct AggPlan 19 | { 20 | PlanNode plan; 21 | AstTableRef *head; 22 | bool planned; 23 | } AggPlan; 24 | 25 | typedef struct FilterPlan 26 | { 27 | PlanNode plan; 28 | char *tbl_name; 29 | } FilterPlan; 30 | 31 | typedef struct InsertPlan 32 | { 33 | PlanNode plan; 34 | AstTableRef *head; 35 | } InsertPlan; 36 | 37 | typedef struct ProjectPlan 38 | { 39 | PlanNode plan; 40 | } ProjectPlan; 41 | 42 | typedef struct ScanPlan 43 | { 44 | PlanNode plan; 45 | AstJoinClause *scan_rel; 46 | } ScanPlan; 47 | 48 | typedef struct ProgramPlan 49 | { 50 | apr_pool_t *pool; 51 | List *defines; 52 | List *timers; 53 | List *facts; 54 | List *rules; 55 | } ProgramPlan; 56 | 57 | typedef struct OpChainPlan 58 | { 59 | AstJoinClause *delta_tbl; 60 | AstTableRef *head; 61 | /* A PlanNode for each op in the chain */ 62 | List *chain; 63 | } OpChainPlan; 64 | 65 | typedef struct RulePlan 66 | { 67 | List *chains; 68 | /* 69 | * While most plan nodes are re-created for each op chain, we share a single 70 | * AggPlan among all the chains for a given rule. This is because we want to 71 | * do grouping/aggregation over the entire rule output. 72 | */ 73 | AggPlan *agg_plan; 74 | /* 75 | * When a rule is installed, we need to trigger deltas for facts that 76 | * existed prior to the rule's definition. We do this by picking an 77 | * arbitrary "bootstrap" table from the rule's body terms, and re-inserting 78 | * the entire content of that table after installing the plan. 79 | */ 80 | AstTableRef *bootstrap_tbl; 81 | } RulePlan; 82 | 83 | ProgramPlan *plan_program(AstProgram *ast, apr_pool_t *pool, C4Runtime *c4); 84 | void print_plan_info(PlanNode *plan, apr_pool_t *p); 85 | 86 | #endif /* PLANNER_H */ 87 | -------------------------------------------------------------------------------- /src/libc4/include/router.h: -------------------------------------------------------------------------------- 1 | #ifndef ROUTER_H 2 | #define ROUTER_H 3 | 4 | #include "c4-api-callback.h" 5 | #include "operator/operator.h" 6 | #include "planner/planner.h" 7 | #include "types/tuple.h" 8 | 9 | typedef struct C4Router C4Router; 10 | 11 | C4Router *router_make(C4Runtime *c4); 12 | void router_main_loop(C4Router *router); 13 | 14 | /* Public APIs */ 15 | char *runtime_enqueue_dump_table(C4Runtime *c4, const char *tbl_name, 16 | apr_pool_t *pool); 17 | 18 | /* Internal APIs: XXX: clearer naming */ 19 | void router_insert_tuple(C4Router *router, Tuple *tuple, 20 | TableDef *tbl_def, bool check_remote); 21 | void router_delete_tuple(C4Router *router, Tuple *tuple, TableDef *tbl_def); 22 | void router_enqueue_internal(C4Router *router, Tuple *tuple, TableDef *tbl_def); 23 | 24 | OpChainList *router_get_opchain_list(C4Router *router, const char *tbl_name); 25 | void router_add_op_chain(C4Router *router, OpChain *op_chain); 26 | bool router_is_deleting(C4Router *router); 27 | 28 | #endif /* ROUTER_H */ 29 | -------------------------------------------------------------------------------- /src/libc4/include/runtime.h: -------------------------------------------------------------------------------- 1 | #ifndef C4_RUNTIME_H 2 | #define C4_RUNTIME_H 3 | 4 | #include 5 | 6 | #include "c4-api-callback.h" 7 | #include "types/catalog.h" 8 | #include "types/tuple.h" 9 | #include "util/strbuf.h" 10 | #include "util/thread_sync.h" 11 | 12 | C4Runtime *c4_runtime_start(int port, C4ThreadSync *thread_sync, 13 | apr_pool_t *pool, apr_thread_t **thread); 14 | 15 | typedef enum WorkItemKind 16 | { 17 | WI_PROGRAM, 18 | WI_DUMP_TABLE, 19 | WI_CALLBACK, 20 | WI_SHUTDOWN 21 | } WorkItemKind; 22 | 23 | typedef struct WorkItem 24 | { 25 | WorkItemKind kind; 26 | C4ThreadSync *sync; 27 | 28 | /* WI_PROGRAM: */ 29 | const char *program_src; 30 | 31 | /* WI_DUMP_TABLE: */ 32 | const char *tbl_name; 33 | StrBuf *buf; 34 | 35 | /* WI_CALLBACK */ 36 | const char *cb_tbl_name; 37 | C4TupleCallback cb_func; 38 | void *cb_data; 39 | } WorkItem; 40 | 41 | void runtime_enqueue_work(C4Runtime *c4, WorkItem *wi); 42 | 43 | #endif /* C4_RUNTIME_H */ 44 | -------------------------------------------------------------------------------- /src/libc4/include/storage/mem_table.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_TABLE_H 2 | #define MEM_TABLE_H 3 | 4 | #include "storage/table.h" 5 | #include "util/rset.h" 6 | 7 | typedef struct MemTable 8 | { 9 | AbstractTable table; 10 | rset_t *tuples; 11 | } MemTable; 12 | 13 | MemTable *mem_table_make(TableDef *def, C4Runtime *c4, apr_pool_t *pool); 14 | 15 | #endif /* MEM_TABLE_H */ 16 | -------------------------------------------------------------------------------- /src/libc4/include/storage/sqlite.h: -------------------------------------------------------------------------------- 1 | #ifndef C4_SQLITE_H 2 | #define C4_SQLITE_H 3 | 4 | #include 5 | 6 | typedef struct SQLiteState 7 | { 8 | C4Runtime *c4; 9 | sqlite3 *db; 10 | bool xact_in_progress; 11 | } SQLiteState; 12 | 13 | SQLiteState *sqlite_init(C4Runtime *c4); 14 | void sqlite_begin_xact(SQLiteState *sql); 15 | void sqlite_commit_xact(SQLiteState *sql); 16 | void sqlite_exec_sql(SQLiteState *sql, const char *stmt); 17 | sqlite3_stmt *sqlite_pstmt_make(SQLiteState *sql, const char *stmt, 18 | int stmt_len, apr_pool_t *pool); 19 | 20 | #endif /* C4_SQLITE_H */ 21 | -------------------------------------------------------------------------------- /src/libc4/include/storage/sqlite_table.h: -------------------------------------------------------------------------------- 1 | #ifndef SQLITE_TABLE_H 2 | #define SQLITE_TABLE_H 3 | 4 | #include 5 | 6 | #include "storage/table.h" 7 | 8 | typedef struct SQLiteTable 9 | { 10 | AbstractTable table; 11 | sqlite3_stmt *insert_stmt; 12 | } SQLiteTable; 13 | 14 | SQLiteTable *sqlite_table_make(TableDef *def, C4Runtime *c4, 15 | apr_pool_t *pool); 16 | 17 | #endif /* SQLITE_TABLE_H */ 18 | -------------------------------------------------------------------------------- /src/libc4/include/storage/table.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLE_H 2 | #define TABLE_H 3 | 4 | #include "types/catalog.h" 5 | #include "types/tuple.h" 6 | 7 | typedef struct AbstractTable AbstractTable; 8 | 9 | struct ScanCursor; 10 | 11 | /* Table API */ 12 | 13 | /* 14 | * Insert the tuple into this table. Returns "true" if the tuple was added; 15 | * returns false if the insert was a no-op because the tuple is already 16 | * contained by this table. 17 | */ 18 | typedef bool (*table_insert_f)(AbstractTable *tbl, Tuple *t); 19 | 20 | /* 21 | * Delete the tuple from this table. Returns "true" if the tuple was deleted; 22 | * returns false if the tuple was not found in the table. 23 | */ 24 | typedef bool (*table_delete_f)(AbstractTable *tbl, Tuple *t); 25 | 26 | typedef void (*table_cleanup_f)(AbstractTable *tbl); 27 | typedef struct ScanCursor *(*table_scan_make_f)(AbstractTable *tbl, apr_pool_t *pool); 28 | typedef void (*table_scan_reset_f)(AbstractTable *tbl, struct ScanCursor *scan); 29 | typedef Tuple *(*table_scan_next_f)(AbstractTable *tbl, struct ScanCursor *scan); 30 | 31 | struct AbstractTable 32 | { 33 | apr_pool_t *pool; 34 | C4Runtime *c4; 35 | TableDef *def; 36 | 37 | /* Table API */ 38 | table_insert_f insert; 39 | table_delete_f delete; 40 | table_cleanup_f cleanup; 41 | table_scan_make_f scan_make; 42 | table_scan_reset_f scan_reset; 43 | table_scan_next_f scan_next; 44 | }; 45 | 46 | AbstractTable *table_make(TableDef *def, C4Runtime *c4, apr_pool_t *pool); 47 | AbstractTable *table_make_super(apr_size_t sz, TableDef *def, C4Runtime *c4, 48 | table_insert_f insert_f, 49 | table_delete_f delete_f, 50 | table_cleanup_f cleanup_f, 51 | table_scan_make_f scan_make_f, 52 | table_scan_reset_f scan_reset_f, 53 | table_scan_next_f scan_next_f, 54 | apr_pool_t *pool); 55 | 56 | #endif /* TABLE_H */ 57 | -------------------------------------------------------------------------------- /src/libc4/include/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | typedef struct C4Timer C4Timer; 5 | 6 | C4Timer *timer_make(C4Runtime *c4); 7 | void timer_add_alarm(C4Timer *timer, const char *name, 8 | apr_int64_t period_msec); 9 | apr_interval_time_t timer_get_sleep_time(C4Timer *timer); 10 | bool timer_poll(C4Timer *timer); 11 | 12 | #endif /* TIMER_H */ 13 | -------------------------------------------------------------------------------- /src/libc4/include/types/agg_funcs.h: -------------------------------------------------------------------------------- 1 | #ifndef AGG_FUNCS_H 2 | #define AGG_FUNCS_H 3 | 4 | #include "parser/ast.h" 5 | 6 | typedef union AggStateVal 7 | { 8 | Datum d; /* "Simple" state value */ 9 | void *ptr; /* Opaque pointer to agg-maintained internal state */ 10 | } AggStateVal; 11 | 12 | struct AggOperator; 13 | 14 | /* 15 | * Initialize the state of an agg, given an initial input value. If NULL, use 16 | * the initial value as the state. 17 | */ 18 | typedef AggStateVal (*agg_init_f)(Datum v, struct AggOperator *agg_op, int aggno); 19 | 20 | /* Forward or backward transition func: return updated state */ 21 | typedef AggStateVal (*agg_trans_f)(AggStateVal state, Datum v); 22 | 23 | /* Emit an output value. If NULL, use the current simple state value */ 24 | typedef Datum (*agg_output_f)(AggStateVal state); 25 | 26 | /* Shutdown an aggregate. If NULL, shutdown is a no-op. */ 27 | typedef void (*agg_shutdown_f)(AggStateVal state); 28 | 29 | typedef struct AggFuncDesc 30 | { 31 | agg_init_f init_f; 32 | agg_trans_f fw_trans_f; 33 | agg_trans_f bw_trans_f; 34 | agg_output_f output_f; 35 | agg_shutdown_f shutdown_f; 36 | } AggFuncDesc; 37 | 38 | AggFuncDesc *lookup_agg_desc(AstAggKind agg_kind); 39 | 40 | #endif /* AGG_FUNCS_H */ 41 | -------------------------------------------------------------------------------- /src/libc4/include/types/catalog.h: -------------------------------------------------------------------------------- 1 | #ifndef CATALOG_H 2 | #define CATALOG_H 3 | 4 | #include "c4-api-callback.h" 5 | #include "parser/ast.h" 6 | #include "types/datum.h" 7 | #include "types/schema.h" 8 | #include "util/list.h" 9 | 10 | typedef struct C4Catalog C4Catalog; 11 | 12 | struct AbstractTable; 13 | struct OpChainList; 14 | struct Tuple; 15 | 16 | typedef struct CallbackRecord CallbackRecord; 17 | 18 | /* 19 | * A TableDef is a container for metadata about a C4 table. Because this 20 | * struct is shared among threads, fields of the structure should not be 21 | * modified. 22 | */ 23 | typedef struct TableDef 24 | { 25 | apr_pool_t *pool; 26 | char *name; 27 | AstStorageKind storage; 28 | Schema *schema; 29 | 30 | /* Column number of location spec, or -1 if none */ 31 | int ls_colno; 32 | 33 | /* List of callbacks registered for this table */ 34 | CallbackRecord *cb; 35 | 36 | /* Table implementation */ 37 | struct AbstractTable *table; 38 | 39 | /* 40 | * We keep a direct pointer to the router's list of op chains that have 41 | * this table as their delta table. This is mildly gross from a layering 42 | * POV, but useful for perf: it means we can skip a hash table lookup 43 | * for each derived tuple that is routed. 44 | */ 45 | struct OpChainList *op_chain_list; 46 | } TableDef; 47 | 48 | struct CallbackRecord 49 | { 50 | C4TupleCallback callback; 51 | void *data; 52 | struct CallbackRecord *next; 53 | }; 54 | 55 | C4Catalog *cat_make(C4Runtime *c4); 56 | 57 | void cat_define_table(C4Catalog *cat, const char *name, 58 | AstStorageKind storage, List *schema); 59 | void cat_delete_table(C4Catalog *cat, const char *name); 60 | bool cat_table_exists(C4Catalog *cat, const char *name); 61 | TableDef *cat_get_table(C4Catalog *cat, const char *name); 62 | struct AbstractTable *cat_get_table_impl(C4Catalog *cat, const char *name); 63 | 64 | void cat_register_callback(C4Catalog *cat, const char *tbl_name, 65 | C4TupleCallback callback, void *data); 66 | void table_invoke_callbacks(struct Tuple *tuple, TableDef *tbl, bool is_delete); 67 | 68 | bool is_numeric_type(DataType type_id); 69 | bool is_valid_type_name(const char *type_name); 70 | DataType get_type_id(const char *type_name); 71 | char *get_type_name(DataType type_id); 72 | 73 | datum_hash_func type_get_hash_func(DataType type); 74 | datum_eq_func type_get_eq_func(DataType type); 75 | datum_cmp_func type_get_cmp_func(DataType type); 76 | datum_bin_in_func type_get_binary_in_func(DataType type); 77 | datum_text_in_func type_get_text_in_func(DataType type); 78 | datum_bin_out_func type_get_binary_out_func(DataType type); 79 | datum_text_out_func type_get_text_out_func(DataType type); 80 | 81 | #endif /* CATALOG_H */ 82 | -------------------------------------------------------------------------------- /src/libc4/include/types/datum.h: -------------------------------------------------------------------------------- 1 | #ifndef DATUM_H 2 | #define DATUM_H 3 | 4 | #include "util/strbuf.h" 5 | 6 | /* 7 | * This is effectively a set of type IDs. A more sophisticated type ID 8 | * system is probably a natural next step. 9 | */ 10 | typedef unsigned char DataType; 11 | 12 | #define TYPE_INVALID 0 13 | #define TYPE_BOOL 1 14 | #define TYPE_CHAR 2 15 | #define TYPE_DOUBLE 3 16 | #define TYPE_INT 4 17 | #define TYPE_STRING 5 18 | 19 | typedef struct C4String 20 | { 21 | /* The number of bytes in "data"; we do NOT store a NUL terminator */ 22 | apr_uint32_t len; 23 | apr_uint16_t refcount; 24 | char data[1]; /* Variable-sized */ 25 | } C4String; 26 | 27 | typedef union Datum 28 | { 29 | /* Pass-by-value types (unboxed) */ 30 | bool b; 31 | unsigned char c; 32 | apr_int64_t i8; 33 | double d8; 34 | /* Pass-by-ref types (boxed) */ 35 | C4String *s; 36 | } Datum; 37 | 38 | typedef bool (*datum_eq_func)(Datum d1, Datum d2); 39 | typedef int (*datum_cmp_func)(Datum d1, Datum d2); 40 | typedef apr_uint32_t (*datum_hash_func)(Datum d); 41 | typedef Datum (*datum_bin_in_func)(StrBuf *buf); 42 | typedef Datum (*datum_text_in_func)(const char *str); 43 | typedef void (*datum_bin_out_func)(Datum d, StrBuf *buf); 44 | typedef void (*datum_text_out_func)(Datum d, StrBuf *buf); 45 | 46 | bool bool_equal(Datum d1, Datum d2); 47 | bool char_equal(Datum d1, Datum d2); 48 | bool double_equal(Datum d1, Datum d2); 49 | bool int_equal(Datum d1, Datum d2); 50 | bool string_equal(Datum d1, Datum d2); 51 | 52 | int bool_cmp(Datum d1, Datum d2); 53 | int char_cmp(Datum d1, Datum d2); 54 | int double_cmp(Datum d1, Datum d2); 55 | int int_cmp(Datum d1, Datum d2); 56 | int string_cmp(Datum d1, Datum d2); 57 | 58 | apr_uint32_t bool_hash(Datum d); 59 | apr_uint32_t char_hash(Datum d); 60 | apr_uint32_t double_hash(Datum d); 61 | apr_uint32_t int_hash(Datum d); 62 | apr_uint32_t string_hash(Datum d); 63 | 64 | /* Binary input functions */ 65 | Datum bool_from_buf(StrBuf *buf); 66 | Datum char_from_buf(StrBuf *buf); 67 | Datum double_from_buf(StrBuf *buf); 68 | Datum int_from_buf(StrBuf *buf); 69 | Datum string_from_buf(StrBuf *buf); 70 | 71 | /* Binary output functions */ 72 | void bool_to_buf(Datum d, StrBuf *buf); 73 | void char_to_buf(Datum d, StrBuf *buf); 74 | void double_to_buf(Datum d, StrBuf *buf); 75 | void int_to_buf(Datum d, StrBuf *buf); 76 | void string_to_buf(Datum d, StrBuf *buf); 77 | 78 | /* Text input functions */ 79 | Datum bool_from_str(const char *str); 80 | Datum char_from_str(const char *str); 81 | Datum double_from_str(const char *str); 82 | Datum int_from_str(const char *str); 83 | Datum string_from_str(const char *str); 84 | 85 | /* Text output functions */ 86 | void bool_to_str(Datum d, StrBuf *buf); 87 | void char_to_str(Datum d, StrBuf *buf); 88 | void double_to_str(Datum d, StrBuf *buf); 89 | void int_to_str(Datum d, StrBuf *buf); 90 | void string_to_str(Datum d, StrBuf *buf); 91 | 92 | char *string_to_text(Datum d, apr_pool_t *pool); 93 | 94 | bool datum_equal(Datum d1, Datum d2, DataType type); 95 | int datum_cmp(Datum d1, Datum d2, DataType type); 96 | Datum datum_copy(Datum d, DataType type); 97 | void datum_free(Datum d, DataType type); 98 | void pool_track_datum(apr_pool_t *pool, Datum datum, DataType type); 99 | 100 | void datum_to_str(Datum d, DataType type, StrBuf *buf); 101 | Datum datum_from_str(DataType type, const char *str); 102 | 103 | C4String *make_string(apr_size_t slen); 104 | 105 | #endif /* DATUM_H */ 106 | -------------------------------------------------------------------------------- /src/libc4/include/types/expr.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPR_H 2 | #define EXPR_H 3 | 4 | #include "parser/ast.h" 5 | #include "types/tuple.h" 6 | 7 | typedef struct ExprEvalContext 8 | { 9 | Tuple *inner; 10 | Tuple *outer; 11 | } ExprEvalContext; 12 | 13 | typedef struct ExprNode ExprNode; 14 | typedef struct ExprState ExprState; 15 | 16 | typedef Datum (*eval_expr_func)(ExprState *state); 17 | 18 | struct ExprState 19 | { 20 | ExprEvalContext *cxt; 21 | ExprNode *expr; 22 | eval_expr_func expr_func; 23 | ExprState *lhs; 24 | ExprState *rhs; 25 | }; 26 | 27 | struct ExprNode 28 | { 29 | C4Node node; 30 | DataType type; 31 | }; 32 | 33 | typedef struct ExprVar 34 | { 35 | ExprNode expr; 36 | int attno; 37 | bool is_outer; 38 | char *name; 39 | } ExprVar; 40 | 41 | typedef struct ExprOp 42 | { 43 | ExprNode expr; 44 | AstOperKind op_kind; 45 | ExprNode *lhs; 46 | ExprNode *rhs; 47 | } ExprOp; 48 | 49 | typedef struct ExprConst 50 | { 51 | ExprNode expr; 52 | Datum value; 53 | } ExprConst; 54 | 55 | #define eval_expr(state) ((state)->expr_func(state)) 56 | 57 | ExprState *make_expr_state(ExprNode *expr, ExprEvalContext *cxt, 58 | apr_pool_t *pool); 59 | 60 | bool eval_qual_set(int nquals, ExprState **qual_ary); 61 | 62 | #endif /* EXPR_H */ 63 | -------------------------------------------------------------------------------- /src/libc4/include/types/schema.h: -------------------------------------------------------------------------------- 1 | #ifndef SCHEMA_H 2 | #define SCHEMA_H 3 | 4 | #include "types/datum.h" 5 | #include "util/error.h" 6 | #include "util/list.h" 7 | #include "util/tuple_pool.h" 8 | 9 | typedef struct Schema 10 | { 11 | int len; 12 | DataType *types; 13 | datum_hash_func *hash_funcs; 14 | datum_eq_func *eq_funcs; 15 | datum_bin_in_func *bin_in_funcs; 16 | datum_text_in_func *text_in_funcs; 17 | datum_bin_out_func *bin_out_funcs; 18 | datum_text_out_func *text_out_funcs; 19 | /* Used to manage tuple allocations */ 20 | TuplePool *tuple_pool; 21 | } Schema; 22 | 23 | struct ExprState; 24 | 25 | Schema *schema_make(int len, DataType *types, C4Runtime *c4, apr_pool_t *pool); 26 | Schema *schema_make_from_ast(List *ast_schema, C4Runtime *c4, apr_pool_t *pool); 27 | Schema *schema_make_from_exprs(int len, struct ExprState **expr_ary, 28 | C4Runtime *c4, apr_pool_t *pool); 29 | bool schema_equal(Schema *s1, Schema *s2); 30 | char *schema_to_sql_param_str(Schema *schema, apr_pool_t *pool); 31 | 32 | static inline DataType 33 | schema_get_type(Schema *s, int idx) 34 | { 35 | ASSERT(idx >= 0); 36 | ASSERT(idx < s->len); 37 | 38 | return s->types[idx]; 39 | } 40 | 41 | #endif /* SCHEMA_H */ 42 | -------------------------------------------------------------------------------- /src/libc4/include/types/tuple.h: -------------------------------------------------------------------------------- 1 | #ifndef TUPLE_H 2 | #define TUPLE_H 3 | 4 | #include "types/catalog.h" 5 | #include "types/datum.h" 6 | #include "types/schema.h" 7 | #include "util/strbuf.h" 8 | 9 | /* 10 | * Note that tuple refcounts are not currently tracked by the APR pool cleanup 11 | * mechanism. Therefore, modules keeping a tuple pinned should be sure to 12 | * release their pin in a cleanup function. 13 | */ 14 | typedef struct Tuple 15 | { 16 | /* 17 | * TODO: On LP64 machines, we have 6 bytes of padding we can use. One 18 | * possible use for them is to cache the per-Tuple hash code. 19 | */ 20 | apr_uint16_t refcount; 21 | Datum vals[1]; /* Variable-length array */ 22 | } Tuple; 23 | 24 | #define tuple_get_val(t, i) ((t)->vals[(i)]) 25 | 26 | Tuple *tuple_make_empty(Schema *s); 27 | Tuple *tuple_make(Schema *s, Datum *values); 28 | Tuple *tuple_make_from_strings(Schema *s, char **values); 29 | 30 | void tuple_pin(Tuple *tuple); 31 | void tuple_unpin(Tuple *tuple, Schema *s); 32 | 33 | bool tuple_equal(Tuple *t1, Tuple *t2, Schema *s); 34 | apr_uint32_t tuple_hash(Tuple *t, Schema *s); 35 | /* For use in hash tables */ 36 | bool tuple_cmp_tbl(const void *k1, const void *k2, void *data); 37 | unsigned int tuple_hash_tbl(const void *key, void *data); 38 | 39 | bool tuple_is_remote(Tuple *tuple, TableDef *tbl_def, C4Runtime *c4); 40 | 41 | char *tuple_to_str(Tuple *tuple, Schema *s, apr_pool_t *pool); 42 | void tuple_to_str_buf(Tuple *tuple, Schema *s, StrBuf *buf); 43 | void tuple_to_buf(Tuple *tuple, Schema *s, StrBuf *buf); 44 | Tuple *tuple_from_buf(StrBuf *buf, Schema *s); 45 | char *tuple_to_sql_insert_str(Tuple *tuple, Schema *s, apr_pool_t *pool); 46 | 47 | #endif /* TUPLE_H */ 48 | -------------------------------------------------------------------------------- /src/libc4/include/util/dump_table.h: -------------------------------------------------------------------------------- 1 | #ifndef DUMP_TABLE_H 2 | #define DUMP_TABLE_H 3 | 4 | #include "util/strbuf.h" 5 | 6 | void dump_table(C4Runtime *c4, const char *tbl_name, StrBuf *buf); 7 | 8 | #endif /* DUMP_H */ 9 | -------------------------------------------------------------------------------- /src/libc4/include/util/error.h: -------------------------------------------------------------------------------- 1 | #ifndef ERROR_H 2 | #define ERROR_H 3 | 4 | /* 5 | * Note that we include "fmt" in the variadic argument list, because C99 6 | * apparently doesn't allow variadic macros to be invoked without any vargs 7 | * parameters. 8 | */ 9 | #define ERROR(...) var_error(__FILE__, __LINE__, __VA_ARGS__) 10 | #define FAIL() simple_error(__FILE__, __LINE__) 11 | #define FAIL_APR(s) apr_error((s), __FILE__, __LINE__) 12 | #define FAIL_SQLITE(c) sqlite_error((c), __FILE__, __LINE__) 13 | 14 | #ifdef C4_ASSERT_ENABLED 15 | #define ASSERT(cond) \ 16 | do { \ 17 | if (!(cond)) \ 18 | assert_fail(APR_STRINGIFY(cond), __FILE__, __LINE__); \ 19 | } while (0) 20 | #else 21 | #define ASSERT(cond) 22 | #endif 23 | 24 | void apr_error(apr_status_t s, const char *file, int line_num) __attribute__((noreturn)); 25 | void sqlite_error(C4Runtime *c4, const char *file, int line_num) __attribute__((noreturn)); 26 | void simple_error(const char *file, int line_num) __attribute__((noreturn)); 27 | void var_error(const char *file, int line_num, 28 | const char *fmt, ...) __attribute__((format(printf, 3, 4), noreturn)); 29 | 30 | void assert_fail(const char *cond, const char *file, int line_num) __attribute__((noreturn)); 31 | 32 | #endif /* ERROR_H */ 33 | -------------------------------------------------------------------------------- /src/libc4/include/util/hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C4Hash is a hash table implementation based on the apr_hash code from 3 | * APR. This version taken from APR trunk, as of July 3 11:00 AM PST 4 | * (r791000). ~nrc 5 | */ 6 | 7 | /* Licensed to the Apache Software Foundation (ASF) under one or more 8 | * contributor license agreements. See the NOTICE file distributed with 9 | * this work for additional information regarding copyright ownership. 10 | * The ASF licenses this file to You under the Apache License, Version 2.0 11 | * (the "License"); you may not use this file except in compliance with 12 | * the License. You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | 23 | #ifndef C4_HASH_H 24 | #define C4_HASH_H 25 | 26 | /** 27 | * Abstract type for hash tables. 28 | */ 29 | typedef struct c4_hash_t c4_hash_t; 30 | 31 | /** 32 | * Abstract type for scanning hash tables. 33 | */ 34 | typedef struct c4_hash_index_t c4_hash_index_t; 35 | 36 | /** 37 | * Callback functions for calculating hash values. 38 | * @param key The key. 39 | * @param klen The length of the key. 40 | * @param user_data User-specified opaque callback data 41 | */ 42 | typedef unsigned int (*c4_hashfunc_t)(const char *key, int klen, 43 | void *user_data); 44 | 45 | /** 46 | * Callback functions for determining whether two hash table keys are equal. 47 | * @param k1 First key 48 | * @param k2 Second key 49 | * @param klen The length of both keys. 50 | * @param user_data User-specified opaque callback data 51 | * @return Zero if the two keys should be considered equal, non-zero otherwise. 52 | */ 53 | typedef bool (*c4_keycomp_func_t)(const void *k1, const void *k2, 54 | int klen, void *user_data); 55 | 56 | /** 57 | * Create a hash table 58 | * @param pool The pool to allocate the hash table out of 59 | * @param key_len The length of keys in the table (must be fixed) 60 | * @param hash_func A custom hash function 61 | * @param cmp_func A custom key comparison function 62 | * @param cb_data Opaque user data that is passed to hash and cmp functions 63 | * @return The hash table just created, or NULL if memory allocation failed 64 | */ 65 | c4_hash_t *c4_hash_make(apr_pool_t *pool, int key_len, void *cb_data, 66 | c4_hashfunc_t hash_func, 67 | c4_keycomp_func_t cmp_func); 68 | 69 | /** 70 | * Associate a value with a key in a hash table. 71 | * @param ht The hash table 72 | * @param key Pointer to the key 73 | * @param val Value to associate with the key 74 | * @remark If the value is NULL the hash entry is deleted. 75 | */ 76 | void c4_hash_set(c4_hash_t *ht, const void *key, void *val); 77 | 78 | /** 79 | * Associated a value with a key in the hash table, iff the key is not 80 | * already in the hash table. 81 | * @param ht The hash table 82 | * @param key Pointer to the key 83 | * @param val Value to associate with the key, if the key is not already in 84 | * the hash table. Cannot be NULL. 85 | * @param is_new Output parameter; set to true iff key was not previously 86 | * in hash table 87 | * @return The value associated with the key. If key is new, this is "val"; 88 | * otherwise, the hash table is unmodified, and the result is 89 | * equivalent to c4_hash_get(ht, key). 90 | */ 91 | void *c4_hash_set_if_new(c4_hash_t *ht, const void *key, 92 | void *val, bool *is_new); 93 | 94 | 95 | /** 96 | * Look up the value associated with a key in a hash table. 97 | * @param ht The hash table 98 | * @param key Pointer to the key 99 | * @return Returns NULL if the key is not present. 100 | */ 101 | void *c4_hash_get(c4_hash_t *ht, const void *key); 102 | 103 | /** 104 | * Remove an entry from the hash table. 105 | * @param ht The hash table 106 | * @param key Pointer to the key 107 | * @return true if the key was found (and deleted), false if the key was not 108 | * found 109 | */ 110 | bool c4_hash_remove(c4_hash_t *ht, const void *key); 111 | 112 | /** 113 | * Prepare to start iterating over the entries in a hash table. This 114 | * creates an iterator and places it "before" the first element in the 115 | * hash table. 116 | * @param p The pool to allocate the c4_hash_index_t iterator in. Must 117 | * not be NULL. 118 | * @param ht The hash table 119 | */ 120 | c4_hash_index_t *c4_hash_iter_make(apr_pool_t *p, c4_hash_t *ht); 121 | 122 | /** 123 | * Reset the state of a hash table iterator, so that it is placed 124 | * "before" the first element in the hash table. 125 | */ 126 | void c4_hash_iter_reset(c4_hash_index_t *hi); 127 | 128 | /** 129 | * Continue iterating over the entries in a hash table. 130 | * @param hi The iteration state. This is written-through (modified). 131 | * @return true if there are any entries remaining in the hash table, false otherwise. 132 | */ 133 | bool c4_hash_iter_next(c4_hash_index_t *hi); 134 | 135 | /** 136 | * Get the current key from the iteration state. 137 | * @param hi The iteration state 138 | * @return Pointer for the current key. 139 | */ 140 | const void *c4_hash_this_key(c4_hash_index_t *hi); 141 | 142 | /** 143 | * Get the current value from the iteration state. 144 | * @param hi The iteration state 145 | * @return Pointer for the current value. 146 | */ 147 | void *c4_hash_this_val(c4_hash_index_t *hi); 148 | 149 | /** 150 | * Get the number of key/value pairs in the hash table. 151 | * @param ht The hash table 152 | * @return The number of key/value pairs in the hash table. 153 | */ 154 | unsigned int c4_hash_count(c4_hash_t *ht); 155 | 156 | /** 157 | * Clear any key/value pairs in the hash table. 158 | * @param ht The hash table 159 | */ 160 | void c4_hash_clear(c4_hash_t *ht); 161 | 162 | /** @} */ 163 | 164 | #endif /* !C4_HASH_H */ 165 | -------------------------------------------------------------------------------- /src/libc4/include/util/hash_func.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_FUNC_H 2 | #define HASH_FUNC_H 3 | 4 | apr_uint32_t hash_any(register const unsigned char *k, register int keylen); 5 | 6 | #endif /* HASH_FUNC_H */ 7 | -------------------------------------------------------------------------------- /src/libc4/include/util/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple linked list implementation. The code and interface is inspired 3 | * by the PostgreSQL linked list implementation. ~nrc 4 | */ 5 | #ifndef LIST_H 6 | #define LIST_H 7 | 8 | typedef struct ListCell ListCell; 9 | 10 | typedef struct 11 | { 12 | apr_pool_t *pool; 13 | int length; 14 | ListCell *head; 15 | ListCell *tail; 16 | } List; 17 | 18 | #define list_head(list) (list)->head 19 | #define list_tail(list) (list)->tail 20 | #define list_length(list) (list)->length 21 | #define list_is_empty(list) (list_length(list) == 0) 22 | 23 | struct ListCell 24 | { 25 | union 26 | { 27 | void *ptr_value; 28 | int int_value; 29 | } data; 30 | ListCell *next; 31 | }; 32 | 33 | #define lc_ptr(lc) ((lc)->data.ptr_value) 34 | #define lc_int(lc) ((lc)->data.int_value) 35 | 36 | /* 37 | * foreach - 38 | * a convenience macro which loops through the list 39 | */ 40 | #define foreach(cell, l) \ 41 | for ((cell) = (l)->head; (cell) != NULL; (cell) = (cell)->next) 42 | 43 | /* 44 | * forrest - 45 | * starting from the given cell, advance through the rest of the list 46 | */ 47 | #define forrest(cell, start) \ 48 | for ((cell) = (start); (cell) != NULL; (cell) = (cell)->next) 49 | 50 | List *list_make(apr_pool_t *pool); 51 | #define list_make1(datum, pool) list_append(list_make(pool), (datum)) 52 | #define list_make1_int(datum, pool) list_append_int(list_make(pool), (datum)) 53 | 54 | List *list_append(List *list, void *datum); 55 | List *list_append_int(List *list, int datum); 56 | 57 | List *list_prepend(List *list, void *datum); 58 | List *list_prepend_int(List *list, int datum); 59 | 60 | bool list_member(List *list, const void *datum); 61 | bool list_member_int(List *list, int datum); 62 | bool list_member_str(List *list, const char *str); 63 | 64 | void *list_get(List *list, int idx); 65 | int list_get_int(List *list, int idx); 66 | 67 | void *list_remove_head(List *list); 68 | int list_remove_head_int(List *list); 69 | 70 | void list_remove_cell(List *list, ListCell *cell, ListCell *prev); 71 | 72 | List *list_copy(List *list, apr_pool_t *pool); 73 | List *list_copy_deep(List *list, apr_pool_t *pool); 74 | List *list_copy_str(List *list, apr_pool_t *pool); 75 | 76 | List *list_reverse(List *list, apr_pool_t *pool); 77 | 78 | void list_to_str(List *list, StrBuf *sbuf); 79 | 80 | #endif /* LIST_H */ 81 | -------------------------------------------------------------------------------- /src/libc4/include/util/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_H 2 | #define LOGGER_H 3 | 4 | #include "types/tuple.h" 5 | 6 | typedef struct C4Logger C4Logger; 7 | 8 | C4Logger *logger_make(C4Runtime *c4); 9 | void c4_log(C4Runtime *c4, const char *fmt, ...) 10 | __attribute__((format(printf, 2, 3))); 11 | char *log_tuple(C4Runtime *c4, Tuple *tuple, Schema *s); 12 | char *log_datum(C4Runtime *c4, Datum datum, DataType type); 13 | 14 | void c4_warn_apr(C4Runtime *c4, apr_status_t s, const char *fmt, ...) 15 | __attribute__((format(printf, 3, 4))); 16 | 17 | #endif /* LOGGER_H */ 18 | -------------------------------------------------------------------------------- /src/libc4/include/util/mem.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_H 2 | #define MEM_H 3 | 4 | #include 5 | 6 | void *ol_alloc(apr_size_t sz); 7 | void *ol_alloc0(apr_size_t sz); 8 | void *ol_realloc(void *ptr, apr_size_t sz); 9 | void ol_free(void *ptr); 10 | char *ol_strdup(const char *str); 11 | 12 | apr_pool_t *make_subpool(apr_pool_t *parent); 13 | 14 | #endif /* MEM_H */ 15 | -------------------------------------------------------------------------------- /src/libc4/include/util/rset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * RSet is a refcounted set type: each element of the set has a count of the 3 | * number of times it has been inserted. The count is decremented on removal; 4 | * when the count reaches zero, the element is deleted from the set. 5 | * 6 | * RSet is based on C4Hash, as of rev fad4ec5d1b9e (r635). 7 | */ 8 | 9 | /* Licensed to the Apache Software Foundation (ASF) under one or more 10 | * contributor license agreements. See the NOTICE file distributed with 11 | * this work for additional information regarding copyright ownership. 12 | * The ASF licenses this file to You under the Apache License, Version 2.0 13 | * (the "License"); you may not use this file except in compliance with 14 | * the License. You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef RSET_H 26 | #define RSET_H 27 | 28 | /** 29 | * Abstract type for refcounted sets. 30 | */ 31 | typedef struct rset_t rset_t; 32 | 33 | /** 34 | * Abstract type for scanning refcounted sets. 35 | */ 36 | typedef struct rset_index_t rset_index_t; 37 | 38 | /** 39 | * Callback functions for calculating hash values. 40 | * @param key The key. 41 | * @param user_data User-specified opaque callback data 42 | */ 43 | typedef unsigned int (*rset_hashfunc_t)(const void *key, void *user_data); 44 | 45 | /** 46 | * Callback functions for determining whether two elements are equal. 47 | * @param e1 First element 48 | * @param e2 Second element 49 | * @param user_data User-specified opaque callback data 50 | * @return Zero if the two elements should be considered equal, non-zero otherwise. 51 | */ 52 | typedef bool (*rset_keycomp_func_t)(const void *k1, const void *k2, 53 | void *user_data); 54 | 55 | /** 56 | * Create a refcounted set 57 | * @param pool The pool to allocate the rset out of 58 | * @param elem_len The length of elements in the table (must be fixed) 59 | * @param hash_func A custom hash function 60 | * @param cmp_func A custom key comparison function 61 | * @param cb_data Opaque user data that is passed to hash and cmp functions 62 | * @return The rset just created, or NULL if memory allocation failed 63 | */ 64 | rset_t *rset_make(apr_pool_t *pool, void *cb_data, 65 | rset_hashfunc_t hash_func, rset_keycomp_func_t cmp_func); 66 | 67 | /** 68 | * Add an element to the rset. If the element is already present, bump its 69 | * refcount by 1 and return false; otherwise, add a new element with refcount 1, 70 | * and return true. 71 | * @param rs The rset 72 | * @param elem New element 73 | */ 74 | bool rset_add(rset_t *rs, void *elem); 75 | 76 | /** 77 | * Look up the refcount of an element in the rset. 78 | * @param rs The rset 79 | * @param elem Element to search for 80 | * @return The refcount of the element, or 0 if the element is not found. 81 | */ 82 | unsigned int rset_get(rset_t *rs, void *elem); 83 | 84 | /** 85 | * Decrement the refcount of an element in the rset. If the refcount reaches 86 | * zero, the element is removed. 87 | * @param rs The rset 88 | * @param elem Element to remove 89 | * @param new_rc The updated refcount of the element (might be zero). If the 90 | * element wasn't found in the rset, this is not written-through. 91 | * @return NULL if the element was not found. 92 | */ 93 | void *rset_remove(rset_t *rs, void *elem, unsigned int *new_refcount); 94 | 95 | /** 96 | * Prepare to start iterating over the elements of an rset. This creates an 97 | * iterator and places it "before" the first element in the rset. 98 | * @param p The pool to allocate the rset_index_t iterator in. Must 99 | * not be NULL. 100 | * @param rs The rset. 101 | */ 102 | rset_index_t *rset_iter_make(apr_pool_t *p, rset_t *rs); 103 | 104 | /** 105 | * Reset the state of an rset iterator, so that it is placed "before" the first 106 | * element of the rset. 107 | */ 108 | void rset_iter_reset(rset_index_t *ri); 109 | 110 | /** 111 | * Continue iterating over the entries in an rset. 112 | * @param hi The iteration state. This is written-through (modified). 113 | * @return true if there are any entries remaining in the rset, false otherwise. 114 | */ 115 | bool rset_iter_next(rset_index_t *ri); 116 | 117 | /** 118 | * Get the current entry from the iteration state. 119 | * @param hi The iteration state 120 | * @return The current element 121 | */ 122 | void *rset_this(rset_index_t *ri); 123 | 124 | /** 125 | * Get the number of elements in the rset. 126 | * @param rs The rset. 127 | * @return The number of elements in the rset. 128 | */ 129 | unsigned int rset_count(rset_t *rs); 130 | 131 | /** @} */ 132 | 133 | #endif /* !RSET_H */ 134 | -------------------------------------------------------------------------------- /src/libc4/include/util/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef SOCKET_H 2 | #define SOCKET_H 3 | 4 | #include 5 | 6 | void socket_set_non_block(apr_socket_t *sock); 7 | 8 | apr_sockaddr_t *socket_get_remote_addr(apr_socket_t *sock); 9 | char *socket_get_remote_loc(apr_socket_t *sock, apr_pool_t *pool); 10 | 11 | #endif /* SOCKET_H */ 12 | 13 | -------------------------------------------------------------------------------- /src/libc4/include/util/strbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef STRBUF_H 2 | #define STRBUF_H 3 | 4 | #include 5 | 6 | /* 7 | * A StrBuf is an extensible string buffer. "data" holds the region of storage 8 | * currently allocated for the string. "len" holds the current logical length of 9 | * the buffer. "maxlen" holds the amount of storage allocated (maxlen - len is 10 | * the amount of currently-free storage). Note that StrBuf is not required to be 11 | * NUL-terminated, and can be used to store binary data. 12 | * 13 | * In addition to append operations, StrBuf also allows "relative" read 14 | * operations: that is, it records a current "position," and interprets the read 15 | * request relative to that position. 16 | * 17 | * Because APR pools don't provide an equivalent to either free() or realloc(), 18 | * we currently allocate the buffer's storage via malloc(), and register a 19 | * cleanup function with the pool specified in sbuf_make(). 20 | * 21 | * Originally based on the StringInfo data type in PostgreSQL. 22 | */ 23 | typedef struct StrBuf 24 | { 25 | char *data; 26 | apr_size_t len; 27 | apr_size_t max_len; 28 | apr_size_t pos; 29 | } StrBuf; 30 | 31 | #define sbuf_data_avail(sbuf) ((sbuf)->len - (sbuf)->pos) 32 | 33 | StrBuf *sbuf_make(apr_pool_t *pool); 34 | void sbuf_reset(StrBuf *sbuf); 35 | void sbuf_reset_pos(StrBuf *sbuf); 36 | void sbuf_enlarge(StrBuf *sbuf, apr_size_t more_bytes); 37 | char *sbuf_dup(StrBuf *sbuf, apr_pool_t *pool); 38 | 39 | void sbuf_append(StrBuf *sbuf, const char *str); 40 | void sbuf_appendf(StrBuf *sbuf, const char *fmt, ...) 41 | __attribute((format(printf, 2, 3))); 42 | void sbuf_append_char(StrBuf *sbuf, char c); 43 | void sbuf_append_int16(StrBuf *sbuf, apr_uint16_t i); 44 | void sbuf_append_int32(StrBuf *sbuf, apr_uint32_t i); 45 | void sbuf_append_data(StrBuf *sbuf, const char *data, apr_size_t len); 46 | 47 | unsigned char sbuf_read_char(StrBuf *sbuf); 48 | apr_uint16_t sbuf_read_int16(StrBuf *sbuf); 49 | apr_uint32_t sbuf_read_int32(StrBuf *sbuf); 50 | void sbuf_read_data(StrBuf *sbuf, char *data, apr_size_t len); 51 | 52 | bool sbuf_socket_recv(StrBuf *sbuf, apr_socket_t *sock, 53 | apr_size_t len, bool *is_eof); 54 | bool sbuf_socket_send(StrBuf *sbuf, apr_socket_t *sock); 55 | 56 | #endif /* STRBUF_H */ 57 | -------------------------------------------------------------------------------- /src/libc4/include/util/thread_sync.h: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_SYNC_H 2 | #define THREAD_SYNC_H 3 | 4 | typedef struct C4ThreadSync C4ThreadSync; 5 | 6 | C4ThreadSync *thread_sync_make(apr_pool_t *pool); 7 | void thread_sync_wait(C4ThreadSync *ts); 8 | void thread_sync_signal(C4ThreadSync *ts); 9 | 10 | #endif /* THREAD_SYNC_H */ 11 | -------------------------------------------------------------------------------- /src/libc4/include/util/tuple_buf.h: -------------------------------------------------------------------------------- 1 | #ifndef TUPLE_BUF_H 2 | #define TUPLE_BUF_H 3 | 4 | /* 5 | * TupleBufs are used to hold collections of Tuples; right now, we use them 6 | * to accumulate to-be-routed derived tuples and pending network output 7 | * tuples in the midst of a fixpoint. 8 | * 9 | * XXX: Storing a TableDef with each entry is unfortunate, because in many 10 | * cases the number of distinct TableDefs in a large TupleBuf will be 11 | * small. For now seems better to trade memory consumption for faster 12 | * routing performance, but this tradeoff should be examined. 13 | */ 14 | typedef struct TupleBufEntry 15 | { 16 | Tuple *tuple; 17 | TableDef *tbl_def; 18 | } TupleBufEntry; 19 | 20 | typedef struct TupleBuf 21 | { 22 | int size; /* # of entries allocated in this buffer */ 23 | int start; /* Index of first valid (to-be-routed) entry */ 24 | int end; /* Index of last valid (to-be-routed) entry */ 25 | TupleBufEntry *entries; 26 | } TupleBuf; 27 | 28 | #define tuple_buf_is_empty(buf) ((buf)->start == (buf)->end) 29 | #define tuple_buf_size(buf) ((buf)->end - (buf)->start) 30 | 31 | TupleBuf *tuple_buf_make(int size, apr_pool_t *pool); 32 | void tuple_buf_reset(TupleBuf *buf); 33 | void tuple_buf_push(TupleBuf *buf, Tuple *tuple, TableDef *tbl_def); 34 | void tuple_buf_shift(TupleBuf *buf, Tuple **tuple, TableDef **tbl_def); 35 | void tuple_buf_dump(TupleBuf *buf, C4Runtime *c4); 36 | 37 | #endif /* TUPLE_BUF_H */ 38 | -------------------------------------------------------------------------------- /src/libc4/include/util/tuple_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef TUPLE_POOL_H 2 | #define TUPLE_POOL_H 3 | 4 | /* 5 | * A TuplePool is a memory allocator that is specialized for Tuples of a 6 | * particular size. We can do better than retail malloc() and free(): we grab 7 | * large chunks of memory via apr_palloc() and then use them to hold many 8 | * individual Tuples. There is also a simple freelist: when a Tuple is returned 9 | * to the pool, it is then eligible to be reused as the storage for a 10 | * newly-allocated Tuple. Note that we make no attempt to avoid fragmentation 11 | * or return the contents of the freelist to the OS if it grows large. 12 | */ 13 | typedef struct TuplePool TuplePool; 14 | 15 | void *tuple_pool_loan(TuplePool *tpool); 16 | void tuple_pool_return(TuplePool *tpool, void *ptr); 17 | 18 | /* 19 | * A TuplePoolMgr manages the set of TuplePools in a given C4 instance. It is 20 | * just used as a means to share TuplePools of the same size among different 21 | * call sites. 22 | */ 23 | typedef struct TuplePoolMgr TuplePoolMgr; 24 | 25 | TuplePoolMgr *tpool_mgr_make(apr_pool_t *pool); 26 | TuplePool *get_tuple_pool(TuplePoolMgr *tpool_mgr, apr_size_t elem_size); 27 | 28 | #endif /* TUPLE_POOL_H */ 29 | -------------------------------------------------------------------------------- /src/libc4/nodes/copyfuncs.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "nodes/copyfuncs.h" 3 | #include "nodes/makefuncs.h" 4 | 5 | static AstProgram * 6 | copy_program(AstProgram *in, apr_pool_t *p) 7 | { 8 | return make_program(in->defines, in->timers, in->facts, in->rules, p); 9 | } 10 | 11 | static AstDefine * 12 | copy_define(AstDefine *in, apr_pool_t *p) 13 | { 14 | return make_define(in->name, in->storage, in->schema, p); 15 | } 16 | 17 | static AstTimer * 18 | copy_ast_timer(AstTimer *in, apr_pool_t *p) 19 | { 20 | return make_ast_timer(in->name, in->period, p); 21 | } 22 | 23 | static AstSchemaElt * 24 | copy_schema_elt(AstSchemaElt *in, apr_pool_t *p) 25 | { 26 | return make_schema_elt(in->type_name, in->is_loc_spec, p); 27 | } 28 | 29 | static AstRule * 30 | copy_rule(AstRule *in, apr_pool_t *p) 31 | { 32 | return make_rule(in->name, in->is_delete, in->is_network, in->has_agg, 33 | in->head, in->joins, in->quals, p); 34 | } 35 | 36 | static AstFact * 37 | copy_fact(AstFact *in, apr_pool_t *p) 38 | { 39 | return make_fact(in->head, p); 40 | } 41 | 42 | static AstTableRef * 43 | copy_table_ref(AstTableRef *in, apr_pool_t *p) 44 | { 45 | return make_table_ref(in->name, in->cols, p); 46 | } 47 | 48 | static AstJoinClause * 49 | copy_join_clause(AstJoinClause *in, apr_pool_t *p) 50 | { 51 | return make_join_clause(in->ref, in->not, p); 52 | } 53 | 54 | static AstQualifier * 55 | copy_qualifier(AstQualifier *in, apr_pool_t *p) 56 | { 57 | return make_qualifier(in->expr, p); 58 | } 59 | 60 | static AstOpExpr * 61 | copy_ast_op_expr(AstOpExpr *in, apr_pool_t *p) 62 | { 63 | return make_ast_op_expr(in->lhs, in->rhs, in->op_kind, p); 64 | } 65 | 66 | static AstVarExpr * 67 | copy_ast_var_expr(AstVarExpr *in, apr_pool_t *p) 68 | { 69 | return make_ast_var_expr(in->name, in->type, p); 70 | } 71 | 72 | static AstConstExpr * 73 | copy_ast_const_expr(AstConstExpr *in, apr_pool_t *p) 74 | { 75 | return make_ast_const_expr(in->const_kind, in->value, p); 76 | } 77 | 78 | static AstAggExpr * 79 | copy_ast_agg_expr(AstAggExpr *in, apr_pool_t *p) 80 | { 81 | return make_ast_agg_expr(in->agg_kind, in->expr, p); 82 | } 83 | 84 | static AggPlan * 85 | copy_agg_plan(AggPlan *in, apr_pool_t *p) 86 | { 87 | return make_agg_plan(in->head, in->planned, 88 | in->plan.proj_list, p); 89 | } 90 | 91 | static FilterPlan * 92 | copy_filter_plan(FilterPlan *in, apr_pool_t *p) 93 | { 94 | return make_filter_plan(in->tbl_name, in->plan.quals, 95 | in->plan.qual_exprs, in->plan.proj_list, p); 96 | } 97 | 98 | static InsertPlan * 99 | copy_insert_plan(InsertPlan *in, apr_pool_t *p) 100 | { 101 | return make_insert_plan(in->head, in->plan.proj_list, p); 102 | } 103 | 104 | static ProjectPlan * 105 | copy_project_plan(ProjectPlan *in, apr_pool_t *p) 106 | { 107 | return make_project_plan(in->plan.proj_list, p); 108 | } 109 | 110 | static ScanPlan * 111 | copy_scan_plan(ScanPlan *in, apr_pool_t *p) 112 | { 113 | return make_scan_plan(in->scan_rel, in->plan.quals, in->plan.qual_exprs, 114 | in->plan.proj_list, p); 115 | } 116 | 117 | static ExprOp * 118 | copy_expr_op(ExprOp *in, apr_pool_t *p) 119 | { 120 | return make_expr_op(in->expr.type, in->op_kind, in->lhs, in->rhs, p); 121 | } 122 | 123 | static ExprVar * 124 | copy_expr_var(ExprVar *in, apr_pool_t *p) 125 | { 126 | return make_expr_var(in->expr.type, in->attno, in->is_outer, in->name, p); 127 | } 128 | 129 | static ExprConst * 130 | copy_expr_const(ExprConst *in, apr_pool_t *p) 131 | { 132 | return make_expr_const(in->expr.type, in->value, p); 133 | } 134 | 135 | void * 136 | copy_node(void *ptr, apr_pool_t *pool) 137 | { 138 | C4Node *n = (C4Node *) ptr; 139 | 140 | if (n == NULL) 141 | return NULL; 142 | 143 | switch (n->kind) 144 | { 145 | case AST_PROGRAM: 146 | return copy_program((AstProgram *) n, pool); 147 | 148 | case AST_DEFINE: 149 | return copy_define((AstDefine *) n, pool); 150 | 151 | case AST_TIMER: 152 | return copy_ast_timer((AstTimer *) n, pool); 153 | 154 | case AST_SCHEMA_ELT: 155 | return copy_schema_elt((AstSchemaElt *) n, pool); 156 | 157 | case AST_RULE: 158 | return copy_rule((AstRule *) n, pool); 159 | 160 | case AST_FACT: 161 | return copy_fact((AstFact *) n, pool); 162 | 163 | case AST_TABLE_REF: 164 | return copy_table_ref((AstTableRef *) n, pool); 165 | 166 | case AST_JOIN_CLAUSE: 167 | return copy_join_clause((AstJoinClause *) n, pool); 168 | 169 | case AST_QUALIFIER: 170 | return copy_qualifier((AstQualifier *) n, pool); 171 | 172 | case AST_OP_EXPR: 173 | return copy_ast_op_expr((AstOpExpr *) n, pool); 174 | 175 | case AST_AGG_EXPR: 176 | return copy_ast_agg_expr((AstAggExpr *) n, pool); 177 | 178 | case AST_VAR_EXPR: 179 | return copy_ast_var_expr((AstVarExpr *) n, pool); 180 | 181 | case AST_CONST_EXPR: 182 | return copy_ast_const_expr((AstConstExpr *) n, pool); 183 | 184 | case PLAN_AGG: 185 | return copy_agg_plan((AggPlan *) n, pool); 186 | 187 | case PLAN_FILTER: 188 | return copy_filter_plan((FilterPlan *) n, pool); 189 | 190 | case PLAN_INSERT: 191 | return copy_insert_plan((InsertPlan *) n, pool); 192 | 193 | case PLAN_PROJECT: 194 | return copy_project_plan((ProjectPlan *) n, pool); 195 | 196 | case PLAN_SCAN: 197 | return copy_scan_plan((ScanPlan *) n, pool); 198 | 199 | case EXPR_OP: 200 | return copy_expr_op((ExprOp *) n, pool); 201 | 202 | case EXPR_VAR: 203 | return copy_expr_var((ExprVar *) n, pool); 204 | 205 | case EXPR_CONST: 206 | return copy_expr_const((ExprConst *) n, pool); 207 | 208 | default: 209 | ERROR("Unrecognized node kind: %d", (int) n->kind); 210 | return NULL; /* Keep compiler quiet */ 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/libc4/nodes/nodes.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "nodes/nodes.h" 3 | #include "types/expr.h" 4 | 5 | static char * 6 | get_op_kind_str(AstOperKind op_kind) 7 | { 8 | switch (op_kind) 9 | { 10 | case AST_OP_PLUS: 11 | return "+"; 12 | 13 | case AST_OP_MINUS: 14 | return "-"; 15 | 16 | case AST_OP_TIMES: 17 | return "*"; 18 | 19 | case AST_OP_DIVIDE: 20 | return "/"; 21 | 22 | case AST_OP_MODULUS: 23 | return "%"; 24 | 25 | case AST_OP_UMINUS: 26 | return "-"; 27 | 28 | case AST_OP_LT: 29 | return "<"; 30 | 31 | case AST_OP_LTE: 32 | return "<="; 33 | 34 | case AST_OP_GT: 35 | return ">"; 36 | 37 | case AST_OP_GTE: 38 | return ">="; 39 | 40 | case AST_OP_EQ: 41 | return "=="; 42 | 43 | case AST_OP_NEQ: 44 | return "!="; 45 | 46 | default: 47 | ERROR("Unrecognized op kind: %d", (int) op_kind); 48 | } 49 | } 50 | 51 | static char * 52 | get_const_kind_str(AstConstKind c_kind) 53 | { 54 | switch (c_kind) 55 | { 56 | case AST_CONST_BOOL: 57 | return "bool"; 58 | 59 | case AST_CONST_CHAR: 60 | return "char"; 61 | 62 | case AST_CONST_DOUBLE: 63 | return "double"; 64 | 65 | case AST_CONST_INT: 66 | return "int"; 67 | 68 | case AST_CONST_STRING: 69 | return "string"; 70 | 71 | default: 72 | ERROR("Unrecognized const kind: %d", (int) c_kind); 73 | } 74 | } 75 | 76 | static void 77 | op_expr_to_str(ExprOp *op_expr, StrBuf *sbuf) 78 | { 79 | sbuf_appendf(sbuf, "OP: (%s ", get_op_kind_str(op_expr->op_kind)); 80 | node_to_str((C4Node *) op_expr->lhs, sbuf); 81 | if (op_expr->rhs) 82 | node_to_str((C4Node *) op_expr->rhs, sbuf); 83 | sbuf_append(sbuf, ")"); 84 | } 85 | 86 | static void 87 | var_expr_to_str(ExprVar *var_expr, StrBuf *sbuf) 88 | { 89 | sbuf_appendf(sbuf, "VAR: attno = %d, is_outer = %s, name = %s", 90 | var_expr->attno, var_expr->is_outer ? "true" : "false", 91 | var_expr->name); 92 | } 93 | 94 | static void 95 | const_expr_to_str(ExprConst *const_expr, StrBuf *sbuf) 96 | { 97 | sbuf_append(sbuf, "CONST: "); 98 | datum_to_str(const_expr->value, const_expr->expr.type, sbuf); 99 | } 100 | 101 | static void 102 | ast_op_expr_to_str(AstOpExpr *op_expr, StrBuf *sbuf) 103 | { 104 | sbuf_appendf(sbuf, "AST OP: (%s ", get_op_kind_str(op_expr->op_kind)); 105 | node_to_str(op_expr->lhs, sbuf); 106 | if (op_expr->rhs) 107 | node_to_str(op_expr->rhs, sbuf); 108 | sbuf_append(sbuf, ")"); 109 | } 110 | 111 | static void 112 | ast_const_expr_to_str(AstConstExpr *const_expr, StrBuf *sbuf) 113 | { 114 | sbuf_appendf(sbuf, "AST CONST: %s (kind = %s)", 115 | const_expr->value, 116 | get_const_kind_str(const_expr->const_kind)); 117 | } 118 | 119 | static void 120 | ast_var_expr_to_str(AstVarExpr *var_expr, StrBuf *sbuf) 121 | { 122 | sbuf_appendf(sbuf, "AST_VAR: name = %s, type = %s", 123 | var_expr->name, get_type_name(var_expr->type)); 124 | } 125 | 126 | static void 127 | ast_qual_to_str(AstQualifier *qual, StrBuf *sbuf) 128 | { 129 | node_to_str(qual->expr, sbuf); 130 | } 131 | 132 | void 133 | node_to_str(C4Node *node, StrBuf *sbuf) 134 | { 135 | sbuf_append_char(sbuf, '('); 136 | 137 | switch (node->kind) 138 | { 139 | case EXPR_OP: 140 | op_expr_to_str((ExprOp *) node, sbuf); 141 | break; 142 | 143 | case EXPR_VAR: 144 | var_expr_to_str((ExprVar *) node, sbuf); 145 | break; 146 | 147 | case EXPR_CONST: 148 | const_expr_to_str((ExprConst *) node, sbuf); 149 | break; 150 | 151 | case AST_OP_EXPR: 152 | ast_op_expr_to_str((AstOpExpr *) node, sbuf); 153 | break; 154 | 155 | case AST_VAR_EXPR: 156 | ast_var_expr_to_str((AstVarExpr *) node, sbuf); 157 | break; 158 | 159 | case AST_CONST_EXPR: 160 | ast_const_expr_to_str((AstConstExpr *) node, sbuf); 161 | break; 162 | 163 | case AST_QUALIFIER: 164 | ast_qual_to_str((AstQualifier *) node, sbuf); 165 | break; 166 | 167 | default: 168 | ERROR("Unexpected node kind: %d", (int) node->kind); 169 | } 170 | 171 | sbuf_append_char(sbuf, ')'); 172 | } 173 | 174 | char * 175 | node_get_kind_str(C4Node *node) 176 | { 177 | switch (node->kind) 178 | { 179 | case AST_PROGRAM: 180 | return "AstProgram"; 181 | case AST_DEFINE: 182 | return "AstDefine"; 183 | case AST_SCHEMA_ELT: 184 | return "AstSchemaElt"; 185 | case AST_RULE: 186 | return "AstRule"; 187 | case AST_FACT: 188 | return "AstFact"; 189 | case AST_TABLE_REF: 190 | return "AstTableRef"; 191 | case AST_JOIN_CLAUSE: 192 | return "AstJoinClause"; 193 | case AST_QUALIFIER: 194 | return "AstQualifier"; 195 | case AST_OP_EXPR: 196 | return "AstOpExpr"; 197 | case AST_VAR_EXPR: 198 | return "AstVarExpr"; 199 | case AST_CONST_EXPR: 200 | return "AstConstExpr"; 201 | case AST_AGG_EXPR: 202 | return "AstAggExpr"; 203 | 204 | case PLAN_AGG: 205 | return "PlanAgg"; 206 | case PLAN_FILTER: 207 | return "PlanFilter"; 208 | case PLAN_INSERT: 209 | return "PlanInsert"; 210 | case PLAN_PROJECT: 211 | return "PlanProject"; 212 | case PLAN_SCAN: 213 | return "PlanScan"; 214 | 215 | case OPER_AGG: 216 | return "OperAgg"; 217 | case OPER_FILTER: 218 | return "OperFilter"; 219 | case OPER_INSERT: 220 | return "OperInsert"; 221 | case OPER_PROJECT: 222 | return "OperProject"; 223 | case OPER_SCAN: 224 | return "OperScan"; 225 | 226 | case EXPR_OP: 227 | return "ExprOp"; 228 | case EXPR_VAR: 229 | return "ExprVar"; 230 | case EXPR_CONST: 231 | return "ExprConst"; 232 | 233 | default: 234 | ERROR("Unrecognized node kind: %d", (int) node->kind); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/libc4/operator/filter.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "operator/filter.h" 3 | 4 | static void 5 | filter_invoke(Operator *op, Tuple *t) 6 | { 7 | FilterOperator *filter_op = (FilterOperator *) op; 8 | ExprEvalContext *exec_cxt; 9 | 10 | exec_cxt = filter_op->op.exec_cxt; 11 | exec_cxt->inner = t; 12 | 13 | /* 14 | * Only route the tuple onward if it passes all the quals. Note that 15 | * although OPER_FILTER has a projection list, we don't actually do any 16 | * projection: the planner ensures that the associated projection list 17 | * just preserves the input to the filter. 18 | */ 19 | if (eval_qual_set(filter_op->nquals, filter_op->qual_ary)) 20 | op->next->invoke(op->next, t); 21 | } 22 | 23 | FilterOperator * 24 | filter_op_make(FilterPlan *plan, Operator *next_op, OpChain *chain) 25 | { 26 | FilterOperator *filter_op; 27 | ListCell *lc; 28 | int i; 29 | 30 | filter_op = (FilterOperator *) operator_make(OPER_FILTER, 31 | sizeof(*filter_op), 32 | (PlanNode *) plan, 33 | next_op, 34 | chain, 35 | filter_invoke); 36 | 37 | filter_op->nquals = list_length(filter_op->op.plan->quals); 38 | filter_op->qual_ary = apr_palloc(filter_op->op.pool, 39 | sizeof(*filter_op->qual_ary) * filter_op->nquals); 40 | i = 0; 41 | foreach (lc, filter_op->op.plan->qual_exprs) 42 | { 43 | ExprNode *expr = (ExprNode *) lc_ptr(lc); 44 | 45 | filter_op->qual_ary[i++] = make_expr_state(expr, 46 | filter_op->op.exec_cxt, 47 | filter_op->op.pool); 48 | } 49 | 50 | return filter_op; 51 | } 52 | -------------------------------------------------------------------------------- /src/libc4/operator/insert.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "operator/insert.h" 3 | #include "router.h" 4 | 5 | static void 6 | insert_invoke(Operator *op, Tuple *t) 7 | { 8 | C4Runtime *c4 = op->chain->c4; 9 | InsertOperator *insert_op = (InsertOperator *) op; 10 | 11 | if (router_is_deleting(c4->router)) 12 | router_delete_tuple(c4->router, t, insert_op->tbl_def); 13 | else 14 | router_insert_tuple(c4->router, t, insert_op->tbl_def, true); 15 | } 16 | 17 | InsertOperator * 18 | insert_op_make(InsertPlan *plan, OpChain *chain) 19 | { 20 | InsertOperator *insert_op; 21 | 22 | ASSERT(list_length(plan->plan.quals) == 0); 23 | 24 | insert_op = (InsertOperator *) operator_make(OPER_INSERT, 25 | sizeof(*insert_op), 26 | (PlanNode *) plan, 27 | NULL, 28 | chain, 29 | insert_invoke); 30 | 31 | insert_op->tbl_def = cat_get_table(chain->c4->cat, plan->head->name); 32 | 33 | return insert_op; 34 | } 35 | -------------------------------------------------------------------------------- /src/libc4/operator/operator.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "nodes/copyfuncs.h" 3 | #include "operator/operator.h" 4 | 5 | Operator * 6 | operator_make(C4NodeKind kind, apr_size_t sz, PlanNode *plan, 7 | Operator *next_op, OpChain *chain, 8 | op_invoke_func invoke_f) 9 | { 10 | apr_pool_t *pool = chain->pool; 11 | Operator *op; 12 | ListCell *lc; 13 | int i; 14 | 15 | op = apr_pcalloc(pool, sz); 16 | op->node.kind = kind; 17 | op->pool = pool; 18 | op->plan = copy_node(plan, pool); 19 | op->next = next_op; 20 | op->chain = chain; 21 | op->exec_cxt = apr_pcalloc(pool, sizeof(*op->exec_cxt)); 22 | op->invoke = invoke_f; 23 | 24 | op->nproj = list_length(op->plan->proj_list); 25 | op->proj_ary = apr_palloc(pool, sizeof(ExprState *) * op->nproj); 26 | 27 | i = 0; 28 | foreach (lc, op->plan->proj_list) 29 | { 30 | ExprNode *expr = (ExprNode *) lc_ptr(lc); 31 | 32 | op->proj_ary[i++] = make_expr_state(expr, op->exec_cxt, pool); 33 | } 34 | 35 | op->proj_schema = schema_make_from_exprs(op->nproj, op->proj_ary, 36 | chain->c4, pool); 37 | 38 | return op; 39 | } 40 | 41 | Tuple * 42 | operator_do_project(Operator *op) 43 | { 44 | Tuple *proj_tuple; 45 | int i; 46 | 47 | proj_tuple = tuple_make_empty(op->proj_schema); 48 | for (i = 0; i < op->nproj; i++) 49 | { 50 | ExprState *proj_state = op->proj_ary[i]; 51 | Datum result; 52 | 53 | result = eval_expr(proj_state); 54 | proj_tuple->vals[i] = datum_copy(result, proj_state->expr->type); 55 | } 56 | 57 | return proj_tuple; 58 | } 59 | 60 | OpChainList * 61 | opchain_list_make(apr_pool_t *pool) 62 | { 63 | OpChainList *result; 64 | 65 | result = apr_palloc(pool, sizeof(*result)); 66 | result->length = 0; 67 | result->head = NULL; 68 | 69 | return result; 70 | } 71 | 72 | void 73 | opchain_list_add(OpChainList *list, OpChain *op_chain) 74 | { 75 | ASSERT(op_chain->next == NULL); 76 | 77 | op_chain->next = list->head; 78 | list->head = op_chain; 79 | list->length++; 80 | } 81 | -------------------------------------------------------------------------------- /src/libc4/operator/project.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "operator/project.h" 3 | 4 | static void 5 | project_invoke(Operator *op, Tuple *t) 6 | { 7 | ExprEvalContext *exec_cxt; 8 | Tuple *proj_tuple; 9 | 10 | exec_cxt = op->exec_cxt; 11 | exec_cxt->inner = t; 12 | 13 | proj_tuple = operator_do_project(op); 14 | op->next->invoke(op->next, proj_tuple); 15 | tuple_unpin(proj_tuple, op->proj_schema); 16 | } 17 | 18 | ProjectOperator * 19 | project_op_make(ProjectPlan *plan, Operator *next_op, OpChain *chain) 20 | { 21 | ProjectOperator *proj_op; 22 | 23 | ASSERT(list_length(plan->plan.quals) == 0); 24 | 25 | proj_op = (ProjectOperator *) operator_make(OPER_PROJECT, 26 | sizeof(*proj_op), 27 | (PlanNode *) plan, 28 | next_op, 29 | chain, 30 | project_invoke); 31 | 32 | return proj_op; 33 | } 34 | -------------------------------------------------------------------------------- /src/libc4/operator/scan.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "operator/scan.h" 3 | #include "operator/scancursor.h" 4 | 5 | static void 6 | scan_invoke(Operator *op, Tuple *t) 7 | { 8 | ScanOperator *scan_op = (ScanOperator *) op; 9 | AbstractTable *tbl = scan_op->table; 10 | ExprEvalContext *exec_cxt; 11 | Tuple *scan_tuple; 12 | 13 | exec_cxt = scan_op->op.exec_cxt; 14 | exec_cxt->inner = t; 15 | 16 | tbl->scan_reset(tbl, scan_op->cursor); 17 | while ((scan_tuple = tbl->scan_next(tbl, scan_op->cursor)) != NULL) 18 | { 19 | exec_cxt->outer = scan_tuple; 20 | 21 | if (eval_qual_set(scan_op->nquals, scan_op->qual_ary)) 22 | { 23 | Tuple *join_tuple; 24 | 25 | /* If this is NOT and we see a matching tuple, we're done */ 26 | if (scan_op->anti_scan) 27 | return; 28 | 29 | join_tuple = operator_do_project(op); 30 | op->next->invoke(op->next, join_tuple); 31 | } 32 | } 33 | 34 | /* If this is NOT and no matches, emit an output tuple */ 35 | if (scan_op->anti_scan) 36 | { 37 | Tuple *join_tuple; 38 | 39 | exec_cxt->outer = NULL; 40 | join_tuple = operator_do_project(op); 41 | op->next->invoke(op->next, join_tuple); 42 | } 43 | } 44 | 45 | ScanOperator * 46 | scan_op_make(ScanPlan *plan, Operator *next_op, OpChain *chain) 47 | { 48 | ScanOperator *scan_op; 49 | char *tbl_name; 50 | ListCell *lc; 51 | int i; 52 | 53 | scan_op = (ScanOperator *) operator_make(OPER_SCAN, 54 | sizeof(*scan_op), 55 | (PlanNode *) plan, 56 | next_op, 57 | chain, 58 | scan_invoke); 59 | 60 | tbl_name = plan->scan_rel->ref->name; 61 | scan_op->table = cat_get_table_impl(chain->c4->cat, tbl_name); 62 | scan_op->cursor = scan_op->table->scan_make(scan_op->table, scan_op->op.pool); 63 | scan_op->anti_scan = plan->scan_rel->not; 64 | 65 | scan_op->nquals = list_length(scan_op->op.plan->quals); 66 | scan_op->qual_ary = apr_palloc(scan_op->op.pool, 67 | sizeof(*scan_op->qual_ary) * scan_op->nquals); 68 | i = 0; 69 | foreach (lc, scan_op->op.plan->qual_exprs) 70 | { 71 | ExprNode *expr = (ExprNode *) lc_ptr(lc); 72 | 73 | scan_op->qual_ary[i++] = make_expr_state(expr, 74 | scan_op->op.exec_cxt, 75 | scan_op->op.pool); 76 | } 77 | 78 | return scan_op; 79 | } 80 | -------------------------------------------------------------------------------- /src/libc4/parser/ol_scan.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | 4 | #include "c4-internal.h" 5 | #include "parser/parser-internal.h" 6 | 7 | #include "ol_parse.h" 8 | 9 | #define SCANNER_POOL yyget_extra(yyscanner)->pool 10 | 11 | static unsigned char unescape_char(unsigned char c); 12 | %} 13 | 14 | %option 8bit 15 | %option never-interactive batch 16 | %option fast 17 | %option nounput 18 | %option noyywrap 19 | %option reentrant 20 | %option bison-bridge 21 | %option extra-type="C4Parser *" 22 | 23 | digit [0-9] 24 | space [ \t\n\r\f] 25 | newline [\n\r] 26 | non_newline [^\n\r] 27 | 28 | /* "self" is the set of characters that are returned to the parser as-is. */ 29 | self [,()\[\].;\:\+\-\*\/\%\^\=@\{\}\<\>] 30 | 31 | comment ("//"{non_newline}*) 32 | whitespace ({space}+|{comment}) 33 | 34 | /* 35 | * Note that we distinguish between "table identifiers" and "variable 36 | * identifiers"; the former must be entirely in lower-case, whereas the 37 | * latter must begin with an upperchase letter. This is partly to simplify 38 | * parsing, but arguably it enforces good naming discipline. 39 | * 40 | * XXX: No support for Unicode identifiers 41 | */ 42 | vident_start [A-Z_] 43 | vident_cont [A-Za-z_0-9] 44 | var_ident {vident_start}{vident_cont}* 45 | 46 | tident_start [a-z] 47 | tident_cont [a-z_0-9] 48 | tbl_ident {tident_start}{tident_cont}* 49 | 50 | integer {digit}+ 51 | decimal {digit}+\.{digit}* 52 | real ({integer}|{decimal})([Ee][-+]?{digit}+)? 53 | 54 | %x comment 55 | %x string 56 | 57 | %% 58 | "/*" { BEGIN(comment); } 59 | [^*]* /* eat anything that's not a '*' */ 60 | "*"+[^*/]* /* eat up '*'s not followed by '/'s */ 61 | "*"+"/" { BEGIN(INITIAL); } 62 | 63 | \" { 64 | C4Parser *context = yyget_extra(yyscanner); 65 | sbuf_reset(context->lit_buf); 66 | BEGIN(string); 67 | } 68 | 69 | \\. { 70 | C4Parser *context = yyget_extra(yyscanner); 71 | sbuf_append_char(context->lit_buf, unescape_char((unsigned char) yytext[1])); 72 | } 73 | 74 | [^\\\"]+ { 75 | C4Parser *context = yyget_extra(yyscanner); 76 | sbuf_append_data(context->lit_buf, yytext, yyleng); 77 | } 78 | 79 | \" { 80 | C4Parser *context = yyget_extra(yyscanner); 81 | yylval->str = sbuf_dup(context->lit_buf, SCANNER_POOL); 82 | BEGIN(INITIAL); 83 | return SCONST; 84 | } 85 | 86 | \'[^\\\']\' { 87 | yylval->str = apr_pstrmemdup(SCANNER_POOL, &yytext[1], 1); 88 | return CCONST; 89 | } 90 | 91 | \'\\.\' { 92 | char c = unescape_char((unsigned char) yytext[2]); 93 | yylval->str = apr_pstrmemdup(SCANNER_POOL, &c, 1); 94 | return CCONST; 95 | } 96 | 97 | {whitespace} /* ignore */ 98 | <> { yyterminate(); } 99 | 100 | {self} { return yytext[0]; } 101 | 102 | "<=" { return OL_LTE; } 103 | ">=" { return OL_GTE; } 104 | "!=" { return OL_NEQ; } 105 | "<>" { return OL_NEQ; } 106 | "==" { return OL_EQ; } 107 | 108 | "avg" { return OL_AVG; } 109 | "count" { return OL_COUNT; } 110 | "max" { return OL_MAX; } 111 | "min" { return OL_MIN; } 112 | "sum" { return OL_SUM; } 113 | 114 | "define" { return DEFINE; } 115 | "delete" { return DELETE; } 116 | "false" { return OL_FALSE; } 117 | "memory" { return MEMORY; } 118 | "notin" { return NOTIN; } 119 | "sqlite" { return SQLITE; } 120 | "timer" { return TIMER; } 121 | "true" { return OL_TRUE; } 122 | 123 | {integer} { 124 | yylval->str = apr_pstrmemdup(SCANNER_POOL, yytext, yyleng); 125 | return ICONST; 126 | } 127 | 128 | {real} { 129 | yylval->str = apr_pstrmemdup(SCANNER_POOL, yytext, yyleng); 130 | return FCONST; 131 | } 132 | 133 | {var_ident} { 134 | yylval->str = apr_pstrmemdup(SCANNER_POOL, yytext, yyleng); 135 | return VAR_IDENT; 136 | } 137 | 138 | {tbl_ident} { 139 | yylval->str = apr_pstrmemdup(SCANNER_POOL, yytext, yyleng); 140 | return TBL_IDENT; 141 | } 142 | 143 | %% 144 | 145 | /* 146 | * Setup an input buffer for Flex to scan. This is defined here mostly so 147 | * that we can use the YY_END_OF_BUFFER_CHAR #define, which Flex annoyingly 148 | * doesn't export. Return value is apr_palloc'd in the given pool. 149 | */ 150 | char * 151 | setup_scan_buf(const char *str, apr_size_t len, apr_pool_t *pool) 152 | { 153 | char *scan_buf; 154 | 155 | scan_buf = apr_palloc(pool, len + 2); 156 | memcpy(scan_buf, str, len); 157 | scan_buf[len] = scan_buf[len + 1] = YY_END_OF_BUFFER_CHAR; 158 | 159 | return scan_buf; 160 | } 161 | 162 | static unsigned char 163 | unescape_char(unsigned char c) 164 | { 165 | switch (c) 166 | { 167 | case 'b': 168 | return '\b'; 169 | case 'f': 170 | return '\f'; 171 | case 'n': 172 | return '\n'; 173 | case 'r': 174 | return '\r'; 175 | case 't': 176 | return '\t'; 177 | default: 178 | return c; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/libc4/parser/parser.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | 3 | #include "nodes/copyfuncs.h" 4 | #include "parser/analyze.h" 5 | /* 6 | * XXX: we need to include the definition of C4Parser before we can include 7 | * ol_scan.h, because Flex is broken. 8 | */ 9 | #include "parser/parser-internal.h" 10 | #include "ol_parse.h" 11 | #include "ol_scan.h" 12 | 13 | static C4Parser * 14 | parser_make(apr_pool_t *pool) 15 | { 16 | apr_pool_t *parse_pool; 17 | C4Parser *parser; 18 | 19 | parse_pool = make_subpool(pool); 20 | parser = apr_pcalloc(parse_pool, sizeof(*parser)); 21 | parser->pool = parse_pool; 22 | parser->lit_buf = sbuf_make(parser->pool); 23 | return parser; 24 | } 25 | 26 | static AstProgram * 27 | do_parse(C4Parser *parser, const char *str) 28 | { 29 | apr_size_t slen; 30 | char *scan_buf; 31 | YY_BUFFER_STATE buf_state; 32 | int parse_result; 33 | 34 | yylex_init_extra(parser, &parser->yyscanner); 35 | 36 | slen = strlen(str); 37 | scan_buf = setup_scan_buf(str, slen, parser->pool); 38 | buf_state = yy_scan_buffer(scan_buf, slen + 2, parser->yyscanner); 39 | 40 | parse_result = yyparse(parser, parser->yyscanner); 41 | yy_delete_buffer(buf_state, parser->yyscanner); 42 | yylex_destroy(parser->yyscanner); 43 | 44 | if (parse_result) 45 | ERROR("Parsing failed"); 46 | 47 | return parser->result; 48 | } 49 | 50 | static void 51 | parser_destroy(C4Parser *parser) 52 | { 53 | apr_pool_destroy(parser->pool); 54 | } 55 | 56 | AstProgram * 57 | parse_str(const char *str, apr_pool_t *pool, C4Runtime *c4) 58 | { 59 | C4Parser *parser; 60 | AstProgram *ast; 61 | 62 | parser = parser_make(pool); 63 | ast = do_parse(parser, str); 64 | analyze_ast(ast, parser->pool, c4); 65 | /* Copy the finished AST to the caller's pool */ 66 | ast = copy_node(ast, pool); 67 | parser_destroy(parser); 68 | 69 | return ast; 70 | } 71 | -------------------------------------------------------------------------------- /src/libc4/parser/walker.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "parser/walker.h" 3 | 4 | bool 5 | expr_tree_walker(C4Node *node, expr_callback fn, void *data) 6 | { 7 | if (!node) 8 | return true; 9 | 10 | switch (node->kind) 11 | { 12 | case AST_OP_EXPR: 13 | { 14 | AstOpExpr *op_expr = (AstOpExpr *) node; 15 | 16 | if (expr_tree_walker(op_expr->lhs, fn, data) == false) 17 | return false; 18 | 19 | if (expr_tree_walker(op_expr->rhs, fn, data) == false) 20 | return false; 21 | } 22 | break; 23 | 24 | case AST_VAR_EXPR: 25 | case AST_CONST_EXPR: 26 | break; 27 | 28 | case AST_AGG_EXPR: 29 | { 30 | AstAggExpr *agg_expr = (AstAggExpr *) node; 31 | 32 | if (expr_tree_walker(agg_expr->expr, fn, data) == false) 33 | return false; 34 | } 35 | break; 36 | 37 | case AST_QUALIFIER: 38 | { 39 | AstQualifier *ast_qual = (AstQualifier *) node; 40 | 41 | if (expr_tree_walker(ast_qual->expr, fn, data) == false) 42 | return false; 43 | } 44 | break; 45 | 46 | case AST_TABLE_REF: 47 | { 48 | AstTableRef *tbl_ref = (AstTableRef *) node; 49 | ListCell *lc; 50 | 51 | foreach (lc, tbl_ref->cols) 52 | { 53 | C4Node *col_expr = (C4Node *) lc_ptr(lc); 54 | 55 | if (expr_tree_walker(col_expr, fn, data) == false) 56 | return false; 57 | } 58 | } 59 | break; 60 | 61 | default: 62 | ERROR("Unrecognized expression node kind: %d", 63 | (int) node->kind); 64 | } 65 | 66 | return fn(node, data); 67 | } 68 | 69 | typedef struct VarWalkerContext 70 | { 71 | var_expr_callback fn; 72 | void *user_data; 73 | } VarWalkerContext; 74 | 75 | static bool 76 | var_walker_callback(C4Node *node, void *data) 77 | { 78 | VarWalkerContext *cxt = (VarWalkerContext *) data; 79 | 80 | if (node->kind == AST_VAR_EXPR) 81 | return cxt->fn((AstVarExpr *) node, cxt->user_data); 82 | 83 | return true; 84 | } 85 | 86 | bool 87 | expr_tree_var_walker(C4Node *node, var_expr_callback fn, void *data) 88 | { 89 | VarWalkerContext cxt; 90 | 91 | cxt.fn = fn; 92 | cxt.user_data = data; 93 | 94 | return expr_tree_walker(node, var_walker_callback, &cxt); 95 | } 96 | -------------------------------------------------------------------------------- /src/libc4/runtime.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "net/network.h" 3 | #include "router.h" 4 | #include "runtime.h" 5 | #include "storage/sqlite.h" 6 | #include "timer.h" 7 | #include "types/catalog.h" 8 | 9 | static void * APR_THREAD_FUNC runtime_thread_main(apr_thread_t *thread, 10 | void *data); 11 | 12 | static Datum 13 | get_local_addr(int port, apr_pool_t *pool) 14 | { 15 | char buf[APRMAXHOSTLEN + 1]; 16 | char addr[APRMAXHOSTLEN + 1 + 20]; 17 | apr_status_t s; 18 | 19 | s = apr_gethostname(buf, sizeof(buf), pool); 20 | if (s != APR_SUCCESS) 21 | FAIL_APR(s); 22 | 23 | snprintf(addr, sizeof(addr), "tcp:%s:%d", buf, port); 24 | printf("Local address = %s\n", addr); 25 | return string_from_str(addr); 26 | } 27 | 28 | static char * 29 | get_user_home_dir(apr_pool_t *pool) 30 | { 31 | apr_status_t s; 32 | apr_uid_t uid; 33 | apr_gid_t gid; 34 | char *user_name; 35 | char *home_dir; 36 | 37 | s = apr_uid_current(&uid, &gid, pool); 38 | if (s != APR_SUCCESS) 39 | FAIL_APR(s); 40 | 41 | s = apr_uid_name_get(&user_name, uid, pool); 42 | if (s != APR_SUCCESS) 43 | FAIL_APR(s); 44 | 45 | s = apr_uid_homepath_get(&home_dir, user_name, pool); 46 | if (s != APR_SUCCESS) 47 | FAIL_APR(s); 48 | 49 | return home_dir; 50 | } 51 | 52 | static char * 53 | get_c4_base_dir(int port, apr_pool_t *pool, apr_pool_t *tmp_pool) 54 | { 55 | char *home_dir; 56 | char *base_dir; 57 | apr_status_t s; 58 | 59 | home_dir = get_user_home_dir(tmp_pool); 60 | base_dir = apr_psprintf(pool, "%s/c4_home/tcp_%d", 61 | home_dir, port); 62 | s = apr_dir_make_recursive(base_dir, APR_FPROT_OS_DEFAULT, tmp_pool); 63 | if (s != APR_SUCCESS) 64 | FAIL_APR(s); 65 | 66 | printf("c4: Using base_dir = %s\n", base_dir); 67 | return base_dir; 68 | } 69 | 70 | static C4Runtime * 71 | c4_runtime_make(int port) 72 | { 73 | apr_status_t s; 74 | apr_pool_t *pool; 75 | C4Runtime *c4; 76 | 77 | s = apr_pool_create(&pool, NULL); 78 | if (s != APR_SUCCESS) 79 | FAIL_APR(s); 80 | 81 | c4 = apr_pcalloc(pool, sizeof(*c4)); 82 | c4->pool = pool; 83 | c4->tmp_pool = make_subpool(c4->pool); 84 | c4->log = logger_make(c4); 85 | c4->cat = cat_make(c4); 86 | c4->net = network_make(c4, port); 87 | c4->router = router_make(c4); 88 | c4->sql = sqlite_init(c4); 89 | c4->timer = timer_make(c4); 90 | c4->tpool_mgr = tpool_mgr_make(c4->pool); 91 | c4->port = network_get_port(c4->net); 92 | c4->local_addr = get_local_addr(c4->port, c4->tmp_pool); 93 | c4->base_dir = get_c4_base_dir(c4->port, c4->pool, c4->tmp_pool); 94 | 95 | return c4; 96 | } 97 | 98 | /* 99 | * Data that needs to be exchanged with a new C4 runtime thread during startup. 100 | * Annoyingly, we need to bundle this into a struct, because we're only allowed 101 | * to pass a single pointer to a new thread. 102 | */ 103 | typedef struct RuntimeInitData 104 | { 105 | /* Input data */ 106 | int port; 107 | C4ThreadSync *thread_sync; 108 | 109 | /* Output data */ 110 | C4Runtime *runtime; 111 | } RuntimeInitData; 112 | 113 | /* 114 | * Invoked by the client API (in the client's thread) to create a new C4 runtime 115 | * as a separate thread. Note that we only allocate the thread itself in the 116 | * client-provided pool; the runtime itself uses a distinct top-level APR pool. 117 | */ 118 | C4Runtime * 119 | c4_runtime_start(int port, C4ThreadSync *thread_sync, 120 | apr_pool_t *pool, apr_thread_t **thread) 121 | { 122 | RuntimeInitData *init_data; 123 | apr_status_t s; 124 | apr_threadattr_t *thread_attr; 125 | C4Runtime *runtime; 126 | 127 | init_data = ol_alloc0(sizeof(*init_data)); 128 | init_data->port = port; 129 | init_data->thread_sync = thread_sync; 130 | 131 | s = apr_threadattr_create(&thread_attr, pool); 132 | if (s != APR_SUCCESS) 133 | FAIL_APR(s); 134 | 135 | s = apr_thread_create(thread, thread_attr, runtime_thread_main, 136 | init_data, pool); 137 | if (s != APR_SUCCESS) 138 | FAIL_APR(s); 139 | 140 | thread_sync_wait(init_data->thread_sync); 141 | 142 | runtime = init_data->runtime; 143 | ol_free(init_data); 144 | return runtime; 145 | } 146 | 147 | static void * APR_THREAD_FUNC 148 | runtime_thread_main(apr_thread_t *thread, void *data) 149 | { 150 | RuntimeInitData *init_data = (RuntimeInitData *) data; 151 | C4Runtime *c4; 152 | 153 | c4 = c4_runtime_make(init_data->port); 154 | 155 | /* Signal client that startup has completed */ 156 | init_data->runtime = c4; 157 | thread_sync_signal(init_data->thread_sync); 158 | 159 | router_main_loop(c4->router); 160 | 161 | /* Client initiated an orderly shutdown */ 162 | apr_pool_destroy(c4->pool); 163 | apr_thread_exit(thread, APR_SUCCESS); 164 | 165 | return NULL; /* Return value ignored */ 166 | } 167 | -------------------------------------------------------------------------------- /src/libc4/storage/mem_table.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "operator/scancursor.h" 3 | #include "storage/mem_table.h" 4 | 5 | /* 6 | * Unpin the tuples contained in this table. 7 | */ 8 | static void 9 | mem_table_cleanup(AbstractTable *a_tbl) 10 | { 11 | MemTable *tbl = (MemTable *) a_tbl; 12 | rset_index_t *ri; 13 | 14 | ri = rset_iter_make(a_tbl->pool, tbl->tuples); 15 | while (rset_iter_next(ri)) 16 | { 17 | Tuple *t; 18 | 19 | t = rset_this(ri); 20 | tuple_unpin(t, a_tbl->def->schema); 21 | } 22 | } 23 | 24 | static bool 25 | mem_table_insert(AbstractTable *a_tbl, Tuple *t) 26 | { 27 | MemTable *tbl = (MemTable *) a_tbl; 28 | bool is_new; 29 | 30 | is_new = rset_add(tbl->tuples, t); 31 | if (is_new) 32 | tuple_pin(t); 33 | 34 | return is_new; 35 | } 36 | 37 | static bool 38 | mem_table_delete(AbstractTable *a_tbl, Tuple *t) 39 | { 40 | MemTable *tbl = (MemTable *) a_tbl; 41 | Tuple *old_t; 42 | unsigned int new_count; 43 | 44 | old_t = rset_remove(tbl->tuples, t, &new_count); 45 | if (old_t != NULL && new_count == 0) 46 | { 47 | tuple_unpin(old_t, a_tbl->def->schema); 48 | return true; 49 | } 50 | 51 | /* Either not found or updated ref count > 0 */ 52 | return false; 53 | } 54 | 55 | static ScanCursor * 56 | mem_table_scan_make(AbstractTable *a_tbl, apr_pool_t *pool) 57 | { 58 | MemTable *tbl = (MemTable *) a_tbl; 59 | ScanCursor *scan; 60 | 61 | scan = apr_pcalloc(pool, sizeof(*scan)); 62 | scan->pool = pool; 63 | scan->rset_iter = rset_iter_make(pool, tbl->tuples); 64 | 65 | return scan; 66 | } 67 | 68 | static void 69 | mem_table_scan_reset(AbstractTable *a_tbl, ScanCursor *scan) 70 | { 71 | rset_iter_reset(scan->rset_iter); 72 | } 73 | 74 | static Tuple * 75 | mem_table_scan_next(AbstractTable *a_tbl, ScanCursor *cur) 76 | { 77 | if (!rset_iter_next(cur->rset_iter)) 78 | return NULL; 79 | 80 | return rset_this(cur->rset_iter); 81 | } 82 | 83 | MemTable * 84 | mem_table_make(TableDef *def, C4Runtime *c4, apr_pool_t *pool) 85 | { 86 | MemTable *tbl; 87 | 88 | tbl = (MemTable *) table_make_super(sizeof(*tbl), def, c4, 89 | mem_table_insert, 90 | mem_table_delete, 91 | mem_table_cleanup, 92 | mem_table_scan_make, 93 | mem_table_scan_reset, 94 | mem_table_scan_next, 95 | pool); 96 | tbl->tuples = rset_make(pool, def->schema, 97 | tuple_hash_tbl, tuple_cmp_tbl); 98 | 99 | return tbl; 100 | } 101 | -------------------------------------------------------------------------------- /src/libc4/storage/sqlite.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "storage/sqlite.h" 3 | 4 | static apr_status_t sqlite_cleanup(void *data); 5 | static apr_status_t sqlite_pstmt_cleanup(void *data); 6 | 7 | SQLiteState * 8 | sqlite_init(C4Runtime *c4) 9 | { 10 | SQLiteState *sql; 11 | char *db_fname; 12 | int res; 13 | 14 | sql = apr_palloc(c4->pool, sizeof(*sql)); 15 | sql->c4 = c4; 16 | sql->xact_in_progress = false; 17 | 18 | db_fname = apr_pstrcat(c4->tmp_pool, c4->base_dir, "/", "sqlite.db", NULL); 19 | if ((res = sqlite3_open(db_fname, &sql->db)) != 0) 20 | ERROR("sqlite3_open failed: %d", res); 21 | 22 | apr_pool_cleanup_register(c4->pool, sql, sqlite_cleanup, 23 | apr_pool_cleanup_null); 24 | 25 | return sql; 26 | } 27 | 28 | static apr_status_t 29 | sqlite_cleanup(void *data) 30 | { 31 | SQLiteState *sql = (SQLiteState *) data; 32 | 33 | if (sql->xact_in_progress) 34 | FAIL(); 35 | 36 | (void) sqlite3_close(sql->db); 37 | 38 | return APR_SUCCESS; 39 | } 40 | 41 | void 42 | sqlite_begin_xact(SQLiteState *sql) 43 | { 44 | if (sql->xact_in_progress) 45 | FAIL(); 46 | 47 | sqlite_exec_sql(sql, "BEGIN;"); 48 | sql->xact_in_progress = true; 49 | } 50 | 51 | void 52 | sqlite_commit_xact(SQLiteState *sql) 53 | { 54 | if (!sql->xact_in_progress) 55 | FAIL(); 56 | 57 | sqlite_exec_sql(sql, "COMMIT;"); 58 | sql->xact_in_progress = false; 59 | } 60 | 61 | void 62 | sqlite_exec_sql(SQLiteState *sql, const char *stmt) 63 | { 64 | if (sqlite3_exec(sql->db, stmt, NULL, NULL, NULL) != SQLITE_OK) 65 | FAIL_SQLITE(sql->c4); 66 | } 67 | 68 | /* 69 | * Create a new SQLite3 prepared statement, and register a cleanup 70 | * function in the given pool. stmt_len can be -1 to use strlen(stmt). 71 | */ 72 | sqlite3_stmt * 73 | sqlite_pstmt_make(SQLiteState *sql, const char *stmt, int stmt_len, 74 | apr_pool_t *pool) 75 | { 76 | sqlite3_stmt *pstmt; 77 | 78 | if (sqlite3_prepare_v2(sql->db, stmt, stmt_len, &pstmt, NULL) != SQLITE_OK) 79 | FAIL_SQLITE(sql->c4); 80 | 81 | apr_pool_cleanup_register(pool, pstmt, sqlite_pstmt_cleanup, 82 | apr_pool_cleanup_null); 83 | 84 | return pstmt; 85 | } 86 | 87 | static apr_status_t 88 | sqlite_pstmt_cleanup(void *data) 89 | { 90 | sqlite3_stmt *stmt = (sqlite3_stmt *) data; 91 | 92 | if (sqlite3_finalize(stmt) != SQLITE_OK) 93 | FAIL(); /* XXX: better error recovery */ 94 | 95 | return APR_SUCCESS; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /src/libc4/storage/table.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "storage/mem_table.h" 3 | #include "storage/sqlite_table.h" 4 | #include "storage/table.h" 5 | #include "types/tuple.h" 6 | 7 | static apr_status_t abstract_table_cleanup(void *data); 8 | 9 | AbstractTable * 10 | table_make(TableDef *def, C4Runtime *c4, apr_pool_t *pool) 11 | { 12 | AbstractTable *tbl; 13 | 14 | switch (def->storage) 15 | { 16 | case AST_STORAGE_MEMORY: 17 | tbl = (AbstractTable *) mem_table_make(def, c4, pool); 18 | break; 19 | 20 | case AST_STORAGE_SQLITE: 21 | tbl = (AbstractTable *) sqlite_table_make(def, c4, pool); 22 | break; 23 | 24 | default: 25 | ERROR("Unrecognized storage kind: %d", (int) def->storage); 26 | } 27 | 28 | return tbl; 29 | } 30 | 31 | AbstractTable * 32 | table_make_super(apr_size_t sz, TableDef *def, C4Runtime *c4, 33 | table_insert_f insert_f, 34 | table_delete_f delete_f, 35 | table_cleanup_f cleanup_f, 36 | table_scan_make_f scan_make_f, 37 | table_scan_reset_f scan_reset_f, 38 | table_scan_next_f scan_next_f, 39 | apr_pool_t *pool) 40 | { 41 | AbstractTable *tbl; 42 | 43 | tbl = apr_pcalloc(pool, sz); 44 | tbl->pool = pool; 45 | tbl->c4 = c4; 46 | tbl->def = def; 47 | tbl->insert = insert_f; 48 | tbl->delete = delete_f; 49 | tbl->cleanup = cleanup_f; 50 | tbl->scan_make = scan_make_f; 51 | tbl->scan_reset = scan_reset_f; 52 | tbl->scan_next = scan_next_f; 53 | 54 | apr_pool_cleanup_register(pool, tbl, abstract_table_cleanup, 55 | apr_pool_cleanup_null); 56 | 57 | /* Caller initializes subtype-specific fields */ 58 | return tbl; 59 | } 60 | 61 | static apr_status_t 62 | abstract_table_cleanup(void *data) 63 | { 64 | AbstractTable *tbl = (AbstractTable *) data; 65 | 66 | tbl->cleanup(tbl); 67 | return APR_SUCCESS; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/libc4/timer.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "router.h" 3 | #include "timer.h" 4 | 5 | typedef struct AlarmState 6 | { 7 | apr_interval_time_t period; 8 | apr_interval_time_t deadline; 9 | TableDef *tbl_def; 10 | struct AlarmState *next; 11 | } AlarmState; 12 | 13 | struct C4Timer 14 | { 15 | apr_pool_t *pool; 16 | C4Runtime *c4; 17 | AlarmState *alarm; 18 | }; 19 | 20 | C4Timer * 21 | timer_make(C4Runtime *c4) 22 | { 23 | C4Timer *timer; 24 | 25 | timer = apr_palloc(c4->pool, sizeof(*timer)); 26 | timer->pool = c4->pool; 27 | timer->c4 = c4; 28 | timer->alarm = NULL; 29 | return timer; 30 | } 31 | 32 | static apr_time_t 33 | get_deadline(apr_time_t now, apr_interval_time_t delta) 34 | { 35 | if (APR_INT64_MAX - now < delta) 36 | ERROR("Integer overflow: %" APR_TIME_T_FMT " + %" APR_TIME_T_FMT, 37 | now, delta); 38 | 39 | return now + delta; 40 | } 41 | 42 | void 43 | timer_add_alarm(C4Timer *timer, const char *name, apr_int64_t period_msec) 44 | { 45 | AlarmState *alarm; 46 | 47 | alarm = apr_palloc(timer->pool, sizeof(*alarm)); 48 | alarm->period = period_msec * 1000; 49 | alarm->deadline = get_deadline(apr_time_now(), alarm->period); 50 | alarm->tbl_def = cat_get_table(timer->c4->cat, name); 51 | alarm->next = timer->alarm; 52 | timer->alarm = alarm; 53 | } 54 | 55 | /* 56 | * Return the approximate time before the next alarm will fire, or -1 if there 57 | * are no active alarms. 58 | */ 59 | apr_interval_time_t 60 | timer_get_sleep_time(C4Timer *timer) 61 | { 62 | apr_time_t min_deadline; 63 | apr_time_t now; 64 | AlarmState *alarm; 65 | 66 | if (timer->alarm == NULL) 67 | return -1; 68 | 69 | min_deadline = APR_INT64_MAX; 70 | now = apr_time_now(); 71 | alarm = timer->alarm; 72 | while (alarm != NULL) 73 | { 74 | /* If we should have fired the alarm already, don't sleep */ 75 | if (alarm->deadline <= now) 76 | return 0; 77 | 78 | if (alarm->deadline < min_deadline) 79 | min_deadline = alarm->deadline; 80 | 81 | alarm = alarm->next; 82 | } 83 | 84 | ASSERT(min_deadline > now); 85 | ASSERT(min_deadline < APR_INT64_MAX); 86 | return min_deadline - now; 87 | } 88 | 89 | static void 90 | fire_alarm(AlarmState *alarm, C4Timer *timer) 91 | { 92 | Datum time_val; 93 | Tuple *alarm_tuple; 94 | 95 | time_val.i8 = alarm->deadline; 96 | alarm_tuple = tuple_make(alarm->tbl_def->schema, &time_val); 97 | router_insert_tuple(timer->c4->router, alarm_tuple, 98 | alarm->tbl_def, false); 99 | tuple_unpin(alarm_tuple, alarm->tbl_def->schema); 100 | 101 | alarm->deadline += alarm->period; 102 | } 103 | 104 | bool 105 | timer_poll(C4Timer *timer) 106 | { 107 | apr_time_t now; 108 | AlarmState *alarm; 109 | bool fired_alarm; 110 | 111 | fired_alarm = false; 112 | now = apr_time_now(); 113 | alarm = timer->alarm; 114 | while (alarm != NULL) 115 | { 116 | /* NB: we might fire many times before advancing to the next alarm */ 117 | if (alarm->deadline <= now) 118 | { 119 | fire_alarm(alarm, timer); 120 | fired_alarm = true; 121 | } 122 | else 123 | alarm = alarm->next; 124 | } 125 | 126 | return fired_alarm; 127 | } 128 | -------------------------------------------------------------------------------- /src/libc4/types/agg_funcs.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "operator/agg.h" 3 | #include "types/agg_funcs.h" 4 | #include "util/rbtree.h" 5 | 6 | /* Average */ 7 | typedef struct AvgStateVal 8 | { 9 | apr_pool_t *pool; 10 | Datum sum; 11 | Datum count; 12 | } AvgStateVal; 13 | 14 | /* XXX: Currently assume that avg input and output is TYPE_DOUBLE */ 15 | static AggStateVal 16 | avg_init_f(Datum v, AggOperator *agg_op, __unused int aggno) 17 | { 18 | apr_pool_t *pool; 19 | AvgStateVal *avg_state; 20 | AggStateVal state; 21 | 22 | pool = make_subpool(agg_op->op.pool); 23 | avg_state = apr_palloc(pool, sizeof(*avg_state)); 24 | avg_state->pool = pool; 25 | avg_state->count.i8 = 1; 26 | avg_state->sum.d8 = v.d8; 27 | 28 | state.ptr = avg_state; 29 | return state; 30 | } 31 | 32 | static AggStateVal 33 | avg_fw_trans_f(AggStateVal state, Datum v) 34 | { 35 | AvgStateVal *avg_state = (AvgStateVal *) state.ptr; 36 | 37 | avg_state->count.i8++; 38 | avg_state->sum.d8 += v.d8; 39 | 40 | return state; 41 | } 42 | 43 | static AggStateVal 44 | avg_bw_trans_f(AggStateVal state, Datum v) 45 | { 46 | AvgStateVal *avg_state = (AvgStateVal *) state.ptr; 47 | 48 | avg_state->count.i8--; 49 | avg_state->sum.d8 -= v.d8; 50 | 51 | return state; 52 | } 53 | 54 | static Datum 55 | avg_output_f(AggStateVal state) 56 | { 57 | AvgStateVal *avg_state = (AvgStateVal *) state.ptr; 58 | Datum result; 59 | 60 | result.d8 = avg_state->sum.d8 / (double) avg_state->count.i8; 61 | return result; 62 | } 63 | 64 | static void 65 | avg_shutdown_f(AggStateVal state) 66 | { 67 | AvgStateVal *avg_state = (AvgStateVal *) state.ptr; 68 | 69 | apr_pool_destroy(avg_state->pool); 70 | } 71 | 72 | /* Count */ 73 | static AggStateVal 74 | count_init_f(__unused Datum v, __unused AggOperator *agg_op, __unused int aggno) 75 | { 76 | AggStateVal state; 77 | 78 | state.d.i8 = 1; 79 | return state; 80 | } 81 | 82 | static AggStateVal 83 | count_fw_trans_f(AggStateVal state, __unused Datum v) 84 | { 85 | state.d.i8++; 86 | return state; 87 | } 88 | 89 | static AggStateVal 90 | count_bw_trans_f(AggStateVal state, __unused Datum v) 91 | { 92 | state.d.i8--; 93 | return state; 94 | } 95 | 96 | /* 97 | * Min and Max. For both these aggregates, we keep the input we've seen so far 98 | * in a red-black tree. The only difference between min and max is that the 99 | * output func for min returns RB_MIN of the tree, while max does RB_MAX. Hence, 100 | * we call the common code "extrema". 101 | */ 102 | struct extrema_node 103 | { 104 | RB_ENTRY(extrema_node) entry; 105 | union 106 | { 107 | Datum val; /* Data value */ 108 | struct extrema_node *next_free; /* Next free node, if on free list */ 109 | } v; 110 | }; 111 | 112 | typedef struct ExtremaStateVal ExtremaStateVal; 113 | 114 | RB_HEAD(extrema_tree, extrema_node, ExtremaStateVal *); 115 | 116 | struct ExtremaStateVal 117 | { 118 | apr_pool_t *pool; 119 | datum_cmp_func cmp_func; 120 | struct extrema_tree tree; 121 | struct extrema_node *free_head; 122 | }; 123 | 124 | static int 125 | extrema_node_cmp(struct extrema_tree *tree, struct extrema_node *n1, 126 | struct extrema_node *n2) 127 | { 128 | ExtremaStateVal *extrema_state = (ExtremaStateVal *) tree->opaque; 129 | 130 | return extrema_state->cmp_func(n1->v.val, n2->v.val); 131 | } 132 | 133 | RB_GENERATE_STATIC(extrema_tree, extrema_node, entry, extrema_node_cmp) 134 | 135 | static void 136 | extrema_insert_val(ExtremaStateVal *state, Datum v) 137 | { 138 | struct extrema_node *n; 139 | 140 | if (state->free_head) 141 | { 142 | n = state->free_head; 143 | state->free_head = n->v.next_free; 144 | } 145 | else 146 | { 147 | n = apr_palloc(state->pool, sizeof(*n)); 148 | } 149 | 150 | n->v.val = v; 151 | RB_INSERT(extrema_tree, &state->tree, n); 152 | } 153 | 154 | static AggStateVal 155 | extrema_init_f(Datum v, AggOperator *agg_op, int aggno) 156 | { 157 | AggStateVal state; 158 | ExtremaStateVal *ext_state; 159 | apr_pool_t *pool; 160 | AggExprInfo *agg_info; 161 | int colno; 162 | DataType input_type; 163 | 164 | agg_info = agg_op->agg_info[aggno]; 165 | colno = agg_info->colno; 166 | input_type = schema_get_type(agg_op->op.proj_schema, colno); 167 | 168 | pool = make_subpool(agg_op->op.pool); 169 | ext_state = apr_palloc(pool, sizeof(*ext_state)); 170 | ext_state->pool = pool; 171 | ext_state->cmp_func = type_get_cmp_func(input_type); 172 | ext_state->free_head = NULL; 173 | RB_INIT(&ext_state->tree, ext_state); 174 | 175 | extrema_insert_val(ext_state, v); 176 | 177 | state.ptr = ext_state; 178 | return state; 179 | } 180 | 181 | static AggStateVal 182 | extrema_fw_trans_f(AggStateVal state, Datum v) 183 | { 184 | ExtremaStateVal *ext_state = (ExtremaStateVal *) state.ptr; 185 | 186 | extrema_insert_val(ext_state, v); 187 | return state; 188 | } 189 | 190 | static AggStateVal 191 | extrema_bw_trans_f(AggStateVal state, Datum v) 192 | { 193 | ExtremaStateVal *ext_state = (ExtremaStateVal *) state.ptr; 194 | struct extrema_node find; 195 | struct extrema_node *n; 196 | 197 | find.v.val = v; 198 | n = RB_FIND(extrema_tree, &ext_state->tree, &find); 199 | RB_REMOVE(extrema_tree, &ext_state->tree, n); 200 | 201 | n->v.next_free = ext_state->free_head; 202 | ext_state->free_head = n; 203 | 204 | return state; 205 | } 206 | 207 | static Datum 208 | max_output_f(AggStateVal state) 209 | { 210 | ExtremaStateVal *ext_state = (ExtremaStateVal *) state.ptr; 211 | struct extrema_node *n; 212 | 213 | n = RB_MAX(extrema_tree, &ext_state->tree); 214 | return n->v.val; 215 | } 216 | 217 | static Datum 218 | min_output_f(AggStateVal state) 219 | { 220 | ExtremaStateVal *ext_state = (ExtremaStateVal *) state.ptr; 221 | struct extrema_node *n; 222 | 223 | n = RB_MIN(extrema_tree, &ext_state->tree); 224 | return n->v.val; 225 | } 226 | 227 | static void 228 | extrema_shutdown_f(AggStateVal state) 229 | { 230 | ExtremaStateVal *ext_state = (ExtremaStateVal *) state.ptr; 231 | 232 | apr_pool_destroy(ext_state->pool); 233 | } 234 | 235 | /* Sum */ 236 | /* XXX: Currently assume that sum input and output is TYPE_INT */ 237 | static AggStateVal 238 | sum_fw_trans_f(AggStateVal state, Datum v) 239 | { 240 | state.d.i8 += v.i8; 241 | return state; 242 | } 243 | 244 | static AggStateVal 245 | sum_bw_trans_f(AggStateVal state, Datum v) 246 | { 247 | state.d.i8 -= v.i8; 248 | return state; 249 | } 250 | 251 | static AggFuncDesc agg_desc_avg = { 252 | avg_init_f, 253 | avg_fw_trans_f, 254 | avg_bw_trans_f, 255 | avg_output_f, 256 | avg_shutdown_f 257 | }; 258 | 259 | static AggFuncDesc agg_desc_count = { 260 | count_init_f, 261 | count_fw_trans_f, 262 | count_bw_trans_f, 263 | NULL, 264 | NULL 265 | }; 266 | 267 | static AggFuncDesc agg_desc_max = { 268 | extrema_init_f, 269 | extrema_fw_trans_f, 270 | extrema_bw_trans_f, 271 | max_output_f, 272 | extrema_shutdown_f 273 | }; 274 | 275 | static AggFuncDesc agg_desc_min = { 276 | extrema_init_f, 277 | extrema_fw_trans_f, 278 | extrema_bw_trans_f, 279 | min_output_f, 280 | extrema_shutdown_f 281 | }; 282 | 283 | static AggFuncDesc agg_desc_sum = { 284 | NULL, 285 | sum_fw_trans_f, 286 | sum_bw_trans_f, 287 | NULL, 288 | NULL 289 | }; 290 | 291 | AggFuncDesc * 292 | lookup_agg_desc(AstAggKind agg_kind) 293 | { 294 | switch (agg_kind) 295 | { 296 | case AST_AGG_AVG: 297 | return &agg_desc_avg; 298 | 299 | case AST_AGG_COUNT: 300 | return &agg_desc_count; 301 | 302 | case AST_AGG_MAX: 303 | return &agg_desc_max; 304 | 305 | case AST_AGG_MIN: 306 | return &agg_desc_min; 307 | 308 | case AST_AGG_SUM: 309 | return &agg_desc_sum; 310 | 311 | default: 312 | ERROR("Unrecognized agg kind: %d", (int) agg_kind); 313 | } 314 | } 315 | 316 | -------------------------------------------------------------------------------- /src/libc4/types/schema.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "parser/ast.h" 3 | #include "types/catalog.h" 4 | #include "types/datum.h" 5 | #include "types/expr.h" 6 | #include "types/schema.h" 7 | 8 | static void lookup_type_funcs(Schema *schema, apr_pool_t *pool); 9 | static TuplePool *schema_new_tuple_pool(Schema *schema, C4Runtime *c4); 10 | 11 | Schema * 12 | schema_make(int len, DataType *types, C4Runtime *c4, apr_pool_t *pool) 13 | { 14 | Schema *schema; 15 | 16 | schema = apr_palloc(pool, sizeof(*schema)); 17 | schema->len = len; 18 | schema->types = apr_pmemdup(pool, types, len * sizeof(DataType)); 19 | lookup_type_funcs(schema, pool); 20 | schema->tuple_pool = schema_new_tuple_pool(schema, c4); 21 | 22 | return schema; 23 | } 24 | 25 | Schema * 26 | schema_make_from_ast(List *schema_ast, C4Runtime *c4, apr_pool_t *pool) 27 | { 28 | Schema *schema; 29 | int i; 30 | ListCell *lc; 31 | 32 | schema = apr_palloc(pool, sizeof(*schema)); 33 | schema->len = list_length(schema_ast); 34 | schema->types = apr_palloc(pool, schema->len * sizeof(DataType)); 35 | 36 | i = 0; 37 | foreach (lc, schema_ast) 38 | { 39 | AstSchemaElt *elt = (AstSchemaElt *) lc_ptr(lc); 40 | 41 | schema->types[i] = get_type_id(elt->type_name); 42 | i++; 43 | } 44 | lookup_type_funcs(schema, pool); 45 | schema->tuple_pool = schema_new_tuple_pool(schema, c4); 46 | 47 | return schema; 48 | } 49 | 50 | Schema * 51 | schema_make_from_exprs(int len, ExprState **expr_ary, C4Runtime *c4, apr_pool_t *pool) 52 | { 53 | Schema *schema; 54 | int i; 55 | 56 | schema = apr_palloc(pool, sizeof(*schema)); 57 | schema->len = len; 58 | schema->types = apr_palloc(pool, schema->len * sizeof(DataType)); 59 | 60 | for (i = 0; i < schema->len; i++) 61 | { 62 | schema->types[i] = expr_ary[i]->expr->type; 63 | } 64 | lookup_type_funcs(schema, pool); 65 | schema->tuple_pool = schema_new_tuple_pool(schema, c4); 66 | 67 | return schema; 68 | } 69 | 70 | bool 71 | schema_equal(Schema *s1, Schema *s2) 72 | { 73 | int i; 74 | 75 | if (s1->len != s2->len) 76 | return false; 77 | 78 | for (i = 0; i < s1->len; i++) 79 | { 80 | if (schema_get_type(s1, i) != schema_get_type(s2, i)) 81 | return false; 82 | } 83 | 84 | return true; 85 | } 86 | 87 | /* 88 | * Returns the size of a Tuple that has the given schema. 89 | */ 90 | static apr_size_t 91 | schema_get_tuple_size(Schema *schema) 92 | { 93 | return offsetof(Tuple, vals) + (schema->len * sizeof(Datum)); 94 | } 95 | 96 | static TuplePool * 97 | schema_new_tuple_pool(Schema *schema, C4Runtime *c4) 98 | { 99 | return get_tuple_pool(c4->tpool_mgr, schema_get_tuple_size(schema)); 100 | } 101 | 102 | static 103 | void lookup_type_funcs(Schema *s, apr_pool_t *pool) 104 | { 105 | int i; 106 | 107 | s->hash_funcs = apr_palloc(pool, s->len * sizeof(datum_hash_func)); 108 | s->eq_funcs = apr_palloc(pool, s->len * sizeof(datum_eq_func)); 109 | s->bin_in_funcs = apr_palloc(pool, s->len * sizeof(datum_bin_in_func)); 110 | s->text_in_funcs = apr_palloc(pool, s->len * sizeof(datum_text_in_func)); 111 | s->bin_out_funcs = apr_palloc(pool, s->len * sizeof(datum_bin_out_func)); 112 | s->text_out_funcs = apr_palloc(pool, s->len * sizeof(datum_text_out_func)); 113 | 114 | for (i = 0; i < s->len; i++) 115 | { 116 | s->hash_funcs[i] = type_get_hash_func(s->types[i]); 117 | s->eq_funcs[i] = type_get_eq_func(s->types[i]); 118 | s->bin_in_funcs[i] = type_get_binary_in_func(s->types[i]); 119 | s->text_in_funcs[i] = type_get_text_in_func(s->types[i]); 120 | s->bin_out_funcs[i] = type_get_binary_out_func(s->types[i]); 121 | s->text_out_funcs[i] = type_get_text_out_func(s->types[i]); 122 | } 123 | } 124 | 125 | char * 126 | schema_to_sql_param_str(Schema *schema, apr_pool_t *pool) 127 | { 128 | StrBuf *buf; 129 | int i; 130 | 131 | buf = sbuf_make(pool); 132 | for (i = 0; i < schema->len; i++) 133 | { 134 | if (i != 0) 135 | sbuf_append_char(buf, ','); 136 | sbuf_append_char(buf, '?'); 137 | } 138 | 139 | sbuf_append_char(buf, '\0'); 140 | return buf->data; 141 | } 142 | -------------------------------------------------------------------------------- /src/libc4/types/tuple.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "types/catalog.h" 3 | #include "types/tuple.h" 4 | #include "util/socket.h" 5 | 6 | Tuple * 7 | tuple_make_empty(Schema *s) 8 | { 9 | Tuple *t; 10 | 11 | t = tuple_pool_loan(s->tuple_pool); 12 | t->refcount = 1; 13 | return t; 14 | } 15 | 16 | Tuple * 17 | tuple_make(Schema *s, Datum *values) 18 | { 19 | Tuple *t; 20 | 21 | t = tuple_make_empty(s); 22 | /* XXX: pass-by-ref types? */ 23 | memcpy(t->vals, values, s->len * sizeof(Datum)); 24 | return t; 25 | } 26 | 27 | Tuple * 28 | tuple_make_from_strings(Schema *s, char **values) 29 | { 30 | Tuple *t; 31 | int i; 32 | 33 | t = tuple_make_empty(s); 34 | 35 | for (i = 0; i < s->len; i++) 36 | { 37 | tuple_get_val(t, i) = (s->text_in_funcs[i])(values[i]); 38 | } 39 | 40 | return t; 41 | } 42 | 43 | void 44 | tuple_pin(Tuple *tuple) 45 | { 46 | ASSERT(tuple->refcount > 0); 47 | tuple->refcount++; 48 | } 49 | 50 | void 51 | tuple_unpin(Tuple *tuple, Schema *s) 52 | { 53 | ASSERT(tuple->refcount > 0); 54 | tuple->refcount--; 55 | 56 | if (tuple->refcount == 0) 57 | { 58 | int i; 59 | 60 | for (i = 0; i < s->len; i++) 61 | datum_free(tuple_get_val(tuple, i), 62 | schema_get_type(s, i)); 63 | 64 | tuple_pool_return(s->tuple_pool, tuple); 65 | } 66 | } 67 | 68 | bool 69 | tuple_equal(Tuple *t1, Tuple *t2, Schema *s) 70 | { 71 | int i; 72 | 73 | for (i = 0; i < s->len; i++) 74 | { 75 | Datum val1 = tuple_get_val(t1, i); 76 | Datum val2 = tuple_get_val(t2, i); 77 | 78 | if ((s->eq_funcs[i])(val1, val2) == false) 79 | return false; 80 | } 81 | 82 | return true; 83 | } 84 | 85 | apr_uint32_t 86 | tuple_hash(Tuple *tuple, Schema *s) 87 | { 88 | apr_uint32_t result; 89 | int i; 90 | 91 | result = 37; 92 | for (i = 0; i < s->len; i++) 93 | { 94 | Datum val = tuple_get_val(tuple, i); 95 | apr_uint32_t h = (s->hash_funcs[i])(val); 96 | 97 | /* XXX: choose a better mixing function than XOR */ 98 | result ^= h; 99 | } 100 | 101 | return result; 102 | } 103 | 104 | bool 105 | tuple_cmp_tbl(const void *k1, const void *k2, void *data) 106 | { 107 | Tuple *t1 = (Tuple *) k1; 108 | Tuple *t2 = (Tuple *) k2; 109 | Schema *s = (Schema *) data; 110 | 111 | return tuple_equal(t1, t2, s); 112 | } 113 | 114 | unsigned int 115 | tuple_hash_tbl(const void *key, void *data) 116 | { 117 | Tuple *t = (Tuple *) key; 118 | Schema *s = (Schema *) data; 119 | 120 | return tuple_hash(t, s); 121 | } 122 | 123 | /* 124 | * XXX: Note that we return a malloc'd string, with a cleanup function 125 | * registered in the given pool. This might get expensive if used 126 | * frequently... 127 | */ 128 | char * 129 | tuple_to_str(Tuple *tuple, Schema *s, apr_pool_t *pool) 130 | { 131 | StrBuf *buf; 132 | 133 | buf = sbuf_make(pool); 134 | tuple_to_str_buf(tuple, s, buf); 135 | sbuf_append_char(buf, '\0'); 136 | return buf->data; 137 | } 138 | 139 | void 140 | tuple_to_str_buf(Tuple *tuple, Schema *s, StrBuf *buf) 141 | { 142 | int i; 143 | 144 | for (i = 0; i < s->len; i++) 145 | { 146 | if (i != 0) 147 | sbuf_append_char(buf, ','); 148 | 149 | (s->text_out_funcs[i])(tuple_get_val(tuple, i), buf); 150 | } 151 | 152 | /* Note that we don't NUL-terminate the buffer */ 153 | } 154 | 155 | void 156 | tuple_to_buf(Tuple *tuple, Schema *s, StrBuf *buf) 157 | { 158 | int i; 159 | 160 | for (i = 0; i < s->len; i++) 161 | { 162 | (s->bin_out_funcs[i])(tuple_get_val(tuple, i), buf); 163 | } 164 | } 165 | 166 | Tuple * 167 | tuple_from_buf(StrBuf *buf, Schema *s) 168 | { 169 | Tuple *result; 170 | int i; 171 | 172 | result = tuple_make_empty(s); 173 | 174 | for (i = 0; i < s->len; i++) 175 | { 176 | tuple_get_val(result, i) = (s->bin_in_funcs[i])(buf); 177 | } 178 | 179 | return result; 180 | } 181 | 182 | /* 183 | * XXX: Note that we return a malloc'd string, with a cleanup function 184 | * registered in the given context. This might get expensive if used 185 | * frequently... 186 | */ 187 | char * 188 | tuple_to_sql_insert_str(Tuple *tuple, Schema *s, apr_pool_t *pool) 189 | { 190 | StrBuf *buf; 191 | int i; 192 | 193 | buf = sbuf_make(pool); 194 | for (i = 0; i < s->len; i++) 195 | { 196 | if (i != 0) 197 | sbuf_append_char(buf, ','); 198 | if (s->types[i] == TYPE_STRING) 199 | sbuf_append_char(buf, '\''); 200 | 201 | (s->text_out_funcs[i])(tuple_get_val(tuple, i), buf); 202 | 203 | if (s->types[i] == TYPE_STRING) 204 | sbuf_append_char(buf, '\''); 205 | } 206 | 207 | sbuf_append_char(buf, '\0'); 208 | return buf->data; 209 | } 210 | 211 | bool 212 | tuple_is_remote(Tuple *tuple, TableDef *tbl_def, C4Runtime *c4) 213 | { 214 | Datum tuple_addr; 215 | 216 | if (tbl_def->ls_colno == -1) 217 | return false; 218 | 219 | tuple_addr = tuple_get_val(tuple, tbl_def->ls_colno); 220 | return (string_equal(c4->local_addr, tuple_addr) == false); 221 | } 222 | -------------------------------------------------------------------------------- /src/libc4/util/dump_table.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "types/catalog.h" 3 | #include "operator/scancursor.h" 4 | #include "storage/table.h" 5 | #include "util/dump_table.h" 6 | 7 | void 8 | dump_table(C4Runtime *c4, const char *tbl_name, StrBuf *buf) 9 | { 10 | AbstractTable *table; 11 | ScanCursor *cursor; 12 | Tuple *scan_tuple; 13 | 14 | table = cat_get_table_impl(c4->cat, tbl_name); 15 | 16 | cursor = table->scan_make(table, c4->tmp_pool); 17 | table->scan_reset(table, cursor); 18 | while ((scan_tuple = table->scan_next(table, cursor)) != NULL) 19 | { 20 | tuple_to_str_buf(scan_tuple, table->def->schema, buf); 21 | sbuf_append_char(buf, '\n'); 22 | } 23 | 24 | sbuf_append_char(buf, '\0'); 25 | } 26 | -------------------------------------------------------------------------------- /src/libc4/util/error.c: -------------------------------------------------------------------------------- 1 | /* XXX: __MACH__ is probably slightly wrong */ 2 | #if defined(__linux__) || defined(__FreeBSD__) || defined(__MACH__) 3 | #define HAVE_EXECINFO 4 | #include 5 | #endif 6 | 7 | #include 8 | 9 | #include "c4-internal.h" 10 | #include "storage/sqlite.h" 11 | 12 | static void print_backtrace(void); 13 | 14 | void 15 | apr_error(apr_status_t s, const char *file, int line_num) 16 | { 17 | char buf[512]; 18 | 19 | apr_strerror(s, buf, sizeof(buf)); 20 | fprintf(stderr, "APR ERROR: \"%s\", at %s:%d\n", buf, file, line_num); 21 | print_backtrace(); 22 | exit(1); 23 | } 24 | 25 | void 26 | sqlite_error(C4Runtime *c4, const char *file, int line_num) 27 | { 28 | const char *errmsg; 29 | 30 | errmsg = sqlite3_errmsg(c4->sql->db); 31 | fprintf(stderr, "SQLITE ERROR: \"%s\" (code = %d), at %s:%d\n", 32 | errmsg, sqlite3_errcode(c4->sql->db), file, line_num); 33 | print_backtrace(); 34 | exit(1); 35 | } 36 | 37 | void 38 | simple_error(const char *file, int line_num) 39 | { 40 | fprintf(stderr, "FATAL ERROR at %s:%d\n", file, line_num); 41 | print_backtrace(); 42 | exit(1); 43 | } 44 | 45 | void 46 | var_error(const char *file, int line_num, const char *fmt, ...) 47 | { 48 | va_list args; 49 | char buf[512]; 50 | 51 | va_start(args, fmt); 52 | vsnprintf(buf, sizeof(buf), fmt, args); 53 | va_end(args); 54 | fprintf(stderr, "ERROR: %s, at %s:%d\n", buf, file, line_num); 55 | 56 | print_backtrace(); 57 | exit(1); 58 | } 59 | 60 | void 61 | assert_fail(const char *cond, const char *file, int line_num) 62 | { 63 | fprintf(stderr, "ASSERT FAILED: \"%s\", at %s:%d\n", 64 | cond, file, line_num); 65 | print_backtrace(); 66 | abort(); 67 | } 68 | 69 | static void 70 | print_backtrace(void) 71 | { 72 | #ifdef HAVE_EXECINFO 73 | void *bt_array[128]; 74 | char **bt_strings; 75 | apr_size_t nframes; 76 | apr_size_t i; 77 | 78 | nframes = backtrace(bt_array, 128); 79 | bt_strings = backtrace_symbols(bt_array, nframes); 80 | 81 | fprintf(stderr, "STACK TRACE: (%zd frames)\n", nframes); 82 | for (i = 0; i < nframes; i++) 83 | fprintf(stderr, " %s\n", bt_strings[i]); 84 | 85 | free(bt_strings); 86 | #endif 87 | fflush(stderr); 88 | } 89 | -------------------------------------------------------------------------------- /src/libc4/util/list.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "nodes/copyfuncs.h" 3 | #include "util/list.h" 4 | 5 | static ListCell *make_new_cell(List *list, ListCell *prev, ListCell *next); 6 | 7 | List * 8 | list_make(apr_pool_t *pool) 9 | { 10 | List *result; 11 | 12 | result = apr_palloc(pool, sizeof(*result)); 13 | result->pool = pool; 14 | result->length = 0; 15 | result->head = NULL; 16 | result->tail = NULL; 17 | 18 | return result; 19 | } 20 | 21 | List * 22 | list_append(List *list, void *datum) 23 | { 24 | list->tail = make_new_cell(list, list->tail, NULL); 25 | lc_ptr(list->tail) = datum; 26 | return list; 27 | } 28 | 29 | List * 30 | list_append_int(List *list, int datum) 31 | { 32 | list->tail = make_new_cell(list, list->tail, NULL); 33 | lc_int(list->tail) = datum; 34 | return list; 35 | } 36 | 37 | List * 38 | list_prepend(List *list, void *datum) 39 | { 40 | list->head = make_new_cell(list, NULL, list->head); 41 | lc_ptr(list->head) = datum; 42 | return list; 43 | } 44 | 45 | List * 46 | list_prepend_int(List *list, int datum) 47 | { 48 | list->head = make_new_cell(list, NULL, list->head); 49 | lc_int(list->head) = datum; 50 | return list; 51 | } 52 | 53 | /* 54 | * Add a new cell to the list, with the given "prev" and "next" cells (which 55 | * might be NULL). The caller should fill-in the "data" field of the 56 | * returned ListCell. 57 | */ 58 | static ListCell * 59 | make_new_cell(List *list, ListCell *prev, ListCell *next) 60 | { 61 | ListCell *lc; 62 | 63 | ASSERT(list != NULL); 64 | 65 | lc = apr_palloc(list->pool, sizeof(*lc)); 66 | lc->next = next; 67 | if (prev) 68 | prev->next = lc; 69 | 70 | list->length++; 71 | if (list->length == 1) 72 | list->head = list->tail = lc; 73 | 74 | return lc; 75 | } 76 | 77 | bool 78 | list_member(List *list, const void *datum) 79 | { 80 | ListCell *lc; 81 | 82 | foreach (lc, list) 83 | { 84 | if (lc_ptr(lc) == datum) 85 | return true; 86 | } 87 | 88 | return false; 89 | } 90 | 91 | bool 92 | list_member_int(List *list, int datum) 93 | { 94 | ListCell *lc; 95 | 96 | foreach (lc, list) 97 | { 98 | if (lc_int(lc) == datum) 99 | return true; 100 | } 101 | 102 | return false; 103 | } 104 | 105 | bool 106 | list_member_str(List *list, const char *str) 107 | { 108 | ListCell *lc; 109 | 110 | foreach (lc, list) 111 | { 112 | char *s = (char *) lc_ptr(lc); 113 | 114 | if (strcmp(s, str) == 0) 115 | return true; 116 | } 117 | 118 | return false; 119 | } 120 | 121 | static ListCell * 122 | list_get_cell(List *list, int idx) 123 | { 124 | ListCell *lc; 125 | 126 | if (idx < 0 || idx >= list_length(list)) 127 | FAIL(); 128 | 129 | foreach (lc, list) 130 | { 131 | if (idx == 0) 132 | return lc; 133 | 134 | idx--; 135 | } 136 | 137 | FAIL(); 138 | } 139 | 140 | void * 141 | list_get(List *list, int idx) 142 | { 143 | return lc_ptr(list_get_cell(list, idx)); 144 | } 145 | 146 | int 147 | list_get_int(List *list, int idx) 148 | { 149 | return lc_int(list_get_cell(list, idx)); 150 | } 151 | 152 | static ListCell * 153 | list_remove_first_cell(List *list) 154 | { 155 | ListCell *head; 156 | 157 | if (list->length == 0) 158 | FAIL(); 159 | 160 | head = list->head; 161 | list->head = head->next; 162 | list->length--; 163 | 164 | if (list->length == 0) 165 | list->tail = NULL; 166 | 167 | return head; 168 | } 169 | 170 | /* 171 | * XXX: Note that we leak the ListCell that is removed 172 | */ 173 | void * 174 | list_remove_head(List *list) 175 | { 176 | return lc_ptr(list_remove_first_cell(list)); 177 | } 178 | 179 | int 180 | list_remove_head_int(List *list) 181 | { 182 | return lc_int(list_remove_first_cell(list)); 183 | } 184 | 185 | /* 186 | * Remove the specified cell from the list. "prev" should be the previous 187 | * cell in the list, or NULL to remove the head of the list. Note that the 188 | * removed cell is not free'd. 189 | */ 190 | void 191 | list_remove_cell(List *list, ListCell *cell, ListCell *prev) 192 | { 193 | if (prev) 194 | { 195 | ASSERT(prev->next == cell); 196 | prev->next = cell->next; 197 | } 198 | 199 | if (list->tail == cell) 200 | list->tail = prev; 201 | if (list->head == cell) 202 | { 203 | ASSERT(prev == NULL); 204 | list->head = cell->next; 205 | } 206 | 207 | list->length--; 208 | } 209 | 210 | /* 211 | * Return a shallow copy of "list", allocated from "pool". The data in the 212 | * input list cells is not copied, just the list structure. 213 | */ 214 | List * 215 | list_copy(List *list, apr_pool_t *pool) 216 | { 217 | List *result; 218 | ListCell *lc; 219 | 220 | result = list_make(pool); 221 | foreach (lc, list) 222 | { 223 | ListCell *new_tail = make_new_cell(result, list_tail(result), NULL); 224 | result->tail = new_tail; 225 | new_tail->data = lc->data; 226 | } 227 | 228 | return result; 229 | } 230 | 231 | /* 232 | * Return a deep copy of "list", allocated from "pool". The input list is 233 | * assumed to hold pointers to nodes that can be copied using copy_node(). 234 | */ 235 | List * 236 | list_copy_deep(List *list, apr_pool_t *pool) 237 | { 238 | List *result; 239 | ListCell *lc; 240 | 241 | result = list_make(pool); 242 | foreach (lc, list) 243 | { 244 | void *data = lc_ptr(lc); 245 | list_append(result, copy_node(data, pool)); 246 | } 247 | 248 | return result; 249 | } 250 | 251 | /* 252 | * Return a deep copy of "list", allocated from "pool". The input list is 253 | * assumed to hold pointers to NUL-terminated strings. 254 | */ 255 | List * 256 | list_copy_str(List *list, apr_pool_t *pool) 257 | { 258 | List *result; 259 | ListCell *lc; 260 | 261 | result = list_make(pool); 262 | foreach (lc, list) 263 | { 264 | char *str = (char *) lc_ptr(lc); 265 | list_append(result, apr_pstrdup(pool, str)); 266 | } 267 | 268 | return result; 269 | } 270 | 271 | /* 272 | * Return "list" in reverse order. Note that we construct a new list 273 | * (allocating it from "pool"), but we don't deep-copy the contents of the 274 | * input list. 275 | */ 276 | List * 277 | list_reverse(List *list, apr_pool_t *pool) 278 | { 279 | List *result; 280 | ListCell *lc; 281 | 282 | result = list_make(pool); 283 | foreach (lc, list) 284 | { 285 | ListCell *new_head = make_new_cell(result, NULL, list_head(result)); 286 | new_head->data = lc->data; 287 | result->head = new_head; 288 | } 289 | 290 | return result; 291 | } 292 | 293 | /* We assume that each element of the list is a C4Node */ 294 | void 295 | list_to_str(List *list, StrBuf *sbuf) 296 | { 297 | ListCell *lc; 298 | 299 | foreach (lc, list) 300 | { 301 | C4Node *n = (C4Node *) lc_ptr(lc); 302 | 303 | node_to_str(n, sbuf); 304 | if (lc != list_tail(list)) 305 | sbuf_append(sbuf, ", "); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /src/libc4/util/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "c4-internal.h" 4 | 5 | struct C4Logger 6 | { 7 | C4Runtime *c4; 8 | /* This is reset on each call to c4_log() */ 9 | apr_pool_t *tmp_pool; 10 | }; 11 | 12 | C4Logger * 13 | logger_make(C4Runtime *c4) 14 | { 15 | C4Logger *logger; 16 | 17 | logger = apr_pcalloc(c4->pool, sizeof(*logger)); 18 | logger->tmp_pool = make_subpool(c4->pool); 19 | 20 | return logger; 21 | } 22 | 23 | void 24 | c4_log(C4Runtime *c4, const char *fmt, ...) 25 | { 26 | va_list args; 27 | char *str; 28 | 29 | va_start(args, fmt); 30 | str = apr_pvsprintf(c4->log->tmp_pool, fmt, args); 31 | va_end(args); 32 | 33 | fprintf(stdout, "LOG (%d): %s\n", c4->port, str); 34 | apr_pool_clear(c4->log->tmp_pool); 35 | } 36 | 37 | void 38 | c4_warn_apr(C4Runtime *c4, apr_status_t s, const char *fmt, ...) 39 | { 40 | va_list args; 41 | char *fmt_str; 42 | char apr_buf[512]; 43 | 44 | va_start(args, fmt); 45 | fmt_str = apr_pvsprintf(c4->log->tmp_pool, fmt, args); 46 | va_end(args); 47 | 48 | apr_strerror(s, apr_buf, sizeof(apr_buf)); 49 | 50 | fprintf(stdout, "WARN (%d): %s: \"%s\"\n", c4->port, fmt_str, apr_buf); 51 | apr_pool_clear(c4->log->tmp_pool); 52 | } 53 | 54 | char * 55 | log_tuple(C4Runtime *c4, Tuple *tuple, Schema *s) 56 | { 57 | char *tuple_str = tuple_to_str(tuple, s, c4->log->tmp_pool); 58 | return apr_pstrcat(c4->log->tmp_pool, "{", tuple_str, "}", NULL); 59 | } 60 | 61 | char * 62 | log_datum(C4Runtime *c4, Datum datum, DataType type) 63 | { 64 | StrBuf *sbuf; 65 | 66 | sbuf = sbuf_make(c4->log->tmp_pool); 67 | datum_to_str(datum, type, sbuf); 68 | sbuf_append_char(sbuf, '\0'); 69 | return sbuf->data; 70 | } 71 | -------------------------------------------------------------------------------- /src/libc4/util/mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "c4-internal.h" 4 | 5 | void * 6 | ol_alloc(apr_size_t sz) 7 | { 8 | void *result = malloc(sz); 9 | if (result == NULL) 10 | FAIL(); 11 | return result; 12 | } 13 | 14 | void * 15 | ol_alloc0(apr_size_t sz) 16 | { 17 | void *result = ol_alloc(sz); 18 | memset(result, 0, sz); 19 | return result; 20 | } 21 | 22 | void * 23 | ol_realloc(void *ptr, apr_size_t sz) 24 | { 25 | void *result = realloc(ptr, sz); 26 | if (result == NULL) 27 | FAIL(); 28 | return result; 29 | } 30 | 31 | void 32 | ol_free(void *ptr) 33 | { 34 | free(ptr); 35 | } 36 | 37 | char * 38 | ol_strdup(const char *str) 39 | { 40 | size_t len = strlen(str) + 1; 41 | char *result; 42 | 43 | result = ol_alloc(len); 44 | memcpy(result, str, len); 45 | 46 | return result; 47 | } 48 | 49 | apr_pool_t * 50 | make_subpool(apr_pool_t *parent) 51 | { 52 | apr_status_t s; 53 | apr_pool_t *pool; 54 | 55 | s = apr_pool_create(&pool, parent); 56 | if (s != APR_SUCCESS) 57 | FAIL_APR(s); 58 | 59 | return pool; 60 | } 61 | -------------------------------------------------------------------------------- /src/libc4/util/socket.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "util/socket.h" 3 | 4 | void 5 | socket_set_non_block(apr_socket_t *sock) 6 | { 7 | apr_status_t s; 8 | 9 | s = apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1); 10 | if (s != APR_SUCCESS) 11 | FAIL_APR(s); 12 | 13 | s = apr_socket_timeout_set(sock, 0); 14 | if (s != APR_SUCCESS) 15 | FAIL_APR(s); 16 | } 17 | 18 | apr_sockaddr_t * 19 | socket_get_remote_addr(apr_socket_t *sock) 20 | { 21 | apr_status_t s; 22 | apr_sockaddr_t *addr; 23 | 24 | s = apr_socket_addr_get(&addr, APR_REMOTE, sock); 25 | if (s != APR_SUCCESS) 26 | FAIL_APR(s); 27 | 28 | return addr; 29 | } 30 | 31 | char * 32 | socket_get_remote_loc(apr_socket_t *sock, apr_pool_t *pool) 33 | { 34 | apr_sockaddr_t *addr; 35 | apr_status_t s; 36 | char *ip; 37 | 38 | addr = socket_get_remote_addr(sock); 39 | 40 | s = apr_sockaddr_ip_get(&ip, addr); 41 | if (s != APR_SUCCESS) 42 | FAIL_APR(s); 43 | 44 | return apr_psprintf(pool, "tcp:%s:%hu", ip, addr->port); 45 | } 46 | -------------------------------------------------------------------------------- /src/libc4/util/thread_sync.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "c4-internal.h" 5 | #include "util/thread_sync.h" 6 | 7 | struct C4ThreadSync 8 | { 9 | apr_thread_mutex_t *lock; 10 | apr_thread_cond_t *cond; 11 | enum 12 | { 13 | SYNC_IDLE, 14 | SYNC_WAITING, 15 | SYNC_SIGNALED 16 | } state; 17 | }; 18 | 19 | C4ThreadSync * 20 | thread_sync_make(apr_pool_t *pool) 21 | { 22 | C4ThreadSync *ts; 23 | apr_status_t s; 24 | 25 | ts = apr_palloc(pool, sizeof(*ts)); 26 | ts->state = SYNC_IDLE; 27 | s = apr_thread_mutex_create(&ts->lock, 28 | APR_THREAD_MUTEX_DEFAULT, 29 | pool); 30 | if (s != APR_SUCCESS) 31 | FAIL_APR(s); 32 | 33 | s = apr_thread_cond_create(&ts->cond, pool); 34 | if (s != APR_SUCCESS) 35 | FAIL_APR(s); 36 | 37 | return ts; 38 | } 39 | 40 | void 41 | thread_sync_wait(C4ThreadSync *ts) 42 | { 43 | apr_status_t s; 44 | 45 | s = apr_thread_mutex_lock(ts->lock); 46 | if (s != APR_SUCCESS) 47 | FAIL_APR(s); 48 | 49 | ASSERT(ts->state != SYNC_WAITING); 50 | 51 | /* If we've already been signaled, no need to wait() */ 52 | if (ts->state == SYNC_IDLE) 53 | { 54 | ts->state = SYNC_WAITING; 55 | 56 | do { 57 | s = apr_thread_cond_wait(ts->cond, ts->lock); 58 | if (s != APR_SUCCESS) 59 | FAIL_APR(s); 60 | } while (ts->state != SYNC_SIGNALED); 61 | } 62 | 63 | ts->state = SYNC_IDLE; 64 | s = apr_thread_mutex_unlock(ts->lock); 65 | if (s != APR_SUCCESS) 66 | FAIL_APR(s); 67 | } 68 | 69 | void 70 | thread_sync_signal(C4ThreadSync *ts) 71 | { 72 | apr_status_t s; 73 | 74 | s = apr_thread_mutex_lock(ts->lock); 75 | if (s != APR_SUCCESS) 76 | FAIL_APR(s); 77 | 78 | ASSERT(ts->state != SYNC_SIGNALED); 79 | 80 | if (ts->state == SYNC_WAITING) 81 | { 82 | s = apr_thread_cond_signal(ts->cond); 83 | if (s != APR_SUCCESS) 84 | FAIL_APR(s); 85 | } 86 | ts->state = SYNC_SIGNALED; 87 | s = apr_thread_mutex_unlock(ts->lock); 88 | if (s != APR_SUCCESS) 89 | FAIL_APR(s); 90 | } 91 | -------------------------------------------------------------------------------- /src/libc4/util/tuple_buf.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "util/tuple_buf.h" 3 | 4 | static apr_status_t tuple_buf_cleanup(void *data); 5 | 6 | TupleBuf * 7 | tuple_buf_make(int size, apr_pool_t *pool) 8 | { 9 | TupleBuf *buf; 10 | 11 | buf = apr_palloc(pool, sizeof(*buf)); 12 | buf->size = size; 13 | buf->start = 0; 14 | buf->end = 0; 15 | buf->entries = ol_alloc(buf->size * sizeof(TupleBufEntry)); 16 | 17 | apr_pool_cleanup_register(pool, buf, tuple_buf_cleanup, 18 | apr_pool_cleanup_null); 19 | 20 | return buf; 21 | } 22 | 23 | static apr_status_t 24 | tuple_buf_cleanup(void *data) 25 | { 26 | TupleBuf *buf = (TupleBuf *) data; 27 | 28 | while (!tuple_buf_is_empty(buf)) 29 | { 30 | Tuple *tuple; 31 | TableDef *tbl_def; 32 | 33 | tuple_buf_shift(buf, &tuple, &tbl_def); 34 | tuple_unpin(tuple, tbl_def->schema); 35 | } 36 | 37 | ol_free(buf->entries); 38 | return APR_SUCCESS; 39 | } 40 | 41 | void 42 | tuple_buf_reset(TupleBuf *buf) 43 | { 44 | buf->start = 0; 45 | buf->end = 0; 46 | } 47 | 48 | void 49 | tuple_buf_push(TupleBuf *buf, Tuple *tuple, TableDef *tbl_def) 50 | { 51 | TupleBufEntry *ent; 52 | 53 | if (buf->end == buf->size) 54 | { 55 | /* 56 | * If we've reached the end of the buffer, we need to make room for 57 | * more insertions. We can recover some space by moving the valid 58 | * buffer content back to the beginning of the buffer. If this is 59 | * impossible or not worth the effort, allocate a larger buffer. 60 | * 61 | * Our quick-and-dirty test for whether moving the buffer contents 62 | * is worthwhile is whether we'd regain >= 33% of the buffer space. 63 | * XXX: This could likely be improved. 64 | */ 65 | if (buf->start >= (buf->size / 3)) 66 | { 67 | int nvalid = buf->end - buf->start; 68 | 69 | memmove(buf->entries, &buf->entries[buf->start], 70 | nvalid * sizeof(TupleBufEntry)); 71 | buf->start = 0; 72 | buf->end = nvalid; 73 | } 74 | else 75 | { 76 | buf->size *= 2; 77 | buf->entries = ol_realloc(buf->entries, 78 | buf->size * sizeof(TupleBufEntry)); 79 | } 80 | } 81 | 82 | ent = &buf->entries[buf->end]; 83 | ent->tuple = tuple; 84 | ent->tbl_def = tbl_def; 85 | tuple_pin(ent->tuple); 86 | 87 | buf->end++; 88 | } 89 | 90 | /* 91 | * Remove the first element of the TupleBuf, returning its content via 92 | * the two out parameters. Note that we do NOT unpin the tuple: the 93 | * caller should do that when they are finished with it. 94 | */ 95 | void 96 | tuple_buf_shift(TupleBuf *buf, Tuple **tuple, TableDef **tbl_def) 97 | { 98 | TupleBufEntry *ent; 99 | 100 | ASSERT(!tuple_buf_is_empty(buf)); 101 | ent = &buf->entries[buf->start]; 102 | buf->start++; 103 | if (tuple_buf_is_empty(buf)) 104 | tuple_buf_reset(buf); 105 | 106 | if (tuple) 107 | *tuple = ent->tuple; 108 | if (tbl_def) 109 | *tbl_def = ent->tbl_def; 110 | } 111 | 112 | void 113 | tuple_buf_dump(TupleBuf *buf, C4Runtime *c4) 114 | { 115 | int i; 116 | 117 | for (i = 0; i < tuple_buf_size(buf); i++) 118 | { 119 | int offset = buf->start + i; 120 | TupleBufEntry *ent = &buf->entries[offset]; 121 | 122 | c4_log(c4, "%s: (%d) %s => %s", 123 | __func__, i, 124 | log_tuple(c4, ent->tuple, ent->tbl_def->schema), 125 | ent->tbl_def->name); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/libc4/util/tuple_pool.c: -------------------------------------------------------------------------------- 1 | #include "c4-internal.h" 2 | #include "util/tuple_pool.h" 3 | 4 | typedef struct FreeListElem 5 | { 6 | struct FreeListElem *next_free; 7 | } FreeListElem; 8 | 9 | struct TuplePool 10 | { 11 | apr_pool_t *pool; 12 | FreeListElem *free_head; 13 | apr_size_t elem_size; 14 | 15 | /* 16 | * The total number of tuples of this schema we've allocated space 17 | * for. This includes live tuples, free tuples, and tuples in 18 | * "nalloc_unused". 19 | */ 20 | int ntotal; 21 | 22 | /* 23 | * The number of tuples currently on the freelist. This doesn't include 24 | * "nalloc_unused" below. 25 | */ 26 | int nfree; 27 | 28 | /* 29 | * The current raw allocation that we're assigning tuples from. This is 30 | * used when we can't find a free tuple on the freelist. When the 31 | * allocation is exhausted, we allocate a new block from apr_palloc(). 32 | */ 33 | char *raw_alloc; 34 | int nalloc_unused; 35 | int nalloc_total; 36 | 37 | /* Next element in list of tuple pools maintained by the TuplePoolMgr */ 38 | TuplePool *next; 39 | }; 40 | 41 | struct TuplePoolMgr 42 | { 43 | TuplePool *head; 44 | apr_pool_t *pool; 45 | }; 46 | 47 | #define INITIAL_TPOOL_SIZE 64 48 | 49 | static TuplePool * 50 | make_tuple_pool(apr_size_t elem_size, TuplePoolMgr *tpool_mgr) 51 | { 52 | TuplePool *tpool; 53 | 54 | tpool = apr_palloc(tpool_mgr->pool, sizeof(*tpool)); 55 | tpool->pool = tpool_mgr->pool; 56 | tpool->free_head = NULL; 57 | /* XXX: Ensure this is word-aligned */ 58 | tpool->elem_size = elem_size; 59 | tpool->ntotal = 0; 60 | tpool->nfree = 0; 61 | tpool->nalloc_unused = 0; 62 | tpool->nalloc_total = 0; 63 | tpool->raw_alloc = NULL; 64 | 65 | /* Add to linked list of tuple pools */ 66 | tpool->next = tpool_mgr->head; 67 | tpool_mgr->head = tpool; 68 | 69 | return tpool; 70 | } 71 | 72 | void * 73 | tuple_pool_loan(TuplePool *tpool) 74 | { 75 | void *result; 76 | 77 | /* 78 | * Use the free list if there are any tuples in it. We return the 79 | * most-recently inserted element of the free list, on the theory that 80 | * it is most likely to be "hot" cache-wise. 81 | */ 82 | if (tpool->nfree > 0) 83 | { 84 | result = tpool->free_head; 85 | tpool->free_head = tpool->free_head->next_free; 86 | tpool->nfree--; 87 | return result; 88 | } 89 | 90 | ASSERT(tpool->free_head == NULL); 91 | 92 | if (tpool->nalloc_unused == 0) 93 | { 94 | int alloc_size; 95 | 96 | if (tpool->nalloc_total == 0) 97 | alloc_size = INITIAL_TPOOL_SIZE; 98 | else 99 | alloc_size = tpool->nalloc_total * 2; 100 | 101 | tpool->raw_alloc = apr_palloc(tpool->pool, 102 | alloc_size * tpool->elem_size); 103 | tpool->nalloc_total = alloc_size; 104 | tpool->nalloc_unused = alloc_size; 105 | tpool->ntotal += alloc_size; 106 | } 107 | 108 | result = (void *) tpool->raw_alloc; 109 | tpool->raw_alloc += tpool->elem_size; 110 | tpool->nalloc_unused--; 111 | 112 | return result; 113 | } 114 | 115 | void 116 | tuple_pool_return(TuplePool *tpool, void *ptr) 117 | { 118 | FreeListElem *new_elem = (FreeListElem *) ptr; 119 | 120 | tpool->nfree++; 121 | new_elem->next_free = tpool->free_head; 122 | tpool->free_head = new_elem; 123 | } 124 | 125 | TuplePoolMgr * 126 | tpool_mgr_make(apr_pool_t *pool) 127 | { 128 | TuplePoolMgr *result; 129 | 130 | result = apr_palloc(pool, sizeof(*result)); 131 | result->head = NULL; 132 | result->pool = pool; 133 | 134 | return result; 135 | } 136 | 137 | /* 138 | * This is not efficient, but we don't expect that it will be called in the 139 | * critical path. 140 | */ 141 | TuplePool * 142 | get_tuple_pool(TuplePoolMgr *tpool_mgr, apr_size_t elem_size) 143 | { 144 | TuplePool *tpool; 145 | 146 | for (tpool = tpool_mgr->head; tpool != NULL; tpool = tpool->next) 147 | { 148 | if (tpool->elem_size == elem_size) 149 | return tpool; 150 | } 151 | 152 | return make_tuple_pool(elem_size, tpool_mgr); 153 | } 154 | -------------------------------------------------------------------------------- /src/regress/C4.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'ffi' 3 | 4 | # TODO: 5 | # * Invoke c4_initialize() once at startup-time, and c4_terminate() 6 | # once at shutdown 7 | # * Arrange to invoke c4_destroy() when C4 object is GC'd 8 | class C4 9 | module C4Lib 10 | extend FFI::Library 11 | ffi_lib 'c4' 12 | attach_function 'c4_initialize', [], :void 13 | attach_function 'c4_make', [:pointer, :int], :pointer 14 | attach_function 'c4_install_file', [:pointer, :string], :int 15 | attach_function 'c4_install_str', [:pointer, :string], :int 16 | attach_function 'c4_dump_table', [:pointer, :string], :string 17 | attach_function 'c4_destroy', [:pointer], :void 18 | attach_function 'c4_terminate', [], :void 19 | end 20 | 21 | def initialize(port=0) 22 | C4Lib.c4_initialize 23 | @c4 = C4Lib.c4_make(nil, port) 24 | end 25 | 26 | # TODO: check status 27 | def install_prog(inprog) 28 | s = C4Lib.c4_install_file(@c4, inprog) 29 | end 30 | 31 | # TODO: check status 32 | def install_str(inprog) 33 | s = C4Lib.c4_install_str(@c4, inprog) 34 | end 35 | 36 | def dump_table(tbl_name) 37 | C4Lib.c4_dump_table(@c4, tbl_name) 38 | end 39 | 40 | def destroy 41 | C4Lib.c4_destroy(@c4) 42 | C4Lib.c4_terminate 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /src/regress/README: -------------------------------------------------------------------------------- 1 | Set DYLD_LIBRARY_PATH to contain the directory with libc4.dylib, 2 | or whatever is appropriate on your OS. 3 | 4 | You will also need the "ffi" gem installed: 5 | 6 | gem install ffi 7 | 8 | Then run "ruby regression.rb" 9 | 10 | -------------------------------------------------------------------------------- /src/regress/expected/aggregation: -------------------------------------------------------------------------------- 1 | **** \dump "agg_t1" **** 2 | 5,25 3 | 6,120 4 | **** \dump "agg_t0" **** 5 | 5,25 6 | 6,120 7 | **** \dump "agg_t1" **** 8 | 5,15 9 | 6,120 10 | 7,50 11 | **** \dump "agg_t0" **** 12 | 5,15 13 | 6,120 14 | 7,50 15 | **** \dump "agg_t1" **** 16 | 5,45 17 | 6,120 18 | 7,50 19 | **** \dump "agg_t0" **** 20 | 5,45 21 | 6,120 22 | 7,50 23 | **** \dump "agg_t6" **** 24 | 100,101 25 | 199,99 26 | 2000,1001 27 | **** \dump "agg_t7" **** 28 | 100,5050 29 | 199,14850 30 | 2000,1501500 31 | **** \dump "agg_t9" **** 32 | 10,88 33 | **** \dump "agg_t10_cnt" **** 34 | a,1 35 | b,2 36 | **** \dump "max_t2" **** 37 | 0,50 38 | 1000,1000 39 | 5,20 40 | **** \dump "max_t2" **** 41 | 1000,1000 42 | 5,20 43 | **** \dump "max_t2" **** 44 | 1000,1000 45 | 15,15 46 | **** \dump "min_t2" **** 47 | 1,1 48 | 10,10 49 | 5,5 50 | **** \dump "min_t2" **** 51 | 10,10 52 | 5,5 53 | **** \dump "min_t2" **** 54 | 0,0 55 | 10,10 56 | **** \dump "avg_t1" **** 57 | 1.200000,0.000000 58 | 1.200000,0.300000 59 | 1.200000,0.600000 60 | 1.200000,0.900000 61 | 1.200000,1.200000 62 | 2.400000,0.000000 63 | 2.400000,0.300000 64 | 2.400000,0.600000 65 | 2.400000,0.900000 66 | 2.400000,1.200000 67 | 2.400000,1.500000 68 | 2.400000,1.800000 69 | 2.400000,2.100000 70 | 2.400000,2.400000 71 | **** \dump "avg_t2" **** 72 | 1.200000,1.800000,1.200000,0.600000 73 | 2.400000,3.600000,2.400000,1.200000 74 | **** \dump "bool_agg_t2" **** 75 | 10,true,false 76 | 15,true,true 77 | **** \dump "bool_agg_t2" **** 78 | 10,true,false 79 | 15,true,false 80 | **** \dump "str_agg_t2" **** 81 | 10,foo,abc 82 | 15,x, 83 | 5,foo2,foo 84 | -------------------------------------------------------------------------------- /src/regress/expected/arithmetic: -------------------------------------------------------------------------------- 1 | **** \dump "input" **** 2 | 0 3 | 1 4 | 10 5 | 11 6 | 12 7 | 13 8 | 14 9 | 15 10 | 16 11 | 17 12 | 18 13 | 19 14 | 2 15 | 20 16 | 3 17 | 4 18 | 5 19 | 6 20 | 7 21 | 8 22 | 9 23 | **** \dump "add_one" **** 24 | 0,1 25 | 1,2 26 | 10,11 27 | 11,12 28 | 12,13 29 | 13,14 30 | 14,15 31 | 15,16 32 | 16,17 33 | 17,18 34 | 18,19 35 | 19,20 36 | 2,3 37 | 20,21 38 | 3,4 39 | 4,5 40 | 5,6 41 | 6,7 42 | 7,8 43 | 8,9 44 | 9,10 45 | **** \dump "sub_500" **** 46 | 0,-500 47 | 1,-499 48 | 10,-490 49 | 11,-489 50 | 12,-488 51 | 13,-487 52 | 14,-486 53 | 15,-485 54 | 16,-484 55 | 17,-483 56 | 18,-482 57 | 19,-481 58 | 2,-498 59 | 20,-480 60 | 3,-497 61 | 4,-496 62 | 5,-495 63 | 6,-494 64 | 7,-493 65 | 8,-492 66 | 9,-491 67 | **** \dump "input_d8" **** 68 | 10.000000,10.000000 69 | 2.000000,10.000000 70 | 3.000000,10.000000 71 | 4.000000,10.000000 72 | 5.000000,10.000000 73 | 6.000000,10.000000 74 | 7.000000,10.000000 75 | 8.000000,10.000000 76 | 9.000000,10.000000 77 | **** \dump "add_d8" **** 78 | 10.000000,10.000000,20.000000 79 | 2.000000,10.000000,12.000000 80 | 3.000000,10.000000,13.000000 81 | 4.000000,10.000000,14.000000 82 | 5.000000,10.000000,15.000000 83 | 6.000000,10.000000,16.000000 84 | 7.000000,10.000000,17.000000 85 | 8.000000,10.000000,18.000000 86 | 9.000000,10.000000,19.000000 87 | **** \dump "sub_d8" **** 88 | 10.000000,10.000000,0.000000 89 | 2.000000,10.000000,-8.000000 90 | 3.000000,10.000000,-7.000000 91 | 4.000000,10.000000,-6.000000 92 | 5.000000,10.000000,-5.000000 93 | 6.000000,10.000000,-4.000000 94 | 7.000000,10.000000,-3.000000 95 | 8.000000,10.000000,-2.000000 96 | 9.000000,10.000000,-1.000000 97 | **** \dump "mult_d8" **** 98 | 10.000000,10.000000,100.000000 99 | 2.000000,10.000000,20.000000 100 | 3.000000,10.000000,30.000000 101 | 4.000000,10.000000,40.000000 102 | 5.000000,10.000000,50.000000 103 | 6.000000,10.000000,60.000000 104 | 7.000000,10.000000,70.000000 105 | 8.000000,10.000000,80.000000 106 | 9.000000,10.000000,90.000000 107 | -------------------------------------------------------------------------------- /src/regress/expected/fqpath: -------------------------------------------------------------------------------- 1 | **** \dump "valid_file" **** 2 | 1,0,/,true 3 | 10,8,f2,false 4 | 2,1,Bar,false 5 | 3,1,Baz,true 6 | 4,1,Bax,false 7 | 5,3,xxx,true 8 | 6,3,xyz,false 9 | 7,3,zzz,false 10 | 8,1,Qux,true 11 | 9,8,f1,false 12 | **** \dump "fqpath" **** 13 | 1,/ 14 | 10,/Qux/f2 15 | 2,/Bar 16 | 3,/Baz/ 17 | 4,/Bax 18 | 5,/Baz/xxx/ 19 | 6,/Baz/xyz 20 | 7,/Baz/zzz 21 | 8,/Qux/ 22 | 9,/Qux/f1 23 | **** \dump "valid_file" **** 24 | 1,0,/,true 25 | 10,8,f2,false 26 | 2,1,Bar,false 27 | 4,1,Bax,false 28 | 8,1,Qux,true 29 | 9,8,f1,false 30 | **** \dump "fqpath" **** 31 | 1,/ 32 | 10,/Qux/f2 33 | 2,/Bar 34 | 4,/Bax 35 | 8,/Qux/ 36 | 9,/Qux/f1 37 | **** \dump "valid_file" **** 38 | 39 | **** \dump "fqpath" **** 40 | 41 | -------------------------------------------------------------------------------- /src/regress/expected/issue_5: -------------------------------------------------------------------------------- 1 | **** \dump "i5_a" **** 2 | 3 | -------------------------------------------------------------------------------- /src/regress/expected/join: -------------------------------------------------------------------------------- 1 | **** \dump "r" **** 2 | foo,bam 3 | foo,baz 4 | **** \dump "later" **** 5 | bar,bam 6 | bar,baz 7 | foo,bar 8 | **** \dump "j2" **** 9 | 1 10 | **** \dump "j2" **** 11 | 1 12 | **** \dump "j2" **** 13 | 1 14 | 2 15 | 3 16 | 4 17 | 5 18 | 6 19 | 7 20 | 8 21 | -------------------------------------------------------------------------------- /src/regress/expected/negation: -------------------------------------------------------------------------------- 1 | **** \dump "neg_t3" **** 2 | xxx2,b 3 | **** \dump "neg_t3" **** 4 | xxx2,b 5 | xxx3,foo 6 | **** \dump "neg_t3" **** 7 | xxx2,b 8 | **** \dump "neg_t3" **** 9 | 10 | **** \dump "neg_t4" **** 11 | xxx,1 12 | **** \dump "neg_t5" **** 13 | xxx,1 14 | **** \dump "neg_t8" **** 15 | 16 | **** \dump "neg_t10" **** 17 | xxx,1 18 | **** \dump "neg_t4" **** 19 | 20 | **** \dump "neg_t5" **** 21 | 22 | **** \dump "neg_t8" **** 23 | xxx,1 24 | **** \dump "neg_t10" **** 25 | 26 | **** \dump "multi_a" **** 27 | 0,0 28 | 1,1 29 | 2,2 30 | 3,3 31 | **** \dump "multi_a" **** 32 | 0,0 33 | 1,1 34 | 2,2 35 | **** \dump "multi_insert_dest" **** 36 | 5 37 | 9 38 | **** \dump "multi_insert_dest" **** 39 | 9 40 | **** \dump "multi_insert_dest" **** 41 | 9 42 | **** \dump "multi_insert_dest" **** 43 | 44 | -------------------------------------------------------------------------------- /src/regress/expected/not_equal: -------------------------------------------------------------------------------- 1 | **** \dump "c" **** 2 | a,aaa 3 | x,y 4 | -------------------------------------------------------------------------------- /src/regress/expected/parts: -------------------------------------------------------------------------------- 1 | **** \dump "subpart" **** 2 | bike,bars 3 | bike,bottom bracket 4 | bike,fork 5 | bike,frame 6 | bike,headset 7 | bike,hub 8 | bike,seat 9 | bike,spoke 10 | bike,tire 11 | bike,wheel 12 | frame,bottom bracket 13 | frame,fork 14 | headset,bars 15 | wheel,hub 16 | wheel,spoke 17 | wheel,tire 18 | -------------------------------------------------------------------------------- /src/regress/input/aggregation: -------------------------------------------------------------------------------- 1 | define(agg_t0, {int, int}); 2 | define(agg_t1, {int, int}); 3 | define(agg_t2, {int, int}); 4 | define(agg_t3, {int, int}); 5 | define(agg_t4, {int, int}); 6 | 7 | agg_t0(A, B) :- agg_t1(A, B); 8 | agg_t1(A, sum) :- agg_t2(A, B), notin agg_t3(A, B), notin agg_t4(A, B); 9 | 10 | agg_t2(5, 10); 11 | agg_t2(5, 15); 12 | agg_t2(6, 20); 13 | agg_t2(6, 100); 14 | 15 | \dump agg_t1 16 | \dump agg_t0 17 | 18 | agg_t2(7, 50); 19 | agg_t3(5, 10); 20 | agg_t3(20, 20); 21 | agg_t4(6, 25); 22 | 23 | \dump agg_t1 24 | \dump agg_t0 25 | 26 | agg_t2(6, 25); 27 | agg_t2(5, 15); 28 | agg_t2(5, 30); 29 | 30 | \dump agg_t1 31 | \dump agg_t0 32 | 33 | define(agg_t5, {int, int}); 34 | define(agg_t6, {int, int}); 35 | define(agg_t7, {int, int}); 36 | 37 | agg_t5(A, B + 1) :- agg_t5(A, B), A > B; 38 | agg_t6(A, count) :- agg_t5(A, B); 39 | agg_t7(A, sum) :- agg_t5(A, B); 40 | 41 | agg_t5(100, 0); 42 | agg_t5(199, 101); 43 | agg_t5(2000, 1000); 44 | 45 | \dump agg_t6 46 | \dump agg_t7 47 | 48 | /* Test non-constant expr arguments to agg functions */ 49 | define(agg_t8, {int, int}); 50 | define(agg_t9, {int, int}); 51 | 52 | agg_t8(A, B + 1) :- agg_t8(A, B), A > B; 53 | agg_t9(A, sum) :- agg_t8(A, B); 54 | 55 | agg_t8(10, 0); 56 | 57 | \dump agg_t9 58 | 59 | /* Bug #2: count<> for strings */ 60 | define(agg_t10, {string, string}); 61 | define(agg_t10_cnt, {string, int}); 62 | 63 | agg_t10("a", "A"); 64 | agg_t10("b", "A"); 65 | agg_t10("b", "B"); 66 | agg_t10_cnt(X, count) :- agg_t10(X, Y); 67 | 68 | \dump agg_t10_cnt 69 | 70 | /* Max aggregate */ 71 | define(max_t1, {int, int}); 72 | define(max_t2, {int, int}); 73 | define(max_t3, {int}); 74 | 75 | max_t1(A, B + 1) :- max_t1(A, B), A > B; 76 | max_t2(A, max) :- max_t1(A, B), notin max_t3(A); 77 | 78 | max_t1(5, 10); 79 | max_t1(5, 15); 80 | max_t1(5, 20); 81 | max_t1(0, 50); 82 | max_t1(0, 0); 83 | max_t1(1000, 1); 84 | max_t1(1000, 0); 85 | 86 | \dump max_t2 87 | 88 | max_t3(0); 89 | 90 | \dump max_t2 91 | 92 | max_t3(5); 93 | max_t1(5, 0); 94 | max_t1(15, 0); 95 | 96 | \dump max_t2 97 | 98 | /* Min aggregate */ 99 | define(min_t1, {int, int}); 100 | define(min_t2, {int, int}); 101 | define(min_t3, {int}); 102 | 103 | min_t1(A, B - 1) :- min_t1(A, B), A < B; 104 | min_t2(A, min) :- min_t1(A, B), notin min_t3(A); 105 | 106 | min_t1(5, 20); 107 | min_t1(10, 50); 108 | min_t1(1, 1000); 109 | 110 | \dump min_t2 111 | 112 | min_t3(1); 113 | min_t1(1, 0); 114 | 115 | \dump min_t2 116 | 117 | min_t3(5); 118 | min_t1(0, 5); 119 | min_t1(5, 23); 120 | 121 | \dump min_t2 122 | 123 | /* Avg aggregate */ 124 | define(avg_t1, {double, double}); 125 | define(avg_t2, {double, double, double, double}); 126 | 127 | avg_t1(A, B + 0.3) :- avg_t1(A, B), A > B; 128 | avg_t2(A, avg, avg, avg) :- avg_t1(A, B); 129 | 130 | avg_t1(1.2, 0.0); 131 | avg_t1(2.4, 0.0); 132 | 133 | \dump avg_t1 134 | \dump avg_t2 135 | 136 | /* Min and max for booleans */ 137 | define(bool_agg_t1, {int, bool}); 138 | define(bool_agg_t2, {int, bool, bool}); 139 | 140 | bool_agg_t2(A, max, min) :- bool_agg_t1(A, B); 141 | 142 | bool_agg_t1(10, false); 143 | bool_agg_t1(10, true); 144 | bool_agg_t1(15, true); 145 | 146 | \dump bool_agg_t2 147 | 148 | bool_agg_t1(15, false); 149 | 150 | \dump bool_agg_t2 151 | 152 | /* Min and max for strings */ 153 | define(str_agg_t1, {int, string}); 154 | define(str_agg_t2, {int, string, string}); 155 | 156 | str_agg_t2(A, max, min) :- str_agg_t1(A, S); 157 | 158 | str_agg_t1(5, "foo"); 159 | str_agg_t1(5, "foo2"); 160 | str_agg_t1(10, "bar"); 161 | str_agg_t1(10, "foo"); 162 | str_agg_t1(10, "abc"); 163 | str_agg_t1(15, ""); 164 | str_agg_t1(15, "x"); 165 | 166 | \dump str_agg_t2 167 | -------------------------------------------------------------------------------- /src/regress/input/arithmetic: -------------------------------------------------------------------------------- 1 | define(input, {int}); 2 | define(add_one, {int, int}); 3 | define(sub_500, {int, int}); 4 | 5 | input(0); 6 | input(X+1) :- input(X), X < 20; 7 | add_one(A, A + 1) :- input(A); 8 | sub_500(A, A - 500) :- input(A); 9 | 10 | \dump input 11 | \dump add_one 12 | \dump sub_500 13 | 14 | define(input_d8, {double, double}); 15 | define(add_d8, {double, double, double}); 16 | define(sub_d8, {double, double, double}); 17 | define(mult_d8, {double, double, double}); 18 | 19 | input_d8(2.0, 10.0); 20 | input_d8(X + 1.0, Y) :- input_d8(X, Y), X < Y; 21 | 22 | add_d8(X, Y, X + Y) :- input_d8(X, Y); 23 | sub_d8(X, Y, X - Y) :- input_d8(X, Y); 24 | mult_d8(X, Y, X * Y) :- input_d8(X, Y); 25 | 26 | \dump input_d8 27 | \dump add_d8 28 | \dump sub_d8 29 | \dump mult_d8 30 | -------------------------------------------------------------------------------- /src/regress/input/fqpath: -------------------------------------------------------------------------------- 1 | define(file, {int, int, string, bool}); 2 | define(retract_file, {int}); 3 | define(valid_file, {int, int, string, bool}); 4 | define(fqpath, {int, string}); 5 | 6 | /* 7 | * A file is "valid" if it hasn't been retracted, and either its 8 | * parent is valid, or it is the root of the tree (parent == 0). 9 | */ 10 | valid_file(FileId, FParentId, FName, IsDir) :- 11 | valid_file(FParentId, _, _, _), 12 | file(FileId, FParentId, FName, IsDir), 13 | notin retract_file(FileId); 14 | 15 | valid_file(FileId, 0, FName, IsDir) :- 16 | file(FileId, 0, FName, IsDir), 17 | notin retract_file(FileId); 18 | 19 | fqpath(FileId, FName) :- 20 | valid_file(FileId, 0, FName, IsDir), 21 | IsDir == true; 22 | 23 | fqpath(FileId, ParentPath + FName) :- 24 | valid_file(FileId, FParentId, FName, false), 25 | fqpath(FParentId, ParentPath); 26 | 27 | fqpath(FileId, ParentPath + FName + "/") :- 28 | valid_file(FileId, FParentId, FName, true), 29 | fqpath(FParentId, ParentPath); 30 | 31 | file(1, 0, "/", true); 32 | file(2, 1, "Bar", false); 33 | file(3, 1, "Baz", true); 34 | file(4, 1, "Bax", false); 35 | file(5, 3, "xxx", true); 36 | file(6, 3, "xyz", false); 37 | file(7, 3, "zzz", false); 38 | file(8, 1, "Qux", true); 39 | file(9, 8, "f1", false); 40 | file(10, 8, "f2", false); 41 | 42 | \dump valid_file 43 | \dump fqpath 44 | 45 | retract_file(3); 46 | 47 | \dump valid_file 48 | \dump fqpath 49 | 50 | retract_file(1); 51 | 52 | \dump valid_file 53 | \dump fqpath 54 | -------------------------------------------------------------------------------- /src/regress/input/issue_5: -------------------------------------------------------------------------------- 1 | define(i5_a, {int}); 2 | define(i5_b, {int}); 3 | define(i5_c, {int}); 4 | define(i5_plus_a, {int}); 5 | define(i5_minus_a, {int}); 6 | 7 | define(i5_foo, {bool}); 8 | 9 | i5_b(1); 10 | i5_b(2); 11 | i5_c(1); 12 | i5_c(2); 13 | 14 | i5_foo(true) :- i5_c(_); 15 | 16 | i5_minus_a(X) :- i5_b(X), i5_c(X); 17 | i5_plus_a(X) :- i5_b(X), notin i5_c(X); 18 | i5_a(X) :- i5_plus_a(X), notin i5_minus_a(X); 19 | 20 | \dump i5_a 21 | -------------------------------------------------------------------------------- /src/regress/input/join: -------------------------------------------------------------------------------- 1 | define(q, {string, string}); 2 | define(p, {string, string}); 3 | define(r, {string, string}); 4 | 5 | r(A, C) :- 6 | p(A, B), 7 | q(B, C); 8 | 9 | p("foo", "bar"); 10 | q("bar", "baz"); 11 | q("bar", "bam"); 12 | 13 | \dump r 14 | 15 | define(later, {string, string}); 16 | 17 | later(A, B) :- p(A, B); 18 | later(A, B) :- q(A, B); 19 | 20 | \dump later 21 | 22 | define(j1, {int}); 23 | define(j2, {int}); 24 | 25 | j2(A + 1) :- j2(A), j1(B), A >= B, A <= 7; 26 | 27 | j2(1); 28 | \dump j2 29 | j1(5); 30 | \dump j2 31 | j1(1); 32 | \dump j2 33 | -------------------------------------------------------------------------------- /src/regress/input/negation: -------------------------------------------------------------------------------- 1 | /* Basic negation */ 2 | define(neg_t1, {string, string}); 3 | define(neg_t2, {string, string}); 4 | define(neg_t3, {string, string}); 5 | 6 | neg_t1("xxx1", "a"); 7 | neg_t1("xxx2", "b"); 8 | neg_t2("xxx1", "a"); 9 | neg_t2("xxx2", "bbbbbbb"); 10 | 11 | neg_t3(A, B) :- neg_t1(A, B), notin neg_t2(A, B); 12 | 13 | \dump neg_t3 14 | 15 | neg_t1("xxx3", "foo"); 16 | 17 | \dump neg_t3 18 | 19 | neg_t2("xxx3", "foo"); 20 | neg_t2("xxx4", "ggggg"); 21 | 22 | \dump neg_t3 23 | 24 | neg_t2("xxx2", "b"); 25 | 26 | \dump neg_t3 27 | 28 | /* Deletion and its consequences */ 29 | define(neg_t4, {string, string}); 30 | define(neg_t5, {string, string}); 31 | define(neg_t6, {string, string}); 32 | define(neg_t7, {string, string}); 33 | define(neg_t8, {string, string}); 34 | define(neg_t9, {string, string}); 35 | define(neg_t10, {string, string}); 36 | 37 | neg_t6("xxx", "1"); 38 | neg_t6("yyy", "2"); 39 | neg_t7("yyy", "100"); 40 | neg_t7("zzz", "200"); 41 | neg_t9("xxx", "1"); 42 | 43 | neg_t5(A, B) :- neg_t6(A, B), notin neg_t7(A, _); 44 | neg_t4(A, B) :- neg_t5(A, B); 45 | neg_t8(A, B) :- neg_t9(A, B), notin neg_t4(A, B); 46 | neg_t10(A, B) :- neg_t9(A, B), notin neg_t8(A, B); 47 | 48 | \dump neg_t4 49 | \dump neg_t5 50 | \dump neg_t8 51 | \dump neg_t10 52 | 53 | neg_t7("xxx", "300"); 54 | 55 | \dump neg_t4 56 | \dump neg_t5 57 | \dump neg_t8 58 | \dump neg_t10 59 | 60 | /* Test multiple derivations of the same tuple, then NOT */ 61 | define(multi_a, {int, int}); 62 | define(multi_b, {int, int}); 63 | define(multi_c, {int, int}); 64 | define(multi_d, {int, int}); 65 | define(multi_e, {int, int}); 66 | 67 | multi_b(0, 0); 68 | multi_b(1, 1); 69 | multi_b(2, 2); 70 | 71 | multi_c(2, 2); 72 | multi_c(3, 3); 73 | 74 | multi_a(A, B) :- multi_b(A, B); 75 | multi_a(A, B) :- multi_c(A, B), notin multi_d(A, B), notin multi_e(A, B); 76 | 77 | \dump multi_a 78 | 79 | multi_d(2, 2); 80 | multi_d(2, 2); /* dup */ 81 | multi_d(3, 3); 82 | multi_d(4, 4); 83 | 84 | \dump multi_a 85 | 86 | /* 87 | * Test that multiple facts inserted in the same FP only bump the 88 | * refcount of derived tuples once, not multiple times. 89 | */ 90 | define(multi_insert_src1, {int}); 91 | define(multi_insert_src2, {int}); 92 | define(multi_insert_dest, {int}); 93 | define(multi_insert_cancel, {int}); 94 | 95 | multi_insert_dest(A) :- multi_insert_src1(A), multi_insert_src2(A), 96 | notin multi_insert_cancel(A); 97 | 98 | multi_insert_src1(5); 99 | multi_insert_src2(5); 100 | multi_insert_src1(7); 101 | multi_insert_src1(9); 102 | multi_insert_src2(9); 103 | multi_insert_src2(13); 104 | 105 | \dump multi_insert_dest 106 | 107 | multi_insert_cancel(5); 108 | 109 | \dump multi_insert_dest 110 | 111 | multi_insert_src2(7); 112 | multi_insert_cancel(13); 113 | multi_insert_cancel(7); 114 | 115 | \dump multi_insert_dest 116 | 117 | multi_insert_cancel(9); 118 | 119 | \dump multi_insert_dest 120 | -------------------------------------------------------------------------------- /src/regress/input/not_equal: -------------------------------------------------------------------------------- 1 | define(c, {string, string}); 2 | define(d, {string, string}); 3 | 4 | d("a", "a"); 5 | d("x", "y"); 6 | d("a", "aaa"); 7 | d("y", "y"); 8 | 9 | c(X, Y) :- d(X, Y), X != Y; 10 | 11 | \dump c 12 | -------------------------------------------------------------------------------- /src/regress/input/parts: -------------------------------------------------------------------------------- 1 | define(assembly, {string, string}); 2 | define(subpart, {string, string}); 3 | 4 | subpart(A, B) :- 5 | assembly(A, B); 6 | 7 | subpart(A, B) :- 8 | assembly(A, C), 9 | subpart(C, B); 10 | 11 | assembly("wheel", "spoke"); 12 | assembly("wheel", "tire"); 13 | assembly("wheel", "hub"); 14 | assembly("frame", "fork"); 15 | assembly("frame", "bottom bracket"); 16 | assembly("headset", "bars"); 17 | assembly("bike", "wheel"); 18 | assembly("bike", "frame"); 19 | assembly("bike", "headset"); 20 | assembly("bike", "seat"); 21 | 22 | \dump subpart 23 | -------------------------------------------------------------------------------- /src/regress/regression.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'fileutils' 3 | require_relative './C4' 4 | 5 | INPUT_DIR="input" 6 | OUTPUT_DIR="output" 7 | EXPECTED_DIR="expected" 8 | DIFF_FILE="regress.diffs" 9 | 10 | def make_output_dir 11 | FileUtils.remove_dir(OUTPUT_DIR) if File.directory?(OUTPUT_DIR) 12 | Dir.mkdir(OUTPUT_DIR) 13 | end 14 | 15 | def run_tests(c4, test_name) 16 | puts "====" 17 | tests = Dir.entries(INPUT_DIR).reject { |i| i.match(/^\./) } 18 | ran_tests = [] 19 | tests.each do |test| 20 | next unless (test_name.nil? or test == test_name) 21 | ran_tests << test 22 | print "Running test \"#{test}\"..." 23 | input = "" 24 | output = "" 25 | File.open("#{INPUT_DIR}/#{test}").each_line do |line| 26 | if line =~ /^\\dump (.+)/ 27 | c4.install_str(input) unless input == "" 28 | output << "**** \\dump \"#{$1}\" ****\n" 29 | output << c4.dump_table($1).split("\n").sort.join("\n") 30 | output << "\n" 31 | input = "" 32 | else 33 | input << line 34 | end 35 | end 36 | puts " done" 37 | 38 | File.open("#{OUTPUT_DIR}/#{test}", 'w') do |f| 39 | f.write(output) 40 | end 41 | end 42 | 43 | puts "====" 44 | num_fails = 0 45 | ran_tests.each do |test| 46 | `diff -ur #{EXPECTED_DIR}/#{test} #{OUTPUT_DIR}/#{test} >> #{DIFF_FILE}` 47 | if $? != 0 48 | num_fails += 1 49 | puts "Test \"#{test}\" failed!" 50 | end 51 | end 52 | 53 | if num_fails == 0 54 | puts "All #{ran_tests.length} tests passed" 55 | else 56 | puts "#{num_fails} TEST#{num_fails > 1 ? "S" : ""} FAILED; see regress.diffs" 57 | end 58 | end 59 | 60 | make_output_dir 61 | File.delete(DIFF_FILE) if File.exists?(DIFF_FILE) 62 | c = C4.new 63 | run_tests(c, ARGV[0]) 64 | c.destroy 65 | --------------------------------------------------------------------------------