├── .github └── workflows │ └── cmake.yml ├── CMakeLists.txt ├── README.md ├── cmake └── FindPostgreSQL.cmake ├── examples ├── sorting-speed │ ├── CMakeLists.txt │ ├── paper.pdf │ ├── paper.tex │ ├── sorting-speed.cpp │ ├── speed-data.txt │ ├── speed.pdf │ ├── speed.plot │ └── stats.txt └── stats_writer │ ├── CMakeLists.txt │ ├── stats_writer.h │ └── test.cpp ├── src ├── CMakeLists.txt ├── common.cpp ├── common.h ├── fieldset.cpp ├── fieldset.h ├── gnuplot.cpp ├── importdata.cpp ├── importdata.h ├── latex.cpp ├── main.cpp ├── mysql.cpp ├── mysql.h ├── pgsql.cpp ├── pgsql.h ├── reformat.cpp ├── reformat.h ├── simpleglob.h ├── simpleopt.h ├── sql.cpp ├── sql.h ├── sqlite-functions.cpp ├── sqlite.cpp ├── sqlite.h ├── strtools.h └── textlines.h └── tests ├── CMakeLists.txt ├── gnuplot ├── CMakeLists.txt ├── macro1-data.txt ├── macro1.gp ├── macro1.out ├── multiplot1-data.txt ├── multiplot1.gp ├── multiplot1.out ├── plot1-data.txt ├── plot1.gp ├── plot1.out └── test.data └── latex ├── CMakeLists.txt ├── attr.out ├── attr.tex ├── defmacro.out ├── defmacro.tex ├── format1.out ├── format1.tex ├── multiplot1.out ├── multiplot1.tex ├── nolegend.out ├── nolegend.tex ├── plot1.out ├── plot1.tex ├── tabtable1.out ├── tabtable1.tex ├── tabular1.out ├── tabular1.tex ├── tabular2.out ├── tabular2.tex ├── test-loaded.db ├── test.data ├── texttable1.out └── texttable1.tex /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: Compile and Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | 8 | jobs: 9 | build: 10 | # The type of runner that the job will run on 11 | name: ${{ matrix.config.name }} 12 | runs-on: ${{ matrix.config.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | config: 17 | # Run Tests on Ubuntu 22.04 18 | - name: "Ubuntu 22.04 Debug SQLite" 19 | os: ubuntu-22.04 20 | build_type: "Debug" 21 | test_database: "sqlite" 22 | 23 | - name: "Ubuntu 22.04 Release SQLite" 24 | os: ubuntu-22.04 25 | build_type: "Release" 26 | test_database: "sqlite" 27 | 28 | - name: "Ubuntu 22.04 Debug PostgreSQL" 29 | os: ubuntu-22.04 30 | build_type: "Debug" 31 | test_database: "postgresql" 32 | 33 | - name: "Ubuntu 22.04 Release PostgreSQL" 34 | os: ubuntu-22.04 35 | build_type: "Release" 36 | test_database: "postgresql" 37 | 38 | - name: "Ubuntu 22.04 Debug MySQL" 39 | os: ubuntu-22.04 40 | build_type: "Debug" 41 | test_database: "mysql" 42 | 43 | - name: "Ubuntu 22.04 Release MySQL" 44 | os: ubuntu-22.04 45 | build_type: "Release" 46 | test_database: "mysql" 47 | 48 | # Run Tests on Ubuntu 20.04 49 | - name: "Ubuntu 20.04 Release SQLite" 50 | os: ubuntu-20.04 51 | build_type: "Release" 52 | test_database: "sqlite" 53 | 54 | - name: "Ubuntu 20.04 Release PostgreSQL" 55 | os: ubuntu-20.04 56 | build_type: "Release" 57 | test_database: "postgresql" 58 | 59 | - name: "Ubuntu 20.04 Release MySQL" 60 | os: ubuntu-20.04 61 | build_type: "Release" 62 | test_database: "mysql" 63 | 64 | # Run Tests on Ubuntu 20.04 with newer GCC 65 | - name: "Ubuntu 20.04 gcc 11 Release SQLite" 66 | os: ubuntu-20.04 67 | build_type: "Release" 68 | gcc_install: "11" 69 | test_database: "sqlite" 70 | 71 | # Run Tests on MacOS 72 | - name: "macOS Debug SQLite" 73 | os: macos-latest 74 | build_type: "Debug" 75 | test_database: "sqlite" 76 | cmake_flags: "-DWITH_POSTGRESQL=OFF" 77 | 78 | - name: "macOS Release SQLite" 79 | os: macos-latest 80 | build_type: "Release" 81 | test_database: "sqlite" 82 | cmake_flags: "-DWITH_POSTGRESQL=OFF" 83 | 84 | steps: 85 | - uses: actions/checkout@v3 86 | 87 | - name: Install Boost 88 | if: ${{startsWith(matrix.config.os, 'ubuntu')}} 89 | run: | 90 | echo --- Installing Boost.Regex 91 | sudo apt-get update 92 | sudo apt-get install libboost-regex-dev 93 | 94 | - name: Install Boost 95 | if: ${{startsWith(matrix.config.os, 'macos')}} 96 | run: | 97 | echo --- Installing Boost.Regex 98 | brew install boost 99 | 100 | - name: Install gcc version 101 | if: ${{matrix.config.gcc_install}} 102 | run: | 103 | echo --- Install gcc version ${{matrix.config.gcc_install}} 104 | echo --- gcc version before 105 | gcc --version 106 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 107 | sudo apt-get update 108 | sudo apt-get install gcc-${{matrix.config.gcc_install}} g++-${{matrix.config.gcc_install}} 109 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{matrix.config.gcc_install}} 90 --slave /usr/bin/g++ g++ /usr/bin/g++-${{matrix.config.gcc_install}} 110 | echo --- gcc version after 111 | gcc --version 112 | 113 | - name: Configure CMake 114 | run: > 115 | cmake -B ${{github.workspace}}/build 116 | -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} 117 | -DCMAKE_CXX_FLAGS="${{matrix.config.cxx_flags}}" 118 | -DTEST_DATABASE="${{matrix.config.test_database}}" 119 | -DBUILD_EXAMPLES=ON 120 | ${{matrix.config.cmake_flags}} 121 | 122 | - name: Build 123 | run: cmake --build ${{github.workspace}}/build --config ${{matrix.config.build_type}} 124 | 125 | - name: Set up PostgreSQL 126 | if: ${{startsWith(matrix.config.test_database, 'postgresql')}} 127 | run: | 128 | sudo /etc/init.d/postgresql start 129 | sudo -u postgres psql -c "CREATE USER $USER" 130 | sudo -u postgres psql -c "CREATE DATABASE $USER OWNER $USER;" 131 | 132 | - name: Set up MySQL 133 | if: ${{startsWith(matrix.config.test_database, 'mysql')}} 134 | run: | 135 | sudo /etc/init.d/mysql start 136 | mysql -hlocalhost -uroot -proot -e "CREATE USER $USER" 137 | mysql -hlocalhost -uroot -proot -e "CREATE DATABASE test" 138 | mysql -hlocalhost -uroot -proot -e "GRANT ALL ON test.* TO $USER" 139 | 140 | - name: Test 141 | working-directory: ${{github.workspace}}/build 142 | run: ctest -C ${{matrix.config.build_type}} 143 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # CMakeLists.txt 3 | # 4 | # Build script for sqlplot-tools utility set. 5 | # 6 | ############################################################################### 7 | # Copyright (C) 2013-2014 Timo Bingmann 8 | # 9 | # This program is free software: you can redistribute it and/or modify it under 10 | # the terms of the GNU General Public License as published by the Free Software 11 | # Foundation, either version 3 of the License, or (at your option) any later 12 | # version. 13 | # 14 | # This program is distributed in the hope that it will be useful, but WITHOUT 15 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 | # details. 18 | # 19 | # You should have received a copy of the GNU General Public License along with 20 | # this program. If not, see . 21 | ############################################################################### 22 | 23 | cmake_minimum_required(VERSION 2.8...4.0) 24 | 25 | project(sqlplot-tools) 26 | 27 | # custom cmake scripts 28 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 29 | 30 | # prohibit in-source builds 31 | if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") 32 | message(SEND_ERROR "In-source builds are not allowed.") 33 | endif() 34 | 35 | # default to Debug building for single-config generators 36 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 37 | message("Defaulting CMAKE_BUILD_TYPE to Debug") 38 | set(CMAKE_BUILD_TYPE "Debug") 39 | endif() 40 | 41 | # options to build with various database libraries 42 | option(WITH_POSTGRESQL "Build sqlplot-tools with PostgreSQL interface" ON) 43 | option(WITH_MYSQL "Build sqlplot-tools with MySQL interface" ON) 44 | 45 | # option to update all tests with new output 46 | option(UPDATE_TESTS "Update test output with program output" OFF) 47 | 48 | # option to build examples 49 | option(BUILD_EXAMPLES "Build example programs (for build testing)" OFF) 50 | 51 | # option to change the database system to run tests on 52 | set(TEST_DATABASE "Sqlite" 53 | CACHE STRING "Select the database system to run tests on.") 54 | 55 | # allow user to specify other binary installation paths 56 | set(INSTALL_BIN_DIR "bin" CACHE PATH "Installation directory for sqlplot-tools") 57 | 58 | # enable warnings 59 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall") 60 | 61 | # enable C++11 62 | set(CMAKE_CXX_STANDARD 11) 63 | 64 | # enable use of "make test" 65 | enable_testing() 66 | 67 | ### Find SQL libraries ### 68 | 69 | # Find PostgreSQL client library 70 | 71 | if(WITH_POSTGRESQL) 72 | 73 | set(PostgreSQL_ADDITIONAL_VERSIONS "9.3" "9.2") 74 | 75 | find_package(PostgreSQL REQUIRED) 76 | 77 | if(PostgreSQL_FOUND) 78 | set(SQL_LIBRARIES ${PostgreSQL_LIBRARIES} ${SQL_LIBRARIES}) 79 | add_definitions(-DHAVE_POSTGRESQL=1) 80 | message(STATUS "Found PostgreSQL: ${PostgreSQL_LIBRARIES}") 81 | include_directories(${PostgreSQL_INCLUDE_DIRS}) 82 | endif() 83 | 84 | endif() 85 | 86 | # Find MySQL client library 87 | 88 | if(WITH_MYSQL) 89 | 90 | find_path(MYSQL_INCLUDE_DIRS mysql.h 91 | PATHS /usr/include/mysql /usr/local/include/mysql) 92 | 93 | find_library(MYSQL_LIBRARIES NAMES mysqlclient 94 | PATHS /usr/lib/mysql /usr/local/lib/mysql) 95 | 96 | if(MYSQL_INCLUDE_DIRS AND MYSQL_LIBRARIES) 97 | set(MYSQL_FOUND TRUE) 98 | add_definitions(-DHAVE_MYSQL=1) 99 | message(STATUS "Found MySQL: ${MYSQL_LIBRARIES}") 100 | include_directories(${MYSQL_INCLUDE_DIRS}) 101 | set(SQL_LIBRARIES ${MYSQL_LIBRARIES} ${SQL_LIBRARIES}) 102 | else() 103 | set(MYSQL_FOUND FALSE) 104 | message(STATUS "Could NOT find MySQL library") 105 | endif() 106 | 107 | endif() 108 | 109 | # Find Sqlite3 library 110 | find_path(SQLITE3_INCLUDE_DIRS sqlite3.h 111 | PATHS /usr/include /usr/local/include) 112 | 113 | find_library(SQLITE3_LIBRARIES NAMES sqlite3 114 | PATHS /usr/lib /usr/local/lib) 115 | include_directories(${SQLITE3_INCLUDE_DIRS}) 116 | 117 | if(SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES) 118 | set(SQLITE3_FOUND TRUE) 119 | add_definitions(-DHAVE_SQLITE3=1) 120 | message(STATUS "Found SQLite3: ${SQLITE3_LIBRARIES}") 121 | include_directories(${SQLITE3_INCLUDE_DIRS}) 122 | set(SQL_LIBRARIES ${SQLITE3_LIBRARIES} ${SQL_LIBRARIES}) 123 | else() 124 | set(SQLITE3_FOUND FALSE) 125 | message(SEND_ERROR "Could NOT find SQLite3 library. It is required!") 126 | endif() 127 | 128 | # Use Boost.Regex 129 | find_package(Boost 1.42.0 REQUIRED COMPONENTS regex) 130 | include_directories(${Boost_INCLUDE_DIRS}) 131 | 132 | # descend into source 133 | add_subdirectory(src) 134 | 135 | # descend into testsuite 136 | add_subdirectory(tests) 137 | 138 | # build examples (for testing) 139 | if(BUILD_EXAMPLES) 140 | add_subdirectory(examples/sorting-speed) 141 | add_subdirectory(examples/stats_writer) 142 | endif() 143 | -------------------------------------------------------------------------------- /cmake/FindPostgreSQL.cmake: -------------------------------------------------------------------------------- 1 | # - Find the PostgreSQL installation. 2 | # In Windows, we make the assumption that, if the PostgreSQL files are installed, the default directory 3 | # will be C:\Program Files\PostgreSQL. 4 | # 5 | # This module defines 6 | # PostgreSQL_LIBRARIES - the PostgreSQL libraries needed for linking 7 | # PostgreSQL_INCLUDE_DIRS - the directories of the PostgreSQL headers 8 | # PostgreSQL_VERSION_STRING - the version of PostgreSQL found (since CMake 2.8.8) 9 | 10 | #============================================================================= 11 | # Copyright 2004-2009 Kitware, Inc. 12 | # 13 | # Distributed under the OSI-approved BSD License (the "License"); 14 | # see accompanying file Copyright.txt for details. 15 | # 16 | # This software is distributed WITHOUT ANY WARRANTY; without even the 17 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 18 | # See the License for more information. 19 | #============================================================================= 20 | # (To distribute this file outside of CMake, substitute the full 21 | # License text for the above reference.) 22 | 23 | # ---------------------------------------------------------------------------- 24 | # History: 25 | # This module is derived from the module originally found in the VTK source tree. 26 | # 27 | # ---------------------------------------------------------------------------- 28 | # Note: 29 | # PostgreSQL_ADDITIONAL_VERSIONS is a variable that can be used to set the 30 | # version mumber of the implementation of PostgreSQL. 31 | # In Windows the default installation of PostgreSQL uses that as part of the path. 32 | # E.g C:\Program Files\PostgreSQL\8.4. 33 | # Currently, the following version numbers are known to this module: 34 | # "9.1" "9.0" "8.4" "8.3" "8.2" "8.1" "8.0" 35 | # 36 | # To use this variable just do something like this: 37 | # set(PostgreSQL_ADDITIONAL_VERSIONS "9.2" "8.4.4") 38 | # before calling find_package(PostgreSQL) in your CMakeLists.txt file. 39 | # This will mean that the versions you set here will be found first in the order 40 | # specified before the default ones are searched. 41 | # 42 | # ---------------------------------------------------------------------------- 43 | # You may need to manually set: 44 | # PostgreSQL_INCLUDE_DIR - the path to where the PostgreSQL include files are. 45 | # PostgreSQL_LIBRARY_DIR - The path to where the PostgreSQL library files are. 46 | # If FindPostgreSQL.cmake cannot find the include files or the library files. 47 | # 48 | # ---------------------------------------------------------------------------- 49 | # The following variables are set if PostgreSQL is found: 50 | # PostgreSQL_FOUND - Set to true when PostgreSQL is found. 51 | # PostgreSQL_INCLUDE_DIRS - Include directories for PostgreSQL 52 | # PostgreSQL_LIBRARY_DIRS - Link directories for PostgreSQL libraries 53 | # PostgreSQL_LIBRARIES - The PostgreSQL libraries. 54 | # 55 | # ---------------------------------------------------------------------------- 56 | # If you have installed PostgreSQL in a non-standard location. 57 | # (Please note that in the following comments, it is assumed that 58 | # points to the root directory of the include directory of PostgreSQL.) 59 | # Then you have three options. 60 | # 1) After CMake runs, set PostgreSQL_INCLUDE_DIR to /include and 61 | # PostgreSQL_LIBRARY_DIR to wherever the library pq (or libpq in windows) is 62 | # 2) Use CMAKE_INCLUDE_PATH to set a path to /PostgreSQL<-version>. This will allow find_path() 63 | # to locate PostgreSQL_INCLUDE_DIR by utilizing the PATH_SUFFIXES option. e.g. In your CMakeLists.txt file 64 | # set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "/include") 65 | # 3) Set an environment variable called ${PostgreSQL_ROOT} that points to the root of where you have 66 | # installed PostgreSQL, e.g. . 67 | # 68 | # ---------------------------------------------------------------------------- 69 | 70 | set(PostgreSQL_INCLUDE_PATH_DESCRIPTION "top-level directory containing the PostgreSQL include directories. E.g /usr/local/include/PostgreSQL/8.4 or C:/Program Files/PostgreSQL/8.4/include") 71 | set(PostgreSQL_INCLUDE_DIR_MESSAGE "Set the PostgreSQL_INCLUDE_DIR cmake cache entry to the ${PostgreSQL_INCLUDE_PATH_DESCRIPTION}") 72 | set(PostgreSQL_LIBRARY_PATH_DESCRIPTION "top-level directory containing the PostgreSQL libraries.") 73 | set(PostgreSQL_LIBRARY_DIR_MESSAGE "Set the PostgreSQL_LIBRARY_DIR cmake cache entry to the ${PostgreSQL_LIBRARY_PATH_DESCRIPTION}") 74 | set(PostgreSQL_ROOT_DIR_MESSAGE "Set the PostgreSQL_ROOT system variable to where PostgreSQL is found on the machine E.g C:/Program Files/PostgreSQL/8.4") 75 | 76 | 77 | set(PostgreSQL_KNOWN_VERSIONS ${PostgreSQL_ADDITIONAL_VERSIONS} 78 | "9.1" "9.0" "8.4" "8.3" "8.2" "8.1" "8.0") 79 | 80 | # Define additional search paths for root directories. 81 | if ( WIN32 ) 82 | foreach (suffix ${PostgreSQL_KNOWN_VERSIONS} ) 83 | set(PostgreSQL_ADDITIONAL_SEARCH_PATHS ${PostgreSQL_ADDITIONAL_SEARCH_PATHS} "C:/Program Files/PostgreSQL/${suffix}" ) 84 | endforeach() 85 | endif() 86 | set( PostgreSQL_ROOT_DIRECTORIES 87 | ENV PostgreSQL_ROOT 88 | ${PostgreSQL_ROOT} 89 | ${PostgreSQL_ADDITIONAL_SEARCH_PATHS} 90 | ) 91 | 92 | # 93 | # Look for an installation. 94 | # 95 | find_path(PostgreSQL_INCLUDE_DIR 96 | NAMES libpq-fe.h 97 | PATHS 98 | # Look in other places. 99 | ${PostgreSQL_ROOT_DIRECTORIES} 100 | PATH_SUFFIXES 101 | pgsql 102 | postgresql 103 | include 104 | # Help the user find it if we cannot. 105 | DOC "The ${PostgreSQL_INCLUDE_DIR_MESSAGE}" 106 | ) 107 | 108 | # The PostgreSQL library. 109 | set (PostgreSQL_LIBRARY_TO_FIND pq) 110 | # Setting some more prefixes for the library 111 | set (PostgreSQL_LIB_PREFIX "") 112 | if ( FALSE AND WIN32 ) 113 | set (PostgreSQL_LIB_PREFIX ${PostgreSQL_LIB_PREFIX} "lib") 114 | set ( PostgreSQL_LIBRARY_TO_FIND ${PostgreSQL_LIB_PREFIX}${PostgreSQL_LIBRARY_TO_FIND}) 115 | endif() 116 | 117 | find_library( PostgreSQL_LIBRARY 118 | NAMES ${PostgreSQL_LIBRARY_TO_FIND} 119 | PATHS 120 | ${PostgreSQL_ROOT_DIRECTORIES} 121 | PATH_SUFFIXES 122 | lib 123 | ) 124 | get_filename_component(PostgreSQL_LIBRARY_DIR ${PostgreSQL_LIBRARY} PATH) 125 | 126 | if ( MINGW ) 127 | 128 | find_library(SSL_LIBRARY 129 | NAMES ssl 130 | PATHS ${PostgreSQL_ROOT_DIRECTORIES} 131 | PATH_SUFFIXES lib) 132 | set(PostgreSQL_LIBRARY ${PostgreSQL_LIBRARY} ${SSL_LIBRARY}) 133 | 134 | find_library(CRYPTO_LIBRARY 135 | NAMES crypto 136 | PATHS ${PostgreSQL_ROOT_DIRECTORIES} 137 | PATH_SUFFIXES lib) 138 | set(PostgreSQL_LIBRARY ${PostgreSQL_LIBRARY} ${CRYPTO_LIBRARY}) 139 | 140 | find_library(ZLIB_LIBRARY 141 | NAMES z 142 | PATHS ${PostgreSQL_ROOT_DIRECTORIES} 143 | PATH_SUFFIXESlib) 144 | set(PostgreSQL_LIBRARY ${PostgreSQL_LIBRARY} ${ZLIB_LIBRARY}) 145 | 146 | find_library(WS2_32_LIBRARY 147 | NAMES ws2_32 148 | PATHS ${PostgreSQL_ROOT_DIRECTORIES} 149 | PATH_SUFFIXES lib) 150 | set(PostgreSQL_LIBRARY ${PostgreSQL_LIBRARY} ${WS2_32_LIBRARY}) 151 | 152 | find_library(SECUR32_LIBRARY 153 | NAMES secur32 154 | PATHS ${PostgreSQL_ROOT_DIRECTORIES} 155 | PATH_SUFFIXES lib) 156 | set(PostgreSQL_LIBRARY ${PostgreSQL_LIBRARY} ${SECUR32_LIBRARY}) 157 | 158 | endif() 159 | 160 | if (PostgreSQL_INCLUDE_DIR AND EXISTS "${PostgreSQL_INCLUDE_DIR}/pg_config.h") 161 | file(STRINGS "${PostgreSQL_INCLUDE_DIR}/pg_config.h" pgsql_version_str 162 | REGEX "^#define[\t ]+PG_VERSION[\t ]+\".*\"") 163 | 164 | string(REGEX REPLACE "^#define[\t ]+PG_VERSION[\t ]+\"([^\"]*)\".*" "\\1" 165 | PostgreSQL_VERSION_STRING "${pgsql_version_str}") 166 | unset(pgsql_version_str) 167 | endif() 168 | 169 | # Did we find anything? 170 | include(FindPackageHandleStandardArgs) 171 | find_package_handle_standard_args(PostgreSQL 172 | REQUIRED_VARS PostgreSQL_LIBRARY) 173 | set(PostgreSQL_FOUND ${POSTGRESQL_FOUND}) 174 | 175 | # Now try to get the include and library path. 176 | if(PostgreSQL_FOUND) 177 | 178 | set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR}) 179 | set(PostgreSQL_LIBRARY_DIRS ${PostgreSQL_LIBRARY_DIR}) 180 | set(PostgreSQL_LIBRARIES ${PostgreSQL_LIBRARY}) 181 | 182 | #message("Final PostgreSQL include dir: ${PostgreSQL_INCLUDE_DIRS}") 183 | #message("Final PostgreSQL library dir: ${PostgreSQL_LIBRARY_DIRS}") 184 | #message("Final PostgreSQL libraries: ${PostgreSQL_LIBRARIES}") 185 | endif() 186 | 187 | mark_as_advanced(PostgreSQL_INCLUDE_DIRS PostgreSQL_LIBRARIES) 188 | -------------------------------------------------------------------------------- /examples/sorting-speed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # examples/sorting-speed/CMakeLists.txt 3 | # 4 | # CMake file for example in sqlplot-tools utility set. 5 | # 6 | ############################################################################### 7 | # Copyright (C) 2014 Timo Bingmann 8 | # 9 | # This program is free software: you can redistribute it and/or modify it under 10 | # the terms of the GNU General Public License as published by the Free Software 11 | # Foundation, either version 3 of the License, or (at your option) any later 12 | # version. 13 | # 14 | # This program is distributed in the hope that it will be useful, but WITHOUT 15 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 | # details. 18 | # 19 | # You should have received a copy of the GNU General Public License along with 20 | # this program. If not, see . 21 | ############################################################################### 22 | 23 | add_executable(sorting-speed sorting-speed.cpp) -------------------------------------------------------------------------------- /examples/sorting-speed/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingmann/sqlplot-tools/a0c3ba6f1f5a04ee9dca7a291ec71478cff39891/examples/sorting-speed/paper.pdf -------------------------------------------------------------------------------- /examples/sorting-speed/paper.tex: -------------------------------------------------------------------------------- 1 | % -*- mode: latex; mode: flyspell; ispell-local-dictionary: "en_US"; coding: utf-8; fill-column: 80 -*- 2 | 3 | \documentclass{article} 4 | 5 | \usepackage[utf8]{inputenc} 6 | \usepackage[english]{babel} 7 | 8 | \usepackage{amsmath,amsfonts,amssymb} 9 | \usepackage{fullpage} 10 | \usepackage{verbatim} 11 | 12 | \usepackage{tikz,pgfplots} 13 | 14 | \pgfplotsset{ 15 | width=150mm,height=100mm, 16 | major grid style={thin,dotted,color=black!50}, 17 | minor grid style={thin,dotted,color=black!50}, 18 | grid, 19 | every axis/.append style={ 20 | line width=0.5pt, 21 | tick style={ 22 | line cap=round, 23 | thin, 24 | major tick length=4pt, 25 | minor tick length=2pt, 26 | }, 27 | }, 28 | legend cell align=left, 29 | legend pos=north west, 30 | } 31 | 32 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | 34 | \begin{document} 35 | 36 | \title{Sorting Speed Example} 37 | \author{Timo Bingmann} 38 | \maketitle 39 | 40 | % IMPORT-DATA stats stats.txt 41 | 42 | \begin{center} 43 | \begin{tikzpicture} 44 | \begin{axis}[ 45 | title={Simple C++ Sorting Test}, 46 | xlabel={Item Count [$\log_2(n)$]}, 47 | ylabel={Run Time per Item [Nanoseconds / Item]}, 48 | ] 49 | 50 | %% MULTIPLOT(algo) SELECT LOG(2, size) AS x, MEDIAN(time / repeats / size * 1e9) AS y, MULTIPLOT 51 | %% FROM stats GROUP BY MULTIPLOT,x ORDER BY MULTIPLOT,x 52 | \addplot coordinates { (10,71.6615) (11,77.5939) (12,83.8065) (13,89.5691) (14,95.9241) (15,102.562) (16,109.495) (17,118.608) (18,129.266) (19,139.672) (20,151.737) (21,168.85) (22,199.766) (23,237.565) (24,274.479) (25,311.786) (26,352.004) (27,398.075) (28,452.284) (29,514.045) (30,581.396) }; 53 | \addlegendentry{algo=std::heap\_sort}; 54 | \addplot coordinates { (10,37.9929) (11,42.9001) (12,47.8324) (13,51.2096) (14,55.5614) (15,59.3466) (16,63.2313) (17,67.0138) (18,70.8553) (19,74.6909) (20,78.6448) (21,82.4893) (22,86.4092) (23,90.1377) (24,93.8964) (25,98.3199) (26,102.219) (27,106.035) (28,109.866) (29,113.705) (30,117.649) }; 55 | \addlegendentry{algo=std::sort}; 56 | \addplot coordinates { (10,38.0611) (11,41.7566) (12,45.1556) (13,48.2661) (14,52.3302) (15,55.3477) (16,58.915) (17,62.1703) (18,65.7791) (19,68.9638) (20,73.0819) (21,77.2482) (22,82.1805) (23,85.7273) (24,89.7738) (25,92.6328) (26,96.7431) (27,99.6031) (28,103.699) (29,106.563) (30,110.559) }; 57 | \addlegendentry{algo=std::stable\_sort}; 58 | 59 | \end{axis} 60 | \end{tikzpicture} 61 | \end{center} 62 | 63 | \begin{table}\centering 64 | \begin{tabular}{l|rrr} 65 | $n$ & \texttt{std::sort} & \texttt{std::stable\_sort} & STL heap sort \\ \hline 66 | %% TABULAR REFORMAT(col 1-3=(precision=1) row 0-100=(min=bold)) 67 | %% SELECT '$2^{' || FLOOR(LOG(2, size)) || '}$' AS x, 68 | %% (SELECT MEDIAN(time / repeats / size * 1e9) FROM stats s1 WHERE s1.algo='std::sort' AND s1.size = s.size GROUP BY s1.size), 69 | %% (SELECT MEDIAN(time / repeats / size * 1e9) FROM stats s1 WHERE s1.algo='std::stable_sort' AND s1.size = s.size GROUP BY s1.size), 70 | %% (SELECT MEDIAN(time / repeats / size * 1e9) FROM stats s1 WHERE s1.algo='std::heap_sort' AND s1.size = s.size GROUP BY s1.size) 71 | %% FROM stats s 72 | %% GROUP BY s.size ORDER BY s.size 73 | $2^{10}$ & \bf 38.0 & 38.1 & 71.7 \\ 74 | $2^{11}$ & 42.9 & \bf 41.8 & 77.6 \\ 75 | $2^{12}$ & 47.8 & \bf 45.2 & 83.8 \\ 76 | $2^{13}$ & 51.2 & \bf 48.3 & 89.6 \\ 77 | $2^{14}$ & 55.6 & \bf 52.3 & 95.9 \\ 78 | $2^{15}$ & 59.3 & \bf 55.3 & 102.6 \\ 79 | $2^{16}$ & 63.2 & \bf 58.9 & 109.5 \\ 80 | $2^{17}$ & 67.0 & \bf 62.2 & 118.6 \\ 81 | $2^{18}$ & 70.9 & \bf 65.8 & 129.3 \\ 82 | $2^{19}$ & 74.7 & \bf 69.0 & 139.7 \\ 83 | $2^{20}$ & 78.6 & \bf 73.1 & 151.7 \\ 84 | $2^{21}$ & 82.5 & \bf 77.2 & 168.9 \\ 85 | $2^{22}$ & 86.4 & \bf 82.2 & 199.8 \\ 86 | $2^{23}$ & 90.1 & \bf 85.7 & 237.6 \\ 87 | $2^{24}$ & 93.9 & \bf 89.8 & 274.5 \\ 88 | $2^{25}$ & 98.3 & \bf 92.6 & 311.8 \\ 89 | $2^{26}$ & 102.2 & \bf 96.7 & 352.0 \\ 90 | $2^{27}$ & 106.0 & \bf 99.6 & 398.1 \\ 91 | $2^{28}$ & 109.9 & \bf 103.7 & 452.3 \\ 92 | $2^{29}$ & 113.7 & \bf 106.6 & 514.0 \\ 93 | $2^{30}$ & 117.6 & \bf 110.6 & 581.4 \\ 94 | % END TABULAR SELECT '$2^{' || FLOOR(LOG(2, size)) || '}$' AS x, (SELECT MEDI... 95 | \end{tabular} 96 | \caption{Runtime per element for different sorting implementation, median of 15 runs.} 97 | \end{table} 98 | 99 | \begin{comment} 100 | % TEXTTABLE SELECT COUNT(*), SUM(time) FROM stats 101 | +-------+-------------+ 102 | | count | sum | 103 | +-------+-------------+ 104 | | 945 | 25661.41392 | 105 | +-------+-------------+ 106 | % END TEXTTABLE SELECT COUNT(*), SUM(time) FROM stats 107 | \end{comment} 108 | 109 | \end{document} 110 | 111 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 112 | -------------------------------------------------------------------------------- /examples/sorting-speed/sorting-speed.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * examples/sorting-speed/sorting-speed.cpp 3 | * 4 | * Very simple experiment to measure the speed of std::sort, std::stable_sort, 5 | * and STL's heapsort implementations on a random integer permutation. 6 | * 7 | ****************************************************************************** 8 | * Copyright (C) 2014 Timo Bingmann 9 | * 10 | * This program is free software: you can redistribute it and/or modify it 11 | * under the terms of the GNU General Public License as published by the Free 12 | * Software Foundation, either version 3 of the License, or (at your option) 13 | * any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, but WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | * more details. 19 | * 20 | * You should have received a copy of the GNU General Public License along with 21 | * this program. If not, see . 22 | *****************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | //! minimum total item count sorted per experiment -> increase for faster 30 | //! machines. 31 | const size_t test_volume = 32*1024*1024; 32 | 33 | //! smallest item count to test 34 | const size_t size_min = 1024; 35 | 36 | //! largest item count to test 37 | const size_t size_max = 1024*1024*1024; 38 | 39 | //! number of iterations of each test size 40 | const size_t iterations = 15; 41 | 42 | //! item type 43 | typedef unsigned int item_type; 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | void test_std_sort(item_type* array, size_t n) 48 | { 49 | std::sort(array, array + n); 50 | } 51 | 52 | void test_std_stable_sort(item_type* array, size_t n) 53 | { 54 | std::stable_sort(array, array + n); 55 | } 56 | 57 | void test_std_heap_sort(item_type* array, size_t n) 58 | { 59 | std::make_heap(array, array + n); 60 | std::sort_heap(array, array + n); 61 | } 62 | 63 | //////////////////////////////////////////////////////////////////////////////// 64 | 65 | //! timestamp 66 | double timestamp() 67 | { 68 | struct timeval tv; 69 | gettimeofday(&tv, NULL); 70 | return tv.tv_sec + tv.tv_usec / 1e6; 71 | } 72 | 73 | //! the test framework routine 74 | template 75 | void run_test(const std::string& algoname) 76 | { 77 | for (size_t size = size_min; size <= size_max; size *= 2) 78 | { 79 | size_t repeats = test_volume / size; 80 | if (repeats == 0) repeats = 1; 81 | 82 | std::cout << "Running algorithm " << algoname 83 | << " with size=" << size << " repeats=" << repeats << "\n"; 84 | 85 | for (size_t iter = 0; iter < iterations; ++iter) 86 | { 87 | std::cout << "iteration=" << iter << "\n"; 88 | 89 | item_type* array = new item_type[size]; 90 | 91 | for (size_t i = 0; i < size; ++i) 92 | array[i] = i / 4; 93 | 94 | std::random_shuffle(array, array + size); 95 | 96 | double ts1, ts2; 97 | 98 | if (repeats == 1) 99 | { 100 | ts1 = timestamp(); 101 | 102 | test(array, size); 103 | 104 | ts2 = timestamp(); 105 | } 106 | else 107 | { 108 | item_type* arraycopy = new item_type[size]; 109 | std::copy(array, array + size, arraycopy); 110 | 111 | ts1 = timestamp(); 112 | 113 | for (size_t r = 0; r < repeats; ++r) 114 | { 115 | // copy in new version of random permutation 116 | std::copy(arraycopy, arraycopy + size, array); 117 | 118 | test(array, size); 119 | } 120 | 121 | ts2 = timestamp(); 122 | 123 | // measure time of copying data 124 | for (size_t r = 0; r < repeats; ++r) 125 | { 126 | size_t rx = r % size; 127 | std::copy(arraycopy, arraycopy + size - rx, array + rx); 128 | } 129 | 130 | double ts3 = timestamp(); 131 | 132 | ts2 -= ts3 - ts2; 133 | 134 | volatile item_type x = array[size-1]; // anti-optimization 135 | x = x + 1; 136 | 137 | delete [] arraycopy; 138 | } 139 | 140 | std::cout << "time = " << ts2 - ts1 << std::endl; 141 | 142 | delete [] array; 143 | 144 | std::cout << "RESULT" 145 | << " algo=" << algoname 146 | << " size=" << size 147 | << " time=" << ts2 - ts1 148 | << " repeats=" << repeats 149 | << " iteration=" << iter 150 | << " typesize=" << sizeof(item_type) 151 | << " datasize=" << size * sizeof(item_type) 152 | << std::endl; 153 | } 154 | } 155 | } 156 | 157 | //////////////////////////////////////////////////////////////////////////////// 158 | 159 | int main() 160 | { 161 | run_test("std::sort"); 162 | run_test("std::stable_sort"); 163 | run_test("std::heap_sort"); 164 | 165 | return 0; 166 | } 167 | 168 | //////////////////////////////////////////////////////////////////////////////// 169 | -------------------------------------------------------------------------------- /examples/sorting-speed/speed-data.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # 3 | # DO NOT EDIT THIS FILE MANUALLY! 4 | # ALL CHANGES WILL BE LOST WHEN RECREATED! 5 | # 6 | # The data in this file was generated by sqlplot-tools 7 | # by processing "speed.plot". 8 | # 9 | ################################################################################ 10 | 11 | ################################################################################ 12 | # MULTIPLOT(algo) SELECT LOG(2, size) AS x, MEDIAN(time / repeats / size * 1e9) AS y, MULTIPLOT FROM stats GROUP BY MULTIPLOT,x ORDER BY MULTIPLOT,x 13 | # 14 | # index 0 algo=std::heap_sort 15 | 10.0000000000000000 71.6614723205566 16 | 11.0000000000000000 77.5939226150513 17 | 12.0000000000000000 83.8065147399902 18 | 13.0000000000000000 89.569091796875 19 | 14.0000000000000000 95.9241390228271 20 | 15.0000000000000000 102.562010288239 21 | 16.0000000000000000 109.494626522064 22 | 17.0000000000000000 118.607580661774 23 | 18.0000000000000000 129.266381263733 24 | 19.0000000000000000 139.672458171844 25 | 20.0000000000000000 151.737034320831 26 | 21.0000000000000000 168.850123882294 27 | 22.0000000000000000 199.766457080841 28 | 23.0000000000000000 237.565040588379 29 | 24.0000000000000000 274.478793144226 30 | 25.0000000000000000 311.785936355591 31 | 26.0000000000000000 352.004170417786 32 | 27.0000000000000000 398.074835538864 33 | 28.0000000000000000 452.283769845963 34 | 29.0000000000000000 514.045357704163 35 | 30.0000000000000000 581.39581233263 36 | 37 | 38 | # index 1 algo=std::sort 39 | 10.0000000000000000 37.9928946495056 40 | 11.0000000000000000 42.9001450538635 41 | 12.0000000000000000 47.8324294090271 42 | 13.0000000000000000 51.2096285820007 43 | 14.0000000000000000 55.561363697052 44 | 15.0000000000000000 59.3465566635132 45 | 16.0000000000000000 63.2312893867493 46 | 17.0000000000000000 67.0138001441956 47 | 18.0000000000000000 70.8553194999695 48 | 19.0000000000000000 74.6908783912659 49 | 20.0000000000000000 78.6447525024414 50 | 21.0000000000000000 82.4892520904541 51 | 22.0000000000000000 86.4091515541077 52 | 23.0000000000000000 90.1377201080322 53 | 24.0000000000000000 93.8963890075684 54 | 25.0000000000000000 98.319947719574 55 | 26.0000000000000000 102.21928358078 56 | 27.0000000000000000 106.035172939301 57 | 28.0000000000000000 109.865516424179 58 | 29.0000000000000000 113.705173134804 59 | 30.0000000000000000 117.6493242383 60 | 61 | 62 | # index 2 algo=std::stable_sort 63 | 10.0000000000000000 38.0611419677734 64 | 11.0000000000000000 41.7566299438477 65 | 12.0000000000000000 45.1555848121643 66 | 13.0000000000000000 48.2660531997681 67 | 14.0000000000000000 52.3301959037781 68 | 15.0000000000000000 55.3476810455322 69 | 16.0000000000000000 58.9150190353394 70 | 17.0000000000000000 62.1703267097473 71 | 18.0000000000000000 65.7790899276733 72 | 19.0000000000000000 68.9637660980225 73 | 20.0000000000000000 73.0818510055542 74 | 21.0000000000000000 77.248215675354 75 | 22.0000000000000000 82.1805000305176 76 | 23.0000000000000000 85.7272744178772 77 | 24.0000000000000000 89.7738337516785 78 | 25.0000000000000000 92.6327705383301 79 | 26.0000000000000000 96.743106842041 80 | 27.0000000000000000 99.6030867099762 81 | 28.0000000000000000 103.699043393135 82 | 29.0000000000000000 106.562674045563 83 | 30.0000000000000000 110.559165477753 84 | 85 | 86 | -------------------------------------------------------------------------------- /examples/sorting-speed/speed.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bingmann/sqlplot-tools/a0c3ba6f1f5a04ee9dca7a291ec71478cff39891/examples/sorting-speed/speed.pdf -------------------------------------------------------------------------------- /examples/sorting-speed/speed.plot: -------------------------------------------------------------------------------- 1 | 2 | # IMPORT-DATA stats stats.txt 3 | 4 | set terminal pdf size 28cm,18cm linewidth 2.0 5 | set output "speed.pdf" 6 | 7 | set pointsize 0.7 8 | set style line 6 lc rgb "#f0b000" 9 | set style line 15 lc rgb "#f0b000" 10 | set style line 24 lc rgb "#f0b000" 11 | set style line 33 lc rgb "#f0b000" 12 | set style line 42 lc rgb "#f0b000" 13 | set style line 51 lc rgb "#f0b000" 14 | set style line 60 lc rgb "#f0b000" 15 | set style increment user 16 | 17 | set grid xtics ytics 18 | 19 | set key top left 20 | 21 | set title 'Simple C++ Sorting Test' 22 | set xlabel 'Item Count [log_2(n)]' 23 | set ylabel 'Run Time per Item [Nanoseconds / Item]' 24 | 25 | ## MULTIPLOT(algo) SELECT LOG(2, size) AS x, MEDIAN(time / repeats / size * 1e9) AS y, MULTIPLOT 26 | ## FROM stats GROUP BY MULTIPLOT,x ORDER BY MULTIPLOT,x 27 | plot \ 28 | 'speed-data.txt' index 0 title "algo=std::heap_sort" with linespoints, \ 29 | 'speed-data.txt' index 1 title "algo=std::sort" with linespoints, \ 30 | 'speed-data.txt' index 2 title "algo=std::stable_sort" with linespoints 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/stats_writer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # examples/stats_writer/CMakeLists.txt 3 | # 4 | # CMake file for example in sqlplot-tools utility set. 5 | # 6 | ############################################################################### 7 | # Copyright (C) 2014 Timo Bingmann 8 | # 9 | # This program is free software: you can redistribute it and/or modify it under 10 | # the terms of the GNU General Public License as published by the Free Software 11 | # Foundation, either version 3 of the License, or (at your option) any later 12 | # version. 13 | # 14 | # This program is distributed in the hope that it will be useful, but WITHOUT 15 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 | # details. 18 | # 19 | # You should have received a copy of the GNU General Public License along with 20 | # this program. If not, see . 21 | ############################################################################### 22 | 23 | add_executable(stats_writer_test test.cpp) 24 | -------------------------------------------------------------------------------- /examples/stats_writer/stats_writer.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * examples/stats_writer/stats_writer.h 3 | * 4 | * Class to collect and output statistics as key=value pairs. 5 | * 6 | * The usual method to use this stats collector is to make a global object 7 | * stats_writer g_stats, which is filled by algorithms using sequences as. 8 | * 9 | * g_stats >> "key" << "value " << 42; 10 | * 11 | * After the program was run, the stats are formatted as a RESULT line using 12 | * get(), which can be outputted to a file or stdout. 13 | * 14 | ****************************************************************************** 15 | * Copyright (C) 2012-2014 Timo Bingmann 16 | * 17 | * This program is free software: you can redistribute it and/or modify it 18 | * under the terms of the GNU General Public License as published by the Free 19 | * Software Foundation, either version 3 of the License, or (at your option) 20 | * any later version. 21 | * 22 | * This program is distributed in the hope that it will be useful, but WITHOUT 23 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 24 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 25 | * more details. 26 | * 27 | * You should have received a copy of the GNU General Public License along with 28 | * this program. If not, see . 29 | *****************************************************************************/ 30 | 31 | #ifndef SQLPLOTS_STATS_WRITER_H 32 | #define SQLPLOTS_STATS_WRITER_H 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | /*! 41 | * Collect key=value pairs, which are given by operator >> and operator << 42 | * sequences. After all stats are set, the final output line can be fetched. 43 | */ 44 | class stats_writer 45 | { 46 | protected: 47 | 48 | //! All collected key=value values. 49 | std::ostringstream m_line; 50 | 51 | //! An internal class to collect key=value pairs as a sequence of >> and << 52 | //! operator calls. 53 | class entry 54 | { 55 | protected: 56 | //! Reference to parent stats writer object 57 | class stats_writer& m_sw; 58 | 59 | //! Collected key and value string items 60 | std::string m_key, m_value; 61 | 62 | public: 63 | 64 | //! Start entry collection for the given key 65 | entry(stats_writer& sw, const std::string& key) 66 | : m_sw(sw), m_key(key) 67 | { } 68 | 69 | //! Collect more information about the value 70 | entry& operator << (const std::string& v) 71 | { 72 | m_value += v; 73 | return *this; 74 | } 75 | 76 | //! Collect more information about the value 77 | template 78 | entry& operator << (const ValueType& v) 79 | { 80 | std::ostringstream vstr; 81 | vstr << v; 82 | return operator << (vstr.str()); 83 | } 84 | 85 | //! Start another key= entry via parent 86 | template 87 | entry operator >> (const ValueType& v) 88 | { 89 | // put key=value into writer before returning next entry 90 | m_sw.put(m_key, m_value); 91 | m_key.clear(); m_value.clear(); 92 | return m_sw.operator >> (v); 93 | } 94 | 95 | //! Output key=value to stats writer for the last entry 96 | ~entry() 97 | { 98 | if (m_key.size() || m_value.size()) 99 | m_sw.put(m_key, m_value); 100 | } 101 | }; 102 | 103 | public: 104 | 105 | //! Clear all data in the stats writer. 106 | void clear() 107 | { 108 | m_line.str(""); 109 | } 110 | 111 | //! Append a (key,value) pair as ">> key << value << more" 112 | entry operator >> (const std::string& k) 113 | { 114 | return entry(*this, k); 115 | } 116 | 117 | //! Append a (key,value) pair as ">> key << value << more" 118 | template 119 | entry operator >> (const KeyType& k) 120 | { 121 | std::ostringstream kstr; 122 | kstr << k; 123 | return operator >> (kstr.str()); 124 | } 125 | 126 | //! Append a (key,value) pair as strings 127 | stats_writer& put(const std::string& k, const std::string& v) 128 | { 129 | #if _OPENMP 130 | #pragma omp critical 131 | #endif 132 | m_line << '\t' << k << '=' << v; 133 | return *this; 134 | } 135 | 136 | //! Append a (key,value) pair with automatic conversion to strings 137 | template 138 | stats_writer& put(const KeyType& k, const ValueType& v) 139 | { 140 | std::ostringstream kstr, vstr; 141 | kstr << k; vstr << v; 142 | return put(kstr.str(), vstr.str()); 143 | } 144 | 145 | //! Return RESULT string for outputting. 146 | std::string get() const 147 | { 148 | std::ostringstream out; 149 | out << "RESULT"; 150 | 151 | // output date, time and hostname 152 | 153 | char datetime[64]; 154 | time_t tnow = time(NULL); 155 | 156 | strftime(datetime,sizeof(datetime),"%Y-%m-%d %H:%M:%S", localtime(&tnow)); 157 | out << "\tdatetime=" << datetime; 158 | 159 | char hostname[128]; 160 | gethostname(hostname, sizeof(hostname)); 161 | 162 | out << "\thost=" << hostname; 163 | 164 | // output collected key=values 165 | 166 | out << m_line.str(); 167 | 168 | return out.str(); 169 | } 170 | 171 | //! Return RESULT string for outputting. 172 | friend std::ostream& operator << (std::ostream& os, const stats_writer& sw) 173 | { 174 | return os << sw.get(); 175 | } 176 | }; 177 | 178 | #endif // SQLPLOTS_STATS_WRITER_H 179 | -------------------------------------------------------------------------------- /examples/stats_writer/test.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * examples/stats_writer/test.cc 3 | * 4 | * Example for stats_writer 5 | * 6 | ******************************************************************************* 7 | * Copyright (C) 2014 Timo Bingmann 8 | * 9 | * This program is free software: you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation, either version 3 of the License, or (at your option) 12 | * any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | * more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | ******************************************************************************/ 22 | 23 | #include "stats_writer.h" 24 | 25 | #include 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | stats_writer sw; 30 | 31 | sw >> "keyA" << "value " << 5 32 | >> "keyB" << 42 33 | >> "keyC" << 101.5 34 | >> "argc" << argc 35 | >> "argv[0]" << argv[0]; 36 | 37 | std::cout << sw; 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # src/CMakeLists.txt 3 | # 4 | # CMake file for sqlplot-tools utility set. 5 | # 6 | ############################################################################### 7 | # Copyright (C) 2013-2015 Timo Bingmann 8 | # 9 | # This program is free software: you can redistribute it and/or modify it under 10 | # the terms of the GNU General Public License as published by the Free Software 11 | # Foundation, either version 3 of the License, or (at your option) any later 12 | # version. 13 | # 14 | # This program is distributed in the hope that it will be useful, but WITHOUT 15 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 | # details. 18 | # 19 | # You should have received a copy of the GNU General Public License along with 20 | # this program. If not, see . 21 | ############################################################################### 22 | 23 | set(SQL_SOURCES "") 24 | 25 | if(PostgreSQL_FOUND) 26 | set(SQL_SOURCES ${SQL_SOURCES} pgsql.cpp) 27 | endif() 28 | 29 | if(MYSQL_FOUND) 30 | set(SQL_SOURCES ${SQL_SOURCES} mysql.cpp) 31 | endif() 32 | 33 | add_executable(sqlplot-tools 34 | main.cpp 35 | latex.cpp 36 | gnuplot.cpp 37 | common.cpp 38 | sql.cpp 39 | sqlite.cpp 40 | sqlite-functions.cpp 41 | ${SQL_SOURCES} 42 | importdata.cpp 43 | fieldset.cpp 44 | reformat.cpp 45 | ) 46 | 47 | target_link_libraries(sqlplot-tools ${SQL_LIBRARIES} ${Boost_LIBRARIES}) 48 | 49 | install(TARGETS sqlplot-tools RUNTIME DESTINATION ${INSTALL_BIN_DIR}) 50 | 51 | ################################################################################ 52 | -------------------------------------------------------------------------------- /src/common.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/common.cpp 3 | * 4 | * Common global variables across all programs. 5 | * 6 | ****************************************************************************** 7 | * Copyright (C) 2013-2016 Timo Bingmann 8 | * 9 | * This program is free software: you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation, either version 3 of the License, or (at your option) 12 | * any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | * more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | *****************************************************************************/ 22 | 23 | #include "common.h" 24 | #include "strtools.h" 25 | 26 | //! verbosity, common global option. 27 | int gopt_verbose = 0; 28 | 29 | //! check processed output matches the output file 30 | bool gopt_check_output = false; 31 | 32 | //! global command line parameter: named RANGEs to process 33 | std::vector gopt_ranges; 34 | 35 | //! global SQL datbase connection handle 36 | SqlDatabase* g_db = NULL; 37 | 38 | #include "pgsql.h" 39 | #include "mysql.h" 40 | #include "sqlite.h" 41 | 42 | //! initialize global SQL database connection 43 | bool g_db_connect(const std::string& db_conninfo) 44 | { 45 | g_db_free(); 46 | 47 | if (db_conninfo.size() == 0) 48 | { 49 | #if HAVE_POSTGRESQL 50 | //! first try to connect to a PostgreSQL database 51 | 52 | g_db = new PgSqlDatabase; 53 | if (g_db->initialize("")) 54 | return true; 55 | delete g_db; 56 | #endif 57 | #if HAVE_MYSQL 58 | //! then try to connect to a MySQL database called "test" 59 | 60 | g_db = new MySqlDatabase(); 61 | if (g_db->initialize("test")) 62 | return true; 63 | delete g_db; 64 | #endif 65 | #if HAVE_SQLITE3 66 | //! then try to connect to an in-memory SQLite database 67 | 68 | g_db = new SQLiteDatabase(); 69 | if (g_db->initialize(":memory:")) 70 | return true; 71 | delete g_db; 72 | #endif 73 | } 74 | else 75 | { 76 | std::string sqlname = db_conninfo; 77 | std::string dbname; 78 | 79 | std::string::size_type colonpos = sqlname.find(':'); 80 | if (colonpos != std::string::npos) { 81 | dbname = sqlname.substr(colonpos+1); 82 | sqlname = sqlname.substr(0, colonpos); 83 | } 84 | 85 | sqlname = str_tolower(sqlname); 86 | 87 | if (0) 88 | { 89 | } 90 | #if HAVE_POSTGRESQL 91 | else if (sqlname == "postgresql" || sqlname == "postgres" || 92 | sqlname == "pgsql" || sqlname == "pg") 93 | { 94 | g_db = new PgSqlDatabase; 95 | if (g_db->initialize(dbname)) 96 | return true; 97 | delete g_db; 98 | } 99 | #endif 100 | #if HAVE_MYSQL 101 | else if (sqlname == "mysql" || sqlname == "my") 102 | { 103 | if (dbname.size() == 0) dbname = "test"; 104 | 105 | g_db = new MySqlDatabase; 106 | if (g_db->initialize(dbname)) 107 | return true; 108 | delete g_db; 109 | } 110 | #endif 111 | #if HAVE_SQLITE3 112 | else if (sqlname == "sqlite" || sqlname == "lite") 113 | { 114 | if (dbname.size() == 0) dbname = ":memory:"; 115 | 116 | g_db = new SQLiteDatabase; 117 | if (g_db->initialize(dbname)) 118 | return true; 119 | delete g_db; 120 | } 121 | #endif 122 | else 123 | { 124 | OUT("ERROR: unknown (or not compiled) SQL database type \"" << 125 | sqlname << "\"!"); 126 | } 127 | } 128 | 129 | return false; 130 | } 131 | 132 | //! free global SQL database connection 133 | void g_db_free() 134 | { 135 | if (g_db) { 136 | delete g_db; 137 | g_db = NULL; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/common.h 3 | * 4 | * Common global variables across all programs. 5 | * 6 | ****************************************************************************** 7 | * Copyright (C) 2013-2016 Timo Bingmann 8 | * 9 | * This program is free software: you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation, either version 3 of the License, or (at your option) 12 | * any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | * more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | *****************************************************************************/ 22 | 23 | #ifndef COMMON_HEADER 24 | #define COMMON_HEADER 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "sql.h" 33 | 34 | //! verbosity, common global option. 35 | extern int gopt_verbose; 36 | 37 | //! check processed output matches the output file 38 | extern bool gopt_check_output; 39 | 40 | //! global command line parameter: named RANGEs to process 41 | extern std::vector gopt_ranges; 42 | 43 | //! global SQL database connection handle 44 | extern SqlDatabase* g_db; 45 | 46 | //! initialize global SQL database connection 47 | extern bool g_db_connect(const std::string& db_conninfo); 48 | 49 | //! free global SQL database connection 50 | extern void g_db_free(); 51 | 52 | #ifdef OUT 53 | #undef OUT 54 | #endif 55 | 56 | //! conditional debug output 57 | #define OUTC(dbg,X) do { if (dbg) { std::cerr << X; } } while(0) 58 | 59 | //! write output to std::cerr without newline 60 | #define OUTX(X) OUTC(true, X) 61 | 62 | //! write output to std::cerr 63 | #define OUT(X) OUTX(X << std::endl) 64 | 65 | //! debug output to std::cerr 66 | #define DBG(X) OUTC(debug, X << std::endl) 67 | 68 | //! format output and throw std::runtime_error 69 | #define OUT_THROW(X) do { std::ostringstream os; os << X; throw(std::runtime_error(os.str())); } while (0) 70 | 71 | #endif // COMMON_HEADER 72 | -------------------------------------------------------------------------------- /src/fieldset.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/fieldset.cpp 3 | * 4 | * FieldSet class to automatically detect SQL column types from data set. 5 | * 6 | ****************************************************************************** 7 | * Copyright (C) 2013 Timo Bingmann 8 | * 9 | * This program is free software: you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation, either version 3 of the License, or (at your option) 12 | * any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | * more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | *****************************************************************************/ 22 | 23 | #include "fieldset.h" 24 | #include "common.h" 25 | 26 | #include 27 | #include 28 | 29 | //! return the SQL data type name for a field type 30 | const char* FieldSet::sqlname(fieldtype t) 31 | { 32 | switch (t) { 33 | default: 34 | case T_NONE: return "NONE"; 35 | case T_VARCHAR: 36 | { 37 | if (g_db->type() == SqlDatabase::DB_MYSQL) 38 | return "TEXT"; 39 | 40 | return "VARCHAR"; 41 | } 42 | case T_DOUBLE: return "DOUBLE PRECISION"; 43 | case T_INTEGER: return "BIGINT"; 44 | } 45 | } 46 | 47 | //! detect the field type of a string 48 | FieldSet::fieldtype FieldSet::detect(const std::string& str) 49 | { 50 | std::string::const_iterator it = str.begin(); 51 | 52 | // skip sign 53 | if (it != str.end() && (*it == '+' || *it == '-')) ++it; 54 | 55 | // iterate over digits 56 | while (it != str.end() && isdigit(*it)) ++it; 57 | 58 | if (it == str.end() && it != str.begin()) { 59 | return T_INTEGER; 60 | } 61 | 62 | // skip decimal and iterate over digits 63 | if (it != str.end() && *it == '.') { 64 | ++it; 65 | 66 | // iterate over digits 67 | while (it != str.end() && isdigit(*it)) ++it; 68 | 69 | if (it == str.end() && it != str.begin()) { 70 | return T_DOUBLE; 71 | } 72 | 73 | // check double exponent 74 | if (it != str.end() && (*it == 'e' || *it == 'E')) { 75 | ++it; 76 | 77 | // skip sign 78 | if (it != str.end() && (*it == '+' || *it == '-')) ++it; 79 | 80 | // iterate over digits 81 | while (it != str.end() && isdigit(*it)) ++it; 82 | 83 | if (it == str.end() && it != str.begin()) { 84 | return T_DOUBLE; 85 | } 86 | } 87 | } 88 | // skip double exponent and iterate over digits 89 | else if (it != str.end() && (*it == 'e' || *it == 'E')) 90 | { 91 | ++it; 92 | 93 | // skip sign 94 | if (it != str.end() && (*it == '+' || *it == '-')) ++it; 95 | 96 | // iterate over digits 97 | while (it != str.end() && isdigit(*it)) ++it; 98 | 99 | if (it == str.end() && it != str.begin()) { 100 | return T_DOUBLE; 101 | } 102 | 103 | } 104 | 105 | return T_VARCHAR; 106 | } 107 | 108 | //! self-verify field type detection 109 | void FieldSet::check_detect() 110 | { 111 | assert( detect("1234") == T_INTEGER ); 112 | assert( detect("1234.3") == T_DOUBLE ); 113 | assert( detect(".3e-3") == T_DOUBLE ); 114 | assert( detect("5e-05") == T_DOUBLE ); 115 | assert( detect("1234,3") == T_VARCHAR ); 116 | assert( detect("sdfdf") == T_VARCHAR ); 117 | } 118 | 119 | //! add new field (key,value), detect the value type and augment found type 120 | void FieldSet::add_field(const std::string& key, const std::string& value) 121 | { 122 | fieldtype t = detect(value); 123 | 124 | for (fieldset_type::iterator fi = m_fieldset.begin(); 125 | fi != m_fieldset.end(); ++fi) 126 | { 127 | if (fi->first == key) // found matching entry 128 | { 129 | if (fi->second > t) { 130 | fi->second = t; 131 | } 132 | return; 133 | } 134 | } 135 | 136 | // add new entry 137 | m_fieldset.push_back( sfpair_type(key,t) ); 138 | } 139 | 140 | //! return CREATE TABLE for the given fieldset 141 | std::string FieldSet::make_create_table(const std::string& tablename, bool temporary) const 142 | { 143 | std::ostringstream os; 144 | os << "CREATE " 145 | << (temporary ? "TEMPORARY " : "") 146 | << "TABLE " << g_db->quote_field(tablename) << " ("; 147 | 148 | for (fieldset_type::const_iterator fi = m_fieldset.begin(); 149 | fi != m_fieldset.end(); ++fi) 150 | { 151 | if (fi != m_fieldset.begin()) os << ", "; 152 | os << g_db->quote_field(fi->first) << ' ' << sqlname(fi->second); 153 | } 154 | 155 | os << ")"; 156 | return os.str(); 157 | } 158 | -------------------------------------------------------------------------------- /src/fieldset.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/fieldset.h 3 | * 4 | * FieldSet class to automatically detect SQL column types from data set. 5 | * 6 | ****************************************************************************** 7 | * Copyright (C) 2013 Timo Bingmann 8 | * 9 | * This program is free software: you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation, either version 3 of the License, or (at your option) 12 | * any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | * more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | *****************************************************************************/ 22 | 23 | #ifndef FIELDSET_HEADER 24 | #define FIELDSET_HEADER 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | //! List of field specifications to automatically detect SQL columns types 31 | class FieldSet 32 | { 33 | public: 34 | //! automatically detected SQL column data types: larger ones are more 35 | //! specific, lower ones are more generic. 36 | enum fieldtype { T_NONE, T_VARCHAR, T_DOUBLE, T_INTEGER }; 37 | 38 | //! return the SQL data type name for a field type 39 | static const char* sqlname(fieldtype t); 40 | 41 | //! detect the field type of a string 42 | static fieldtype detect(const std::string& str); 43 | 44 | //! self-verify field type detection 45 | static void check_detect(); 46 | 47 | protected: 48 | //! pair (key,field-type) field specifications 49 | typedef std::pair sfpair_type; 50 | 51 | //! list of field specifications 52 | typedef std::vector fieldset_type; 53 | 54 | //! list of field specifications 55 | fieldset_type m_fieldset; 56 | 57 | public: 58 | //! number of fields is set 59 | inline size_t count() const 60 | { 61 | return m_fieldset.size(); 62 | } 63 | 64 | //! add new field (key,value), detect the value type and augment found type 65 | void add_field(const std::string& key, const std::string& value); 66 | 67 | //! return CREATE TABLE for the given fieldset 68 | std::string make_create_table(const std::string& tablename, bool temporary) const; 69 | }; 70 | 71 | #endif // FIELDSET_HEADER 72 | -------------------------------------------------------------------------------- /src/importdata.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/importdata.h 3 | * 4 | * Import RESULT files into the local PostgreSQL database for further 5 | * processing. Automatically detects the SQL column types. 6 | * 7 | ****************************************************************************** 8 | * Copyright (C) 2013 Timo Bingmann 9 | * 10 | * This program is free software: you can redistribute it and/or modify it 11 | * under the terms of the GNU General Public License as published by the Free 12 | * Software Foundation, either version 3 of the License, or (at your option) 13 | * any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, but WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | * more details. 19 | * 20 | * You should have received a copy of the GNU General Public License along with 21 | * this program. If not, see . 22 | *****************************************************************************/ 23 | 24 | #ifndef IMPORTDATA_HEADER 25 | #define IMPORTDATA_HEADER 26 | 27 | #include "fieldset.h" 28 | 29 | #include 30 | 31 | //! Encapsules one sp-importdata processes, which can also be run from other 32 | //! sqlplot processors. 33 | class ImportData 34 | { 35 | protected: 36 | 37 | //! verbosity 38 | int mopt_verbose; 39 | 40 | //! take field types from first line and process stream 41 | bool mopt_firstline; 42 | 43 | //! parse all lines as key=value lines, ignoring RESULT flags 44 | bool mopt_all_lines; 45 | 46 | //! skip duplicate RESULT lines 47 | bool mopt_noduplicates; 48 | 49 | //! add numeric column numbers for key-less values (without equal sign) 50 | bool mopt_colnums; 51 | 52 | //! use TEMPORARY in CREATE TABLE for transient data 53 | bool mopt_temporary_table; 54 | 55 | //! allow empty tables and globs matching no files 56 | bool mopt_empty_okay; 57 | 58 | //! append rows to table instead of clearing all data 59 | bool mopt_append_data; 60 | 61 | //! table imported 62 | std::string m_tablename; 63 | 64 | //! field set of all imported data 65 | FieldSet m_fieldset; 66 | 67 | //! type of array of all data key=value lines 68 | typedef std::vector slist_type; 69 | 70 | //! array of all data key=value lines 71 | slist_type m_linedata; 72 | 73 | //! sorted set of all data lines, for mopt_noduplicates 74 | std::set m_lineset; 75 | 76 | //! number of RESULT lines counted in current file 77 | size_t m_count; 78 | 79 | //! number of RESULT lines counted over all files 80 | size_t m_total_count; 81 | 82 | public: 83 | //! initializing constructor 84 | ImportData(bool temporary_table = false); 85 | 86 | //! returns true if the give table exists. 87 | static bool exist_table(const std::string& table); 88 | 89 | //! CREATE TABLE for the accumulated data set 90 | bool create_table() const; 91 | 92 | //! insert a line into the database table 93 | bool insert_line(const std::string& line); 94 | 95 | //! process a line: cache lines or insert directly. 96 | bool process_line(const std::string& line); 97 | 98 | //! process an input stream and split into lines 99 | void process_stream(FILE* in, const char* fname); 100 | void process_stream(std::istream& in, const char* fname); 101 | 102 | //! process a line: cache lines or insert directly. 103 | void process_file(const std::string& fname); 104 | 105 | //! process cached data lines 106 | void process_linedata(); 107 | 108 | //! print command line usage 109 | int print_usage(const std::string& progname); 110 | 111 | //! process command line arguments and data 112 | int main(int argc, char* argv[]); 113 | }; 114 | 115 | #endif // IMPORTDATA_HEADER 116 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/sqlplot-tools.cpp 3 | * 4 | * Process embedded SQL plot instructions in LaTeX or Gnuplot files. 5 | * 6 | ****************************************************************************** 7 | * Copyright (C) 2013-2016 Timo Bingmann 8 | * 9 | * This program is free software: you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation, either version 3 of the License, or (at your option) 12 | * any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | * more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | *****************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include "simpleopt.h" 31 | #include "common.h" 32 | #include "strtools.h" 33 | #include "pgsql.h" 34 | #include "textlines.h" 35 | #include "importdata.h" 36 | 37 | //! file type from command line 38 | static std::string sopt_filetype; 39 | 40 | //! external prototype for latex.cpp 41 | extern void sp_latex(const std::string& filename, TextLines& lines); 42 | 43 | //! external prototype for gnuplot.cpp 44 | extern void sp_gnuplot(const std::string& filename, TextLines& lines); 45 | 46 | //! process a stream 47 | static inline TextLines 48 | sp_process_stream(const std::string& filename, std::istream& is) 49 | { 50 | TextLines lines; 51 | 52 | // read complete file line-wise 53 | lines.read_stream(is); 54 | 55 | // automatically detect file type 56 | std::string filetype; 57 | 58 | if (sopt_filetype.size()) 59 | { 60 | filetype = sopt_filetype; 61 | } 62 | else if (is_suffix(filename, ".tex") || 63 | is_suffix(filename, ".latex") || 64 | is_suffix(filename, ".ltx")) 65 | { 66 | filetype = "latex"; 67 | } 68 | else if (is_suffix(filename, ".gp") || 69 | is_suffix(filename, ".gpi") || 70 | is_suffix(filename, ".gnu") || 71 | is_suffix(filename, ".plt") || 72 | is_suffix(filename, ".plot") || 73 | is_suffix(filename, ".gnuplot")) 74 | { 75 | filetype = "gnuplot"; 76 | } 77 | 78 | // process lines in place 79 | if (filetype == "latex") 80 | sp_latex(filename, lines); 81 | else if (filetype == "gnuplot") 82 | sp_gnuplot(filename, lines); 83 | else 84 | OUT_THROW("--- Error processing " << filename << " : unknown file type, use -f !"); 85 | 86 | OUT("--- Finished processing " << filename << " successfully."); 87 | 88 | return lines; 89 | } 90 | 91 | //! define identifiers for command line arguments 92 | enum { OPT_HELP, OPT_VERBOSE, OPT_FILETYPE, 93 | OPT_OUTPUT, OPT_CHECK_OUTPUT, OPT_DATABASE, OPT_RANGE, 94 | OPT_WORK_DIR }; 95 | 96 | //! define command line arguments 97 | static CSimpleOpt::SOption sopt_list[] = { 98 | { OPT_HELP, "-?", SO_NONE }, 99 | { OPT_HELP, "-h", SO_NONE }, 100 | { OPT_VERBOSE, "-v", SO_NONE }, 101 | { OPT_FILETYPE, "-f", SO_REQ_SEP }, 102 | { OPT_OUTPUT, "-o", SO_REQ_SEP }, 103 | { OPT_CHECK_OUTPUT, "-C", SO_NONE }, 104 | { OPT_DATABASE, "-D", SO_REQ_SEP }, 105 | { OPT_RANGE, "-R", SO_REQ_SEP }, 106 | { OPT_WORK_DIR, "-W", SO_REQ_SEP }, 107 | SO_END_OF_OPTIONS 108 | }; 109 | 110 | //! print command line usage 111 | static inline int 112 | sp_process_usage(const std::string& progname) 113 | { 114 | OUT("Usage: " << progname << " [options] [files...]" << std::endl << 115 | std::endl << 116 | "Options: " << std::endl << 117 | " import Call IMPORT-DATA subprogram to load SQL tables." << std::endl << 118 | " -v Increase verbosity." << std::endl << 119 | " -f Force input file type = latex or gnuplot." << std::endl << 120 | " -o Output all processed files to this stream." << std::endl << 121 | " -C Verify that -o output file matches processed data (for tests)." << std::endl << 122 | " -D Select SQL database type and file or database." << std::endl << 123 | " -R Process only named RANGE in files." << std::endl << 124 | " -W Change working directory at start-up." << std::endl); 125 | 126 | return EXIT_FAILURE; 127 | } 128 | 129 | //! process LaTeX or Gnuplot, main function 130 | static inline int 131 | sp_process(int argc, char* argv[]) 132 | { 133 | // output file name 134 | std::string opt_outputfile; 135 | 136 | // database connection to establish 137 | std::string opt_db_conninfo; 138 | 139 | // working directory 140 | std::string opt_work_dir; 141 | 142 | //! parse command line parameters using SimpleOpt 143 | CSimpleOpt args(argc, argv, sopt_list); 144 | 145 | while (args.Next()) 146 | { 147 | if (args.LastError() != SO_SUCCESS) { 148 | OUT(argv[0] << ": invalid command line argument '" << args.OptionText() << "'"); 149 | return EXIT_FAILURE; 150 | } 151 | 152 | switch (args.OptionId()) 153 | { 154 | case OPT_HELP: default: 155 | return sp_process_usage(argv[0]); 156 | 157 | case OPT_VERBOSE: 158 | gopt_verbose++; 159 | break; 160 | 161 | case OPT_FILETYPE: 162 | sopt_filetype = args.OptionArg(); 163 | break; 164 | 165 | case OPT_OUTPUT: 166 | opt_outputfile = args.OptionArg(); 167 | break; 168 | 169 | case OPT_CHECK_OUTPUT: 170 | gopt_check_output = true; 171 | break; 172 | 173 | case OPT_DATABASE: 174 | opt_db_conninfo = args.OptionArg(); 175 | break; 176 | 177 | case OPT_RANGE: 178 | gopt_ranges.push_back(args.OptionArg()); 179 | break; 180 | 181 | case OPT_WORK_DIR: 182 | opt_work_dir = args.OptionArg(); 183 | break; 184 | } 185 | } 186 | 187 | if (!opt_work_dir.empty()) { 188 | if (chdir(opt_work_dir.c_str()) != 0) 189 | OUT_THROW("Error chdir() to work directory: " << strerror(errno)); 190 | } 191 | 192 | // make connection to the database 193 | if (!g_db_connect(opt_db_conninfo)) 194 | OUT_THROW("Fatal: could not connect to a SQL database"); 195 | 196 | // open output file or string stream 197 | std::ostream* output = NULL; 198 | if (gopt_check_output) 199 | { 200 | if (!opt_outputfile.size()) 201 | OUT_THROW("Fatal: checking output requires an output filename."); 202 | 203 | output = new std::ostringstream; 204 | } 205 | else if (opt_outputfile == "-") 206 | { 207 | output = &std::cout; 208 | } 209 | else if (opt_outputfile.size()) 210 | { 211 | output = new std::ofstream(opt_outputfile.c_str()); 212 | 213 | if (!output->good()) 214 | OUT_THROW("Error opening output stream: " << strerror(errno)); 215 | } 216 | 217 | // process file commandline arguments 218 | if (args.FileCount()) 219 | { 220 | for (int fi = 0; fi < args.FileCount(); ++fi) 221 | { 222 | const char* filename = args.File(fi); 223 | 224 | std::ifstream in(filename); 225 | if (!in.good()) { 226 | OUT_THROW("Error reading " << filename << ": " << strerror(errno)); 227 | } 228 | else { 229 | TextLines out = sp_process_stream(filename, in); 230 | 231 | if (output) { 232 | // write to common output 233 | out.write_stream(*output); 234 | } 235 | else { 236 | // overwrite input file 237 | in.close(); 238 | std::ofstream outfile(filename); 239 | if (!outfile.good()) 240 | OUT_THROW("Error writing " << filename << ": " << strerror(errno)); 241 | 242 | out.write_stream(outfile); 243 | if (!outfile.good()) 244 | OUT_THROW("Error writing " << filename << ": " << strerror(errno)); 245 | } 246 | } 247 | } 248 | } 249 | else // no file arguments -> process stdin 250 | { 251 | OUT("Reading text from stdin ..."); 252 | TextLines out = sp_process_stream("stdin", std::cin); 253 | 254 | if (output) { 255 | // write to common output 256 | out.write_stream(*output); 257 | } 258 | else { 259 | // write to stdout 260 | out.write_stream(std::cout); 261 | } 262 | } 263 | 264 | // verify processed output against file 265 | if (gopt_check_output) 266 | { 267 | std::ifstream in(opt_outputfile.c_str()); 268 | if (!in.good()) { 269 | OUT("Error reading " << opt_outputfile << ": " << strerror(errno)); 270 | return EXIT_FAILURE; 271 | } 272 | std::string checkdata = read_stream(in); 273 | 274 | assert(output); 275 | std::ostringstream* oss = (std::ostringstream*)output; 276 | 277 | if (checkdata != oss->str()) 278 | { 279 | OUT("Mismatch to expected output file:"); 280 | simple_diff(oss->str(), checkdata); 281 | OUT_THROW("Mismatch to expected output file " << opt_outputfile); 282 | } 283 | } 284 | 285 | if (output) { 286 | if (output != &std::cout) 287 | delete output; 288 | } 289 | 290 | g_db_free(); 291 | 292 | return EXIT_SUCCESS; 293 | } 294 | 295 | //! main(), yay. 296 | int main(int argc, char* argv[]) 297 | { 298 | try { 299 | if (argc >= 2 && 300 | (strcmp(argv[1], "import") == 0 || 301 | strcmp(argv[1], "import-data") == 0)) 302 | { 303 | return ImportData().main(argc-1, argv+1); 304 | } 305 | else 306 | { 307 | return sp_process(argc, argv); 308 | } 309 | } 310 | catch (std::runtime_error& e) 311 | { 312 | OUT(e.what()); 313 | return EXIT_FAILURE; 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /src/mysql.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/mysql.cpp 3 | * 4 | * Encapsulate MySQL queries into a C++ class, which is a specialization of the 5 | * generic SQL database interface. 6 | * 7 | ****************************************************************************** 8 | * Copyright (C) 2014 Timo Bingmann 9 | * 10 | * This program is free software: you can redistribute it and/or modify it 11 | * under the terms of the GNU General Public License as published by the Free 12 | * Software Foundation, either version 3 of the License, or (at your option) 13 | * any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, but WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | * more details. 19 | * 20 | * You should have received a copy of the GNU General Public License along with 21 | * this program. If not, see . 22 | *****************************************************************************/ 23 | 24 | #include "mysql.h" 25 | #include "common.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | //! Opaque structure for MYSQL_BIND 33 | struct MySqlBind : public MYSQL_BIND 34 | { 35 | }; 36 | 37 | //! Class for result value 38 | struct MySqlColumn 39 | { 40 | //! NULL value indicator 41 | #if defined(LIBMYSQL_VERSION_ID) && LIBMYSQL_VERSION_ID >= 80000 42 | bool is_null; 43 | #else 44 | my_bool is_null; 45 | #endif 46 | 47 | //! output string data, currently extra long strings are truncated. 48 | char strdata[128]; 49 | 50 | //! output string data length 51 | unsigned long length; 52 | 53 | //! initialize internal bind pointers 54 | void initialize_as_string(MySqlBind& bind) 55 | { 56 | // buffer 57 | bind.buffer_type = MYSQL_TYPE_STRING; 58 | bind.buffer = strdata; 59 | bind.buffer_length = sizeof(strdata); 60 | // null 61 | bind.is_null = &is_null; 62 | // length 63 | bind.length = &length; 64 | } 65 | }; 66 | 67 | //! Execute a SQL query without parameters, throws on errors. 68 | MySqlQuery::MySqlQuery(class MySqlDatabase& db, const std::string& query) 69 | : SqlQueryImpl(query), 70 | m_db(db), m_bind(NULL), m_result(NULL) 71 | { 72 | // allocate prepared statement object 73 | m_stmt = mysql_stmt_init(m_db.m_db); 74 | 75 | // prepare statement 76 | int rc = mysql_stmt_prepare(m_stmt, query.data(), query.size()); 77 | 78 | if (rc != 0) 79 | { 80 | OUT_THROW("SQL query \"" << query << "\"\n" << 81 | "Failed : " << mysql_stmt_error(m_stmt)); 82 | } 83 | 84 | execute(); 85 | } 86 | 87 | //! Execute a SQL query with parameters, throws on errors. 88 | MySqlQuery::MySqlQuery(class MySqlDatabase& db, const std::string& query, 89 | const std::vector& params) 90 | : SqlQueryImpl(query), 91 | m_db(db), m_bind(NULL), m_result(NULL) 92 | { 93 | // allocate prepared statement object 94 | m_stmt = mysql_stmt_init(m_db.m_db); 95 | 96 | // prepare statement 97 | int rc = mysql_stmt_prepare(m_stmt, query.data(), query.size()); 98 | 99 | if (rc != 0) 100 | { 101 | OUT_THROW("SQL query \"" << query << "\"\n" << 102 | "Failed : " << mysql_stmt_error(m_stmt)); 103 | } 104 | 105 | // bind parameters 106 | MYSQL_BIND bind[params.size()]; 107 | memset(bind, 0, sizeof(bind)); 108 | 109 | for (size_t i = 0; i < params.size(); ++i) 110 | { 111 | bind[i].buffer_type = MYSQL_TYPE_STRING; 112 | bind[i].buffer = (char*)params[i].data(); 113 | bind[i].buffer_length = params[i].size(); 114 | bind[i].is_null = 0; 115 | bind[i].length = &bind[i].buffer_length; 116 | } 117 | 118 | rc = mysql_stmt_bind_param(m_stmt, bind); 119 | 120 | if (rc != 0) 121 | { 122 | OUT_THROW("SQL bind parameters " << query << "\n" << 123 | "Failed with " << mysql_stmt_error(m_stmt)); 124 | } 125 | 126 | execute(); 127 | } 128 | 129 | //! Free result 130 | MySqlQuery::~MySqlQuery() 131 | { 132 | mysql_stmt_close(m_stmt); 133 | 134 | if (m_bind) 135 | delete [] m_bind; 136 | 137 | if (m_result) 138 | delete [] m_result; 139 | } 140 | 141 | //! Bind output results and execute query 142 | void MySqlQuery::execute() 143 | { 144 | int rc = mysql_stmt_execute(m_stmt); 145 | 146 | if (rc != 0) 147 | { 148 | OUT_THROW("SQL execute \"" << query() << "\"\n" << 149 | "Failed : " << mysql_stmt_error(m_stmt)); 150 | } 151 | 152 | //! Bind all result columns, mysql apparently cannot fetch single column 153 | //! data, mysql_stmt_fetch_column() segfaults without 154 | //! mysql_stmt_bind_result(). 155 | int cols = num_cols(); 156 | 157 | m_bind = new MySqlBind[cols]; 158 | memset(m_bind, 0, cols * sizeof(MySqlBind)); 159 | 160 | m_result = new MySqlColumn[cols]; 161 | memset(m_result, 0, cols * sizeof(MySqlColumn)); 162 | 163 | for (int c = 0; c < cols; ++c) 164 | { 165 | m_result[c].initialize_as_string(m_bind[c]); 166 | } 167 | 168 | mysql_stmt_bind_result(m_stmt, m_bind); 169 | 170 | m_row = -1; 171 | } 172 | 173 | //! Return number of rows in result, throws if no tuples. 174 | unsigned int MySqlQuery::num_rows() const 175 | { 176 | if (SqlDataCache::is_complete()) 177 | return SqlDataCache::num_rows(); 178 | 179 | assert(!"Row number not available without cached result."); 180 | return -1; 181 | } 182 | 183 | //! Return column name of col 184 | std::string MySqlQuery::col_name(unsigned int col) const 185 | { 186 | MYSQL_RES* res = mysql_stmt_result_metadata(m_stmt); 187 | 188 | MYSQL_FIELD *field = mysql_fetch_field_direct(res, col); 189 | 190 | std::string name = field->name; 191 | 192 | mysql_free_result(res); 193 | 194 | return name; 195 | } 196 | 197 | //! Return number of columns in result, throws if no tuples. 198 | unsigned int MySqlQuery::num_cols() const 199 | { 200 | return mysql_stmt_field_count(m_stmt); 201 | } 202 | 203 | //! Read column name map for the following col -> num mappings. 204 | void MySqlQuery::read_colmap() 205 | { 206 | m_colmap.clear(); 207 | 208 | MYSQL_RES* res = mysql_stmt_result_metadata(m_stmt); 209 | 210 | MYSQL_FIELD *field; 211 | unsigned int col = 0; 212 | 213 | while ( (field = mysql_fetch_field(res)) ) 214 | { 215 | m_colmap[ field->name ] = col++; 216 | } 217 | 218 | mysql_free_result(res); 219 | } 220 | 221 | //! Return the current row number 222 | unsigned int MySqlQuery::current_row() const 223 | { 224 | return m_row; 225 | } 226 | 227 | //! Advance current result row to next (or first if uninitialized) 228 | bool MySqlQuery::step() 229 | { 230 | ++m_row; 231 | return (mysql_stmt_fetch(m_stmt) == 0); 232 | } 233 | 234 | //! Returns true if cell (row,col) is NULL. 235 | bool MySqlQuery::isNULL(unsigned int col) const 236 | { 237 | assert(col < num_cols()); 238 | return m_result[col].is_null; 239 | } 240 | 241 | //! Return text representation of column col of current row. 242 | std::string MySqlQuery::text(unsigned int col) const 243 | { 244 | assert(col < num_cols()); 245 | return std::string(m_result[col].strdata, m_result[col].length); 246 | } 247 | 248 | //! read complete result into memory 249 | void MySqlQuery::read_complete() 250 | { 251 | return SqlDataCache::read_complete(*this); 252 | } 253 | 254 | //! Returns true if cell (row,col) is NULL. 255 | bool MySqlQuery::isNULL(unsigned int row, unsigned int col) const 256 | { 257 | return SqlDataCache::isNULL(row, col); 258 | } 259 | 260 | //! Return text representation of cell (row,col). 261 | std::string MySqlQuery::text(unsigned int row, unsigned int col) const 262 | { 263 | return SqlDataCache::text(row, col); 264 | } 265 | 266 | //////////////////////////////////////////////////////////////////////////////// 267 | 268 | //! try to connect to the database with default parameters 269 | bool MySqlDatabase::initialize(const std::string& params) 270 | { 271 | OUT("Connecting to MySQL database \"" << params << "\"."); 272 | 273 | // create mysql connection object 274 | m_db = mysql_init(NULL); 275 | 276 | if (!m_db) OUT_THROW("Could not create MySQL object."); 277 | 278 | // open connection to the database 279 | if (mysql_real_connect(m_db, NULL, NULL, NULL, NULL, 0, NULL, 0) == NULL) 280 | { 281 | OUT("Connection to MySQL failed: " << errmsg()); 282 | return false; 283 | } 284 | 285 | // have to select a database 286 | execute("USE " + quote_field(params)); 287 | 288 | return true; 289 | } 290 | 291 | //! destructor to free connection 292 | MySqlDatabase::~MySqlDatabase() 293 | { 294 | mysql_close(m_db); 295 | } 296 | 297 | //! return type of SQL database 298 | MySqlDatabase::db_type MySqlDatabase::type() const 299 | { 300 | return DB_MYSQL; 301 | } 302 | 303 | //! return string for the i-th placeholder, where i starts at 0. 304 | std::string MySqlDatabase::placeholder(unsigned int) const 305 | { 306 | return "?"; 307 | } 308 | 309 | //! return quoted table or field identifier 310 | std::string MySqlDatabase::quote_field(const std::string& field) const 311 | { 312 | return '`' + field + '`'; 313 | } 314 | 315 | //! execute SQL query without result 316 | bool MySqlDatabase::execute(const std::string& query) 317 | { 318 | // prepare statement 319 | int rc = mysql_real_query(m_db, query.data(), query.size()); 320 | 321 | if (rc != 0) 322 | { 323 | OUT_THROW("SQL query \"" << query << "\"\n" << 324 | "Failed : " << errmsg()); 325 | } 326 | 327 | return true; 328 | } 329 | 330 | //! construct query object for given string 331 | SqlQuery MySqlDatabase::query(const std::string& query) 332 | { 333 | return SqlQuery( new MySqlQuery(*this, query) ); 334 | } 335 | 336 | //! construct query object for given string with placeholder parameters 337 | SqlQuery MySqlDatabase::query(const std::string& query, 338 | const std::vector& params) 339 | { 340 | return SqlQuery( new MySqlQuery(*this, query, params) ); 341 | } 342 | 343 | //! test if a table exists in the database 344 | bool MySqlDatabase::exist_table(const std::string&) 345 | { 346 | // in MySQL there is no way to check for existing TEMPORARY TABLES, so we 347 | // just DROP TABLE and retry CREATE TABLE if it fails onces. 348 | 349 | return false; 350 | } 351 | 352 | //! return last error message string 353 | const char* MySqlDatabase::errmsg() const 354 | { 355 | return mysql_error(m_db); 356 | } 357 | 358 | //////////////////////////////////////////////////////////////////////////////// 359 | -------------------------------------------------------------------------------- /src/mysql.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/mysql.h 3 | * 4 | * Encapsulate MySQL queries into a C++ class, which is a specialization 5 | * of the generic SQL database interface. 6 | * 7 | ****************************************************************************** 8 | * Copyright (C) 2013-2014 Timo Bingmann 9 | * 10 | * This program is free software: you can redistribute it and/or modify it 11 | * under the terms of the GNU General Public License as published by the Free 12 | * Software Foundation, either version 3 of the License, or (at your option) 13 | * any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, but WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | * more details. 19 | * 20 | * You should have received a copy of the GNU General Public License along with 21 | * this program. If not, see . 22 | *****************************************************************************/ 23 | 24 | #ifndef MYSQL_HEADER 25 | #define MYSQL_HEADER 26 | 27 | #if HAVE_MYSQL 28 | 29 | #include 30 | 31 | #include "sql.h" 32 | 33 | class MySqlQuery : public SqlQueryImpl, protected SqlDataCache 34 | { 35 | protected: 36 | //! MySQL database connection 37 | class MySqlDatabase& m_db; 38 | 39 | //! MySQL prepared statement object 40 | MYSQL_STMT* m_stmt; 41 | 42 | //! Current result row 43 | unsigned int m_row; 44 | 45 | //! Opaque structure for MYSQL_BIND 46 | struct MySqlBind* m_bind; 47 | 48 | //! Opaque structure used to retrieve results 49 | struct MySqlColumn* m_result; 50 | 51 | //! Bind output results and execute query 52 | void execute(); 53 | 54 | public: 55 | 56 | //! Execute a SQL query without placeholders, throws on errors. 57 | MySqlQuery(class MySqlDatabase& db, const std::string& query); 58 | 59 | //! Execute a SQL query with placeholders, throws on errors. 60 | MySqlQuery(class MySqlDatabase& db, const std::string& query, 61 | const std::vector& params); 62 | 63 | //! Free result 64 | ~MySqlQuery(); 65 | 66 | //! Return number of rows in result, throws if no tuples. 67 | unsigned int num_rows() const; 68 | 69 | //! Return number of columns in result, throws if no tuples. 70 | unsigned int num_cols() const; 71 | 72 | // *** Column Name Mapping *** 73 | 74 | //! Return column name of col 75 | std::string col_name(unsigned int col) const; 76 | 77 | //! Read column name map for the following col -> num mappings. 78 | void read_colmap(); 79 | 80 | // *** Result Iteration *** 81 | 82 | //! Return the current row number. 83 | unsigned int current_row() const; 84 | 85 | //! Advance current result row to next (or first if uninitialized) 86 | bool step(); 87 | 88 | //! Returns true if cell (current_row,col) is NULL. 89 | bool isNULL(unsigned int col) const; 90 | 91 | //! Return text representation of column col of current row. 92 | std::string text(unsigned int col) const; 93 | 94 | // *** Complete Result Caching *** 95 | 96 | //! read complete result into memory 97 | void read_complete(); 98 | 99 | //! Returns true if cell (row,col) is NULL. 100 | bool isNULL(unsigned int row, unsigned int col) const; 101 | 102 | //! Return text representation of cell (row,col). 103 | std::string text(unsigned int row, unsigned int col) const; 104 | }; 105 | 106 | //! MySQL database connection 107 | class MySqlDatabase : public SqlDatabase 108 | { 109 | protected: 110 | //! database connection 111 | MYSQL* m_db; 112 | 113 | //! for access to database connection 114 | friend class MySqlQuery; 115 | 116 | public: 117 | //! virtual destructor to free connection 118 | virtual ~MySqlDatabase(); 119 | 120 | //! return type of SQL database 121 | virtual db_type type() const; 122 | 123 | //! try to connect to the database with given parameters 124 | virtual bool initialize(const std::string& params); 125 | 126 | //! return string for the i-th placeholder, where i starts at 0. 127 | virtual std::string placeholder(unsigned int i) const; 128 | 129 | //! return quoted table or field identifier 130 | virtual std::string quote_field(const std::string& field) const; 131 | 132 | //! execute SQL query without result 133 | virtual bool execute(const std::string& query); 134 | 135 | //! construct query object for given string 136 | virtual SqlQuery query(const std::string& query); 137 | 138 | //! construct query object for given string with placeholder parameters 139 | virtual SqlQuery query(const std::string& query, 140 | const std::vector& params); 141 | 142 | //! test if a table exists in the database 143 | virtual bool exist_table(const std::string& table); 144 | 145 | //! return last error message string 146 | const char* errmsg() const; 147 | }; 148 | 149 | #endif // HAVE_MYSQL 150 | 151 | #endif // MYSQL_HEADER 152 | -------------------------------------------------------------------------------- /src/pgsql.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/pgsql.cpp 3 | * 4 | * Encapsulate PostgreSQL queries into a C++ class, which is a specialization 5 | * of the generic SQL database interface. 6 | * 7 | ****************************************************************************** 8 | * Copyright (C) 2013-2014 Timo Bingmann 9 | * 10 | * This program is free software: you can redistribute it and/or modify it 11 | * under the terms of the GNU General Public License as published by the Free 12 | * Software Foundation, either version 3 of the License, or (at your option) 13 | * any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, but WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | * more details. 19 | * 20 | * You should have received a copy of the GNU General Public License along with 21 | * this program. If not, see . 22 | *****************************************************************************/ 23 | 24 | #include "pgsql.h" 25 | #include "common.h" 26 | #include "strtools.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | //! Execute a SQL query without placeholders, throws on errors. 34 | PgSqlQuery::PgSqlQuery(class PgSqlDatabase& db, const std::string& query) 35 | : SqlQueryImpl(query), 36 | m_db(db) 37 | { 38 | m_res = PQexec(m_db.m_pg, query.c_str()); 39 | 40 | ExecStatusType r = PQresultStatus(m_res); 41 | 42 | if (r == PGRES_BAD_RESPONSE || 43 | r == PGRES_FATAL_ERROR) 44 | { 45 | OUT_THROW("SQL query " << query << "\n" << 46 | "Failed with " << PQresStatus(r) << 47 | " : " << m_db.errmsg()); 48 | } 49 | 50 | m_row = -1; 51 | } 52 | 53 | //! Execute a SQL query with placeholders, throws on errors. 54 | PgSqlQuery::PgSqlQuery(class PgSqlDatabase& db, const std::string& query, 55 | const std::vector& params) 56 | : SqlQueryImpl(query), 57 | m_db(db) 58 | { 59 | // construct vector of const char* for interface 60 | std::vector paramsC(params.size()); 61 | 62 | for (size_t i = 0; i < params.size(); ++i) 63 | paramsC[i] = params[i].c_str(); 64 | 65 | // execute query with string variables 66 | 67 | m_res = PQexecParams(m_db.m_pg, query.c_str(), 68 | params.size(), NULL, paramsC.data(), NULL, NULL, 0); 69 | 70 | ExecStatusType r = PQresultStatus(m_res); 71 | 72 | if (r == PGRES_BAD_RESPONSE || 73 | r == PGRES_FATAL_ERROR) 74 | { 75 | OUT_THROW("SQL query " << query << "\n" << 76 | "Failed with " << PQresStatus(r) << 77 | " : " << m_db.errmsg()); 78 | } 79 | 80 | m_row = -1; 81 | } 82 | 83 | //! Free result 84 | PgSqlQuery::~PgSqlQuery() 85 | { 86 | PQclear(m_res); 87 | } 88 | 89 | //! Return number of rows in result, throws if no tuples. 90 | unsigned int PgSqlQuery::num_rows() const 91 | { 92 | if (PQresultStatus(m_res) != PGRES_TUPLES_OK) 93 | { 94 | OUT_THROW("SQL query " << query() << "\n" << 95 | "Did not return tuples : " << m_db.errmsg()); 96 | } 97 | 98 | int num = PQntuples(m_res); 99 | 100 | if (num < 0) 101 | { 102 | OUT_THROW("SQL query " << query() << "\n" << 103 | "Did not return tuples : " << m_db.errmsg()); 104 | } 105 | 106 | return num; 107 | } 108 | 109 | //! Return column name of col 110 | std::string PgSqlQuery::col_name(unsigned int col) const 111 | { 112 | return PQfname(m_res, col); 113 | } 114 | 115 | //! Return number of columns in result, throws if no tuples. 116 | unsigned int PgSqlQuery::num_cols() const 117 | { 118 | if (PQresultStatus(m_res) != PGRES_TUPLES_OK) 119 | { 120 | OUT_THROW("SQL query " << query() << "\n" << 121 | "Did not return tuples : " << m_db.errmsg()); 122 | } 123 | 124 | int num = PQnfields(m_res); 125 | 126 | if (num < 0) 127 | { 128 | OUT_THROW("SQL query " << query() << "\n" << 129 | "Did not return tuples : " << m_db.errmsg()); 130 | } 131 | 132 | return num; 133 | } 134 | 135 | //! Return the current row number 136 | unsigned int PgSqlQuery::current_row() const 137 | { 138 | return m_row; 139 | } 140 | 141 | //! Advance current result row to next (or first if uninitialized) 142 | bool PgSqlQuery::step() 143 | { 144 | ++m_row; 145 | return (m_row < num_rows()); 146 | } 147 | 148 | //! Returns true if cell (row,col) is NULL. 149 | bool PgSqlQuery::isNULL(unsigned int col) const 150 | { 151 | assert(m_row < num_rows()); 152 | assert(col < num_cols()); 153 | return PQgetisnull(m_res, m_row, col); 154 | } 155 | 156 | //! Return text representation of column col of current row. 157 | std::string PgSqlQuery::text(unsigned int col) const 158 | { 159 | assert(m_row < num_rows()); 160 | assert(col < num_cols()); 161 | size_t length = PQgetlength(m_res, m_row, col); 162 | return std::string(PQgetvalue(m_res, m_row, col), length); 163 | } 164 | 165 | //! read complete result into memory 166 | void PgSqlQuery::read_complete() 167 | { 168 | // noop on PostgreSQL 169 | return; 170 | } 171 | 172 | //! Returns true if cell (row,col) is NULL. 173 | bool PgSqlQuery::isNULL(unsigned int row, unsigned int col) const 174 | { 175 | assert(row < num_rows()); 176 | assert(col < num_cols()); 177 | return PQgetisnull(m_res, row, col); 178 | } 179 | 180 | //! Return text representation of cell (row,col). 181 | std::string PgSqlQuery::text(unsigned int row, unsigned int col) const 182 | { 183 | assert(row < num_rows()); 184 | assert(col < num_cols()); 185 | size_t length = PQgetlength(m_res, row, col); 186 | return std::string(PQgetvalue(m_res, row, col), length); 187 | } 188 | 189 | //////////////////////////////////////////////////////////////////////////////// 190 | 191 | //! try to connect to the database with default parameters 192 | bool PgSqlDatabase::initialize(const std::string& params) 193 | { 194 | OUT("Connecting to PostgreSQL database \"" << params << "\"."); 195 | 196 | // make connection to the database 197 | m_pg = PQconnectdb(params.c_str()); 198 | 199 | // check to see that the backend connection was successfully made 200 | if (PQstatus(m_pg) != CONNECTION_OK) 201 | { 202 | OUT("Connection to PostgreSQL failed: " << errmsg()); 203 | return false; 204 | } 205 | 206 | return true; 207 | } 208 | 209 | //! destructor to free connection 210 | PgSqlDatabase::~PgSqlDatabase() 211 | { 212 | PQfinish(m_pg); 213 | } 214 | 215 | //! return type of SQL database 216 | PgSqlDatabase::db_type PgSqlDatabase::type() const 217 | { 218 | return DB_PGSQL; 219 | } 220 | 221 | //! return string for the i-th placeholder, where i starts at 0. 222 | std::string PgSqlDatabase::placeholder(unsigned int i) const 223 | { 224 | return '$' + to_str(i+1); 225 | } 226 | 227 | //! return quoted table or field identifier 228 | std::string PgSqlDatabase::quote_field(const std::string& field) const 229 | { 230 | return '"' + field + '"'; 231 | } 232 | 233 | //! execute SQL query without result 234 | bool PgSqlDatabase::execute(const std::string& query) 235 | { 236 | PGresult* res = PQexec(m_pg, query.c_str()); 237 | 238 | ExecStatusType r = PQresultStatus(res); 239 | 240 | if (r == PGRES_TUPLES_OK) 241 | { 242 | OUT("SQL query " << query << "\n" << 243 | "Return TUPLES!!!"); 244 | } 245 | else if (r != PGRES_COMMAND_OK) 246 | { 247 | OUT_THROW("SQL query " << query << "\n" << 248 | "Failed with " << PQresStatus(r) << 249 | " : " << errmsg()); 250 | } 251 | 252 | PQclear(res); 253 | 254 | return true; 255 | } 256 | 257 | //! construct query object for given string 258 | SqlQuery PgSqlDatabase::query(const std::string& query) 259 | { 260 | return SqlQuery( new PgSqlQuery(*this, query) ); 261 | } 262 | 263 | //! construct query object for given string with placeholder parameters 264 | SqlQuery PgSqlDatabase::query(const std::string& query, 265 | const std::vector& params) 266 | { 267 | return SqlQuery( new PgSqlQuery(*this, query, params) ); 268 | } 269 | 270 | //! test if a table exists in the database 271 | bool PgSqlDatabase::exist_table(const std::string& table) 272 | { 273 | std::vector params; 274 | params.push_back(table); 275 | 276 | PgSqlQuery sql(*this, 277 | "SELECT COUNT(*) FROM pg_tables WHERE tablename = $1", 278 | params); 279 | 280 | assert(sql.num_rows() == 1 && sql.num_cols() == 1); 281 | sql.step(); 282 | 283 | return (sql.text(0) != "0"); 284 | } 285 | 286 | //! return last error message string 287 | const char* PgSqlDatabase::errmsg() const 288 | { 289 | return PQerrorMessage(m_pg); 290 | } 291 | 292 | //////////////////////////////////////////////////////////////////////////////// 293 | -------------------------------------------------------------------------------- /src/pgsql.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/pgsql.h 3 | * 4 | * Encapsulate PostgreSQL queries into a C++ class, which is a specialization 5 | * of the generic SQL database interface. 6 | * 7 | ****************************************************************************** 8 | * Copyright (C) 2013-2014 Timo Bingmann 9 | * 10 | * This program is free software: you can redistribute it and/or modify it 11 | * under the terms of the GNU General Public License as published by the Free 12 | * Software Foundation, either version 3 of the License, or (at your option) 13 | * any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, but WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 | * more details. 19 | * 20 | * You should have received a copy of the GNU General Public License along with 21 | * this program. If not, see . 22 | *****************************************************************************/ 23 | 24 | #ifndef PGSQL_HEADER 25 | #define PGSQL_HEADER 26 | 27 | #if HAVE_POSTGRESQL 28 | 29 | #include 30 | 31 | #include "sql.h" 32 | 33 | class PgSqlQuery : public SqlQueryImpl 34 | { 35 | protected: 36 | //! PostgreSQL database connection 37 | class PgSqlDatabase& m_db; 38 | 39 | //! PostgreSQL result object 40 | PGresult* m_res; 41 | 42 | //! Current result row 43 | unsigned int m_row; 44 | 45 | public: 46 | 47 | //! Execute a SQL query without placeholders, throws on errors. 48 | PgSqlQuery(class PgSqlDatabase& db, const std::string& query); 49 | 50 | //! Execute a SQL query with placeholders, throws on errors. 51 | PgSqlQuery(class PgSqlDatabase& db, const std::string& query, 52 | const std::vector& params); 53 | 54 | //! Free result 55 | ~PgSqlQuery(); 56 | 57 | //! Return number of rows in result, throws if no tuples. 58 | unsigned int num_rows() const; 59 | 60 | //! Return number of columns in result, throws if no tuples. 61 | unsigned int num_cols() const; 62 | 63 | // *** Column Name Mapping *** 64 | 65 | //! Return column name of col 66 | std::string col_name(unsigned int col) const; 67 | 68 | // *** Result Iteration *** 69 | 70 | //! Return the current row number. 71 | unsigned int current_row() const; 72 | 73 | //! Advance current result row to next (or first if uninitialized) 74 | bool step(); 75 | 76 | //! Returns true if cell (current_row,col) is NULL. 77 | bool isNULL(unsigned int col) const; 78 | 79 | //! Return text representation of column col of current row. 80 | std::string text(unsigned int col) const; 81 | 82 | // *** Complete Result Caching *** 83 | 84 | //! read complete result into memory 85 | void read_complete(); 86 | 87 | //! Returns true if cell (row,col) is NULL. 88 | bool isNULL(unsigned int row, unsigned int col) const; 89 | 90 | //! Return text representation of cell (row,col). 91 | std::string text(unsigned int row, unsigned int col) const; 92 | }; 93 | 94 | //! PostgreSQL database connection 95 | class PgSqlDatabase : public SqlDatabase 96 | { 97 | protected: 98 | //! database connection 99 | PGconn* m_pg; 100 | 101 | //! for access to database connection 102 | friend class PgSqlQuery; 103 | 104 | public: 105 | //! virtual destructor to free connection 106 | virtual ~PgSqlDatabase(); 107 | 108 | //! return type of SQL database 109 | virtual db_type type() const; 110 | 111 | //! try to connect to the database with given parameters 112 | virtual bool initialize(const std::string& params); 113 | 114 | //! return string for the i-th placeholder, where i starts at 0. 115 | virtual std::string placeholder(unsigned int i) const; 116 | 117 | //! return quoted table or field identifier 118 | virtual std::string quote_field(const std::string& field) const; 119 | 120 | //! execute SQL query without result 121 | virtual bool execute(const std::string& query); 122 | 123 | //! construct query object for given string 124 | virtual SqlQuery query(const std::string& query); 125 | 126 | //! construct query object for given string with placeholder parameters 127 | virtual SqlQuery query(const std::string& query, 128 | const std::vector& params); 129 | 130 | //! test if a table exists in the database 131 | virtual bool exist_table(const std::string& table); 132 | 133 | //! return last error message string 134 | virtual const char* errmsg() const; 135 | }; 136 | 137 | #endif // HAVE_POSTGRESQL 138 | 139 | #endif // PGSQL_HEADER 140 | -------------------------------------------------------------------------------- /src/reformat.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * src/reformat.h 3 | * 4 | * Reformatting class for LaTeX output 5 | * 6 | ****************************************************************************** 7 | * Copyright (C) 2014 Timo Bingmann 8 | * 9 | * This program is free software: you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the Free 11 | * Software Foundation, either version 3 of the License, or (at your option) 12 | * any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 | * more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along with 20 | * this program. If not, see . 21 | *****************************************************************************/ 22 | 23 | #ifndef REFORMAT_HEADER 24 | #define REFORMAT_HEADER 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "sql.h" 32 | 33 | /*! 34 | * Reformatting styles for text or double value cells. 35 | * 36 | * cell-level formats: 37 | * - escape (escape special LaTeX characters) 38 | * - round=# (# = 'floor', 'ceil' or number of decimal digits) 39 | * - precision=# (# = show decimal digits) 40 | * - width=# (# = set width of output) 41 | * - digits=# (# = show 3,4,5 decimal digits) 42 | * - group, grouping (show thousand grouping symbol) 43 | * 44 | * column selector: 45 | * - col/cols/column/columns 1-2,3-4,5,6=() 46 | * 47 | * column-level formats: 48 | * - all cell-level formats 49 | * - max/min/maximum/minimum=